// UnusedSalvage returns a web handler function that identifies a user's salvage // drops that are unused by any blueprint they own. func UnusedSalvage(localdb db.LocalDB, sde evego.Database, sess server.Sessionizer) web.HandlerFunc { return func(c web.C, w http.ResponseWriter, r *http.Request) { s := sess.GetSession(&c, w, r) myUserID := s.User charID, err := strconv.Atoi(c.URLParams["charID"]) if err != nil { http.Error(w, `{"status": "Error", "error": "Invalid character ID supplied."}`, http.StatusBadRequest) return } salvage, err := localdb.UnusedSalvage(myUserID, charID) if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to access database."}`, http.StatusInternalServerError) log.Printf("Error accessing database with user %v, character %v: %v", myUserID, charID, err) return } stations := make(map[string]*evego.Station) itemInfo := make(map[string]*evego.Item) for i := range salvage { item := &salvage[i] if _, found := stations[strconv.Itoa(item.StationID)]; !found { stn, err := localdb.StationForID(item.StationID) if err == nil { stations[strconv.Itoa(item.StationID)] = stn } else { // This should really not friggin' happen. http.Error(w, `{"status": "Error", "error": "Unable to look up station/outpost."}`, http.StatusInternalServerError) log.Printf("Unable to look up station/outpost ID %v: %v", item.StationID, err) return } } typeIDStr := strconv.Itoa(item.TypeID) if _, found := itemInfo[typeIDStr]; !found { thisItem, err := sde.ItemForID(item.TypeID) if err == nil { itemInfo[typeIDStr] = thisItem } } } response := struct { Items []evego.InventoryItem `json:"items"` Stations map[string]*evego.Station `json:"stations"` ItemInfo map[string]*evego.Item `json:"itemInfo"` }{salvage, stations, itemInfo} salvageJSON, err := json.Marshal(&response) if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to marshal JSON."}`, http.StatusInternalServerError) log.Printf("Error marshalling JSON unused salvage with user %v, character %v: %v", myUserID, charID, err) return } w.Write(salvageJSON) return } }
// CRESTCallbackListener returns a web handler function that listens for a CREST // SSO callback and accepts the results of authentication. func CRESTCallbackListener(localdb db.LocalDB, auth evesso.Authenticator, sess server.Sessionizer) web.HandlerFunc { return func(c web.C, w http.ResponseWriter, r *http.Request) { // Verify state value. s := sess.GetSession(&c, w, r) passedState := r.FormValue("state") if passedState != s.State { // CSRF attempt or session expired; reject. http.Error(w, "Returned state not valid for this user.", http.StatusBadRequest) log.Printf("Got state %#v, expected state %#v", passedState, s.State) w.Write([]byte(`{"status": "Error"}`)) return } // Extract code from query parameters. code := r.FormValue("code") // Exchange it for a token. tok, err := auth.Exchange(code) if err != nil { http.Error(w, `{"status": "Error"}`, http.StatusInternalServerError) log.Printf("Error exchanging token: %v", err) return } // Get character information. charInfo, err := auth.CharacterInfo(tok) if err != nil { http.Error(w, `{"status": "Error"}`, http.StatusInternalServerError) log.Printf("Error getting character information: %v; token was %+v", err, tok) return } // Update session in database. err = localdb.AuthenticateSession(s.Cookie, tok, charInfo) if err != nil { http.Error(w, `{"status": "Error"}`, http.StatusInternalServerError) log.Printf("Unable to update session post-auth: %v; info was %+v", err, charInfo) return } w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(http.StatusOK) w.Write([]byte(` <html> <head> <title>Authenticated</title> </head> <body> <p>OK.</p> <script type="text/javascript"> window.onload = function() { window.opener.hasAuthenticated(); window.close(); } </script> </body> </html> `)) } }
// updateOutposts grabs the outpost information and inserts it into the // database. func updateOutposts(localdb db.LocalDB) { log.Printf("Starting outposts update") start := time.Now() err := localdb.RepopulateOutposts() if err != nil { log.Printf("Error updating outposts: %v", err) } else { duration := time.Now().Sub(start) log.Printf("Finished outpost update in %.0f ms", duration.Seconds()*1000.0) } }
// LogoutHandler returns a web handler function that deletes the user's // sessions. func LogoutHandler(localdb db.LocalDB, auth evesso.Authenticator, sess server.Sessionizer) web.HandlerFunc { successMsg := []byte("{ \"success\": true }") return func(c web.C, w http.ResponseWriter, r *http.Request) { s := sess.GetSession(&c, w, r) err := localdb.LogoutSession(s.Cookie) if err != nil { http.Error(w, "Unable to find session", http.StatusTeapot) log.Printf("Error logging out: %v", err) return } // Serve some JSON that confirms success. w.Write(successMsg) } }
// SessionInfo returns a web handler function that returns information about the // current session. func SessionInfo(auth evesso.Authenticator, sess server.Sessionizer, localdb db.LocalDB) web.HandlerFunc { return func(c web.C, w http.ResponseWriter, r *http.Request) { curSession := sess.GetSession(&c, w, r) returnInfo := sessionInfo{ Authenticated: curSession.User != 0, OAuthURL: auth.URL(curSession.State), } if curSession.Token != nil { returnInfo.OAuthExpiry = curSession.Token.Expiry } if curSession.User != 0 { // We're authenticated - also pass in the API keys registered to this // user. keys, err := localdb.APIKeys(curSession.User) if err != nil { log.Fatalf("Error - unable to retrieve API keys from database.") } returnInfo.APIKeys = keys } returnJSON, _ := json.Marshal(&returnInfo) w.Write(returnJSON) } }
func autocompleteStations(sde evego.Database, db db.LocalDB, search string) *[]station { // Use make to ensure that we actually have a slice rather than just a nil // pointer. results := make([]station, 0, 5) search = strings.Replace(search, " ", "%", -1) stations, err := db.SearchStations(search) if err != nil && err != sql.ErrNoRows { log.Printf("ERROR: Can't autocomplete stations: %v", err) } for _, s := range stations { isOutpost := false if s.ReprocessingEfficiency == 0.0 { isOutpost = true } stn := stationFromAPI(sde, &s, isOutpost) results = append(results, stn) } return &results }
// StandingsHandler returns a web handler function that provides information on // the user's toons' effective standings. func StandingsHandler(localdb db.LocalDB, sess server.Sessionizer) web.HandlerFunc { standingsFunc := func(c web.C, w http.ResponseWriter, r *http.Request) { s := sess.GetSession(&c, w, r) userID := s.User charID, _ := strconv.Atoi(c.URLParams["charID"]) npcCorpID, _ := strconv.Atoi(c.URLParams["npcCorpID"]) corpStanding, facStanding, err := localdb.CharacterStandings(userID, charID, npcCorpID) if err != nil { errorStr := "Unable to get character standings." if err == sql.ErrNoRows { errorStr = "Invalid corporation ID passed." } http.Error(w, fmt.Sprintf(`{"status": "Error", "error": "%v"}`, errorStr), http.StatusInternalServerError) return } connections, err := localdb.CharacterSkill(userID, charID, connectionsSkillID) if err != nil { http.Error(w, `{"status": "Error", "error": "Ouch"}`, http.StatusInternalServerError) return } diplomacy, err := localdb.CharacterSkill(userID, charID, diplomacySkillID) if err != nil { http.Error(w, `{"status": "Error", "error": "Ouch"}`, http.StatusInternalServerError) return } effectiveStanding := character.EffectiveStanding(corpStanding, facStanding, connections, diplomacy) statusMsg := struct { Status string `json:"status"` EffectiveStanding float64 `json:"standing"` }{ "OK", effectiveStanding, } statusJSON, _ := json.Marshal(&statusMsg) w.Write(statusJSON) return } return standingsFunc }
// BlueprintsHandlers returns web handler functions that provide information on // a toon's bluerpints. func BlueprintsHandlers(localdb db.LocalDB, sde evego.Database, sess server.Sessionizer) (refresh, get web.HandlerFunc) { refresh = func(c web.C, w http.ResponseWriter, r *http.Request) { s := sess.GetSession(&c, w, r) myUserID := s.User charID, _ := strconv.Atoi(c.URLParams["charID"]) apiKeys, err := localdb.APIKeys(myUserID) if err != nil { http.Error(w, `{"status": "Error", "error": "Ouch"}`, http.StatusInternalServerError) return } // Find the key for this character. var myKey *db.XMLAPIKey for _, key := range apiKeys { for _, toon := range key.Characters { if toon.ID == charID { myKey = &key break } } } if myKey == nil { if err != nil { http.Error(w, `{"status": "Error", "error": "Invalid character supplied."}`, http.StatusUnauthorized) return } } err = localdb.GetAssetsBlueprints(*myKey, charID) if err != nil { http.Error(w, `{"status": "Error", "error": "Ouch"}`, http.StatusInternalServerError) return } return } get = func(c web.C, w http.ResponseWriter, r *http.Request) { s := sess.GetSession(&c, w, r) myUserID := s.User charID, err := strconv.Atoi(c.URLParams["charID"]) if err != nil { http.Error(w, `{"status": "Error", "error": "Invalid character ID supplied."}`, http.StatusBadRequest) return } blueprints, err := localdb.CharacterBlueprints(myUserID, charID) if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to access database."}`, http.StatusInternalServerError) log.Printf("Error accessing database with user %v, character %v: %v", myUserID, charID, err) return } stations := make(map[string]*evego.Station) for i := range blueprints { bp := &blueprints[i] if _, found := stations[strconv.Itoa(bp.StationID)]; !found { stn, err := localdb.StationForID(bp.StationID) if err == nil { stations[strconv.Itoa(bp.StationID)] = stn } } } response := struct { Blueprints []evego.BlueprintItem `json:"blueprints"` Stations map[string]*evego.Station `json:"stations"` }{blueprints, stations} blueprintsJSON, err := json.Marshal(&response) if err != nil { http.Error(w, `{"status": "Error", "error": "Unable to marshal JSON."}`, http.StatusInternalServerError) log.Printf("Error marshalling JSON blueprints with user %v, character %v: %v", myUserID, charID, err) return } w.Write(blueprintsJSON) return } return }
// XMLAPIKeysHandlers returns web handler functions that provide information on // the user's API keys that have been registered with this application. func XMLAPIKeysHandlers(localdb db.LocalDB, sess server.Sessionizer) (list, delete, add, refresh web.HandlerFunc) { // charRefresh refreshes the characters associated with an API key and returns // the current list of characters via the passed responseWriter. charRefresh := func(s *db.Session, key *db.XMLAPIKey, w http.ResponseWriter) { toons, err := localdb.GetAPICharacters(s.User, *key) if err != nil { http.Error(w, `{"status": "Error", "error": "Database connection error (add characters)"}`, http.StatusInternalServerError) return } for _, toon := range toons { // Update skills for this character. err = localdb.GetAPISkills(*key, toon.ID) if err != nil { http.Error(w, `{"status": "Error", "error": "Database connection error (add skills)"}`, http.StatusInternalServerError) return } // Update standings. err = localdb.GetAPIStandings(*key, toon.ID) if err != nil { http.Error(w, `{"status": "Error", "error": "Database connection error (add standings)"}`, http.StatusInternalServerError) return } // Update assets and blueprints. err = localdb.GetAssetsBlueprints(*key, toon.ID) if err != nil { http.Error(w, `{"status": "Error", "error": "Database connection error (add assets)"}`, http.StatusInternalServerError) log.Printf("Got error in GetAssets: %v", err) return } } response := struct { Status string `json:"status"` Characters []evego.Character `json:"characters"` }{ Status: "OK", Characters: toons, } responseJSON, err := json.Marshal(response) w.Write(responseJSON) return } list = func(c web.C, w http.ResponseWriter, r *http.Request) { s := sess.GetSession(&c, w, r) userKeys, err := localdb.APIKeys(s.User) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) w.Write([]byte(`{"status": "Error"}`)) return } userKeysJSON, err := json.Marshal(userKeys) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) w.Write([]byte(`{"status": "Error"}`)) return } w.Write(userKeysJSON) } delete = func(c web.C, w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "This function must be called with the POST method", http.StatusMethodNotAllowed) w.Write([]byte(`{"status": "Error"}`)) return } s := sess.GetSession(&c, w, r) keyID, _ := strconv.Atoi(c.URLParams["keyid"]) err := localdb.DeleteAPIKey(s.User, keyID) if err != nil { http.Error(w, "Database connection error", http.StatusInternalServerError) w.Write([]byte(`{"status": "Error"}`)) return } w.Write([]byte(`{"status": "OK"}`)) return } add = func(c web.C, w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "This function must be called with the POST method", http.StatusMethodNotAllowed) w.Write([]byte(`{"status": "Error"}`)) return } s := sess.GetSession(&c, w, r) key, err := unmarshalKey(r, w) if err != nil { return } // Ensure that this key is added under the session's user's account. key.User = s.User err = localdb.AddAPIKey(*key) if err != nil { http.Error(w, "Database connection error (add key)", http.StatusInternalServerError) w.Write([]byte(`{"status": "Error"}`)) return } charRefresh(s, key, w) return } refresh = func(c web.C, w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "This function must be called with the POST method", http.StatusMethodNotAllowed) w.Write([]byte(`{"status": "Error"}`)) return } s := sess.GetSession(&c, w, r) key, err := unmarshalKey(r, w) if err != nil { return } // Ensure that this key is added under the session's user's account. key.User = s.User charRefresh(s, key, w) return } return }