// If the user logs in (and grants permission), they will be redirected here func loginHandler(w http.ResponseWriter, r *http.Request) { ctx := appengine.NewContext(r) u := user.Current(ctx) if u == nil { log.Errorf(ctx, "No identifiable Google user; is this browser in privacy mode ?") http.Error(w, "No identifiable Google user; is this browser in privacy mode ?", http.StatusInternalServerError) return } //c.Infof(" ** Google user logged in ! [%s]", u.Email) // Snag their email address forever more session, err := sessions.Get(r) if err != nil { // This isn't usually an important error (the session was most likely expired, which is why // we're logging in) - so log as Info, not Error. log.Infof(ctx, "sessions.Get [failing is OK for this call] had err: %v", err) } session.Values["email"] = u.Email session.Values["tstamp"] = time.Now().Format(time.RFC3339) if err := session.Save(r, w); err != nil { log.Errorf(ctx, "session.Save: %v", err) } // Now head back to the main page http.Redirect(w, r, "/", http.StatusFound) }
func logoutHandler(w http.ResponseWriter, r *http.Request) { // Overwrite the session with a nil session session, _ := sessions.Get(r) session.Values["email"] = nil session.Save(r, w) http.Redirect(w, r, "/", http.StatusFound) }
func profileUpdateHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) session := sessions.Get(r) if session.Values["email"] == nil { c.Errorf("profileUpdate:, session was empty; no cookie ?") http.Error(w, "session was empty; no cookie ? is this browser in privacy mode ?", http.StatusInternalServerError) return } email := session.Values["email"].(string) r.ParseForm() lat, err := strconv.ParseFloat(r.FormValue("Lat"), 64) if err != nil { c.Errorf("profileUpdate:, parse lat '%s': %v", r.FormValue("Lat"), err) http.Error(w, err.Error(), http.StatusInternalServerError) return } long, err2 := strconv.ParseFloat(r.FormValue("Long"), 64) if err2 != nil { c.Errorf("profileUpdate:, parse long '%s': %v", r.FormValue("Long"), err) http.Error(w, err2.Error(), http.StatusInternalServerError) return } // Maybe make a call to fetch the elevation ?? // https://developers.google.com/maps/documentation/elevation/intro cp := types.ComplainerProfile{ EmailAddress: email, CallerCode: r.FormValue("CallerCode"), FullName: strings.TrimSpace(r.FormValue("FullName")), Address: strings.TrimSpace(r.FormValue("AutoCompletingMagic")), StructuredAddress: types.PostalAddress{ Number: r.FormValue("AddrNumber"), Street: r.FormValue("AddrStreet"), City: r.FormValue("AddrCity"), State: r.FormValue("AddrState"), Zip: r.FormValue("AddrZip"), Country: r.FormValue("AddrCountry"), }, CcSfo: true, //FormValueCheckbox(r, "CcSfo"), Lat: lat, Long: long, } cdb := complaintdb.ComplaintDB{C: c} err = cdb.PutProfile(cp) if err != nil { c.Errorf("profileUpdate: cdb.Put: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } http.Redirect(w, r, "/", http.StatusFound) }
func downloadHandler(w http.ResponseWriter, r *http.Request) { session := sessions.Get(r) if session.Values["email"] == nil { http.Error(w, "session was empty; no cookie ? is this browser in privacy mode ?", http.StatusInternalServerError) return } c := appengine.Timeout(appengine.NewContext(r), 60*time.Second) cdb := complaintdb.ComplaintDB{C: c} cap, err := cdb.GetAllByEmailAddress(session.Values["email"].(string), true) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } _ = cap filename := date.NowInPdt().Format("complaints-20060102.csv") w.Header().Set("Content-Type", "application/csv") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) cols := []string{ "Date", "Time(PDT)", "Notes", "Speedbrakes", "Loudness", "Activity", "Flightnumber", "Origin", "Destination", "Speed(Knots)", "Altitude(Feet)", "Lat", "Long", "Registration", "Callsign", "VerticalSpeed(FeetPerMin)", "Dist2(km)", "Dist3(km)", } csvWriter := csv.NewWriter(w) csvWriter.Write(cols) for _, c := range cap.Complaints { a := c.AircraftOverhead speedbrakes := "" if c.HeardSpeedbreaks { speedbrakes = "y" } r := []string{ c.Timestamp.Format("2006/01/02"), c.Timestamp.Format("15:04:05"), c.Description, speedbrakes, fmt.Sprintf("%d", c.Loudness), c.Activity, a.FlightNumber, a.Origin, a.Destination, fmt.Sprintf("%.0f", a.Speed), fmt.Sprintf("%.0f", a.Altitude), fmt.Sprintf("%.5f", a.Lat), fmt.Sprintf("%.5f", a.Long), a.Registration, a.Callsign, fmt.Sprintf("%.0f", a.VerticalSpeed), fmt.Sprintf("%.1f", c.Dist2KM), fmt.Sprintf("%.1f", c.Dist3KM), } if err := csvWriter.Write(r); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } csvWriter.Flush() }
// If the user logs in (and grants permission), they will be redirected here func loginHandler(w http.ResponseWriter, r *http.Request) { ctx := appengine.NewContext(r) code := r.FormValue("code") // Unpack the token token, err := getConfig(r).Exchange(ctx, code) if err != nil { log.Errorf(ctx, "/fb/login: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } // Use the token to access the FB API on the user's behalf; simply get their email address client := urlfetch.Client(ctx) var resp *http.Response url := "https://graph.facebook.com/me?fields=email&access_token=" + token.AccessToken if resp, err = client.Get(url); err != nil { log.Errorf(ctx, "/fb/login: client.Get: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { err = fmt.Errorf("Bad FB fetch status: %v", resp.Status) log.Errorf(ctx, "/fb/login: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } var jsonMap map[string]interface{} if err = json.NewDecoder(resp.Body).Decode(&jsonMap); err != nil { log.Errorf(ctx, "/fb/login: bad resp parse%v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } session, err := sessions.Get(r) if err != nil { // This isn't usually an important error (the session was most likely expired, which is why // we're logging in) - so log as Info, not Error. log.Infof(ctx, "sessions.Get [failing is OK for this call] had err: %v", err) } session.Values["email"] = jsonMap["email"] session.Values["tstamp"] = time.Now().Format(time.RFC3339) if err := session.Save(r, w); err != nil { log.Errorf(ctx, "session.Save: %v", err) } http.Redirect(w, r, "/", http.StatusFound) }
func masqueradeHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { email := r.FormValue("e") if email == "" { http.Error(w, "masq needs 'e'", http.StatusInternalServerError) return } log.Infof(ctx, "masq into [%s]", email) session, _ := sessions.Get(r) session.Values["email"] = email session.Save(r, w) http.Redirect(w, r, "/", http.StatusFound) }
func masqueradeHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) email := r.FormValue("e") if email == "" { http.Error(w, "masq needs 'e'", http.StatusInternalServerError) return } c.Infof("masq into [%s]", email) session := sessions.Get(r) session.Values["email"] = email session.Save(r, w) http.Redirect(w, r, "/", http.StatusFound) }
// If the user logs in (and grants permission), they will be redirected here func loginHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) u := user.Current(c) if u == nil { c.Errorf("No identifiable Google user; is this browser in privacy mode ?") http.Error(w, "No identifiable Google user; is this browser in privacy mode ?", http.StatusInternalServerError) return } //c.Infof(" ** Google user logged in ! [%s]", u.Email) // Snag their email address forever more session := sessions.Get(r) session.Values["email"] = u.Email session.Save(r,w) // Now head back to the main page http.Redirect(w, r, "/", http.StatusFound) }
// If the user logs in (and grants permission), they will be redirected here func loginHandler(w http.ResponseWriter, r *http.Request) { var c context.Context = newappengine.NewContext(r) code := r.FormValue("code") // Unpack the token token, err := getConfig(r).Exchange(c, code) if err != nil { appengine.NewContext(r).Errorf("/fb/login: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } // Use the token to access the FB API on the user's behalf; simply get their email address client := newurlfetch.Client(c) var resp *http.Response url := "https://graph.facebook.com/me?fields=email&access_token=" + token.AccessToken if resp, err = client.Get(url); err != nil { appengine.NewContext(r).Errorf("/fb/login: client.Get: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { err = fmt.Errorf("Bad FB fetch status: %v", resp.Status) appengine.NewContext(r).Errorf("/fb/login: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } var jsonMap map[string]interface{} if err = json.NewDecoder(resp.Body).Decode(&jsonMap); err != nil { appengine.NewContext(r).Errorf("/fb/login: bad resp parse%v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } session := sessions.Get(r) session.Values["email"] = jsonMap["email"] session.Save(r, w) // appengine.NewContext(r).Infof(" ** Facebook user logged in ! [%s]", jsonMap["email"]) http.Redirect(w, r, "/", http.StatusFound) }
func HandleWithSession(ch contextHandler, ifNoSessionRedirectTo string) baseHandler { return func(w http.ResponseWriter, r *http.Request) { ctx, _ := context.WithTimeout(appengine.NewContext(r), 55*time.Second) session, err := sessions.Get(r) if err != nil { log.Errorf(ctx, "session.Get failed with err: %v", err) } if strings.HasPrefix(r.UserAgent(), "Google") { // Robot - do nothing } else if session.Values["email"] == nil { reqBytes, _ := httputil.DumpRequest(r, true) log.Errorf(ctx, "session was empty") //for _,c := range r.Cookies() { // log.Errorf(ctx, "cookie: %s", c) //} log.Errorf(ctx, "req: %s", reqBytes) // If we have a URL to redirect to, in cases of no session, then do it if ifNoSessionRedirectTo != "" { http.Redirect(w, r, ifNoSessionRedirectTo, http.StatusFound) return } } else { sesh := UserSession{Email: session.Values["email"].(string)} if session.Values["tstamp"] != nil { tstampStr := session.Values["tstamp"].(string) tstamp, _ := time.Parse(time.RFC3339, tstampStr) sesh.CreatedAt = tstamp sesh.hasCreatedAt = true // time.IsZero seems useless } ctx = context.WithValue(ctx, sessionEmailKey, sesh) } // Call the underlying handler, with our shiny context ch(ctx, w, r) } }
func profileFormHandler(w http.ResponseWriter, r *http.Request) { // https, yay if r.URL.Host == "stop.jetnoise.net" { // We're behind cloudflare, so we always see http. This is how we can tell if the user is // using https ... if r.Header.Get("Cf-Visitor") != `{"scheme":"https"}` { safeUrl := r.URL safeUrl.Scheme = "https" http.Redirect(w, r, safeUrl.String(), http.StatusFound) return } } c := appengine.NewContext(r) session := sessions.Get(r) if session.Values["email"] == nil { http.Error(w, "session was empty; no cookie ? is this browser in privacy mode ?", http.StatusInternalServerError) return } email := session.Values["email"].(string) cdb := complaintdb.ComplaintDB{C: c} cp, _ := cdb.GetProfileByEmailAddress(email) if cp.EmailAddress == "" { // First ever visit - empty profile ! cp.EmailAddress = email cp.CcSfo = true } var params = map[string]interface{}{ "Profile": cp, "MapsAPIKey": kGoogleMapsAPIKey, // For autocomplete & latlong goodness } params["Message"] = r.FormValue("msg") if err := templates.ExecuteTemplate(w, "profile-edit", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
func personalReportHandler(w http.ResponseWriter, r *http.Request) { session := sessions.Get(r) if session.Values["email"] == nil { http.Error(w, "session was empty; no cookie ?", http.StatusInternalServerError) return } email := session.Values["email"].(string) if r.FormValue("date") == "" { var params = map[string]interface{}{ "Yesterday": date.NowInPdt().AddDate(0, 0, -1), } if err := templates.ExecuteTemplate(w, "personal-report-form", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } start, end, _ := widget.FormValueDateRange(r) ctx := appengine.Timeout(appengine.NewContext(r), 60*time.Second) cdb := complaintdb.ComplaintDB{C: ctx} w.Header().Set("Content-Type", "text/plain") // w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", "sc.txt")) fmt.Fprintf(w, "Personal disturbances report for <%s>:\n From [%s]\n To [%s]\n", email, start, end) complaintStrings := []string{} var countsByHour [24]int countsByDate := map[string]int{} countsByAirline := map[string]int{} iter := cdb.NewIter(cdb.QueryInSpanByEmailAddress(start, end, email)) n := 0 for { c := iter.Next() if c == nil { break } str := fmt.Sprintf("Time: %s, Loudness:%d, Speedbrakes:%v, Flight:%6.6s, Notes:%s", c.Timestamp.Format("2006.01.02 15:04:05"), c.Loudness, c.HeardSpeedbreaks, c.AircraftOverhead.FlightNumber, c.Description) n++ complaintStrings = append(complaintStrings, str) countsByHour[c.Timestamp.Hour()]++ countsByDate[c.Timestamp.Format("2006.01.02")]++ if airline := c.AircraftOverhead.IATAAirlineCode(); airline != "" { countsByAirline[airline]++ } } fmt.Fprintf(w, "\nTotal number of disturbance reports, over %d days: %d\n", len(countsByDate), n) fmt.Fprintf(w, "\nDisturbance reports, counted by Airline (where known):\n") for _, k := range keysByIntValDesc(countsByAirline) { fmt.Fprintf(w, " %s: % 4d\n", k, countsByAirline[k]) } fmt.Fprintf(w, "\nDisturbance reports, counted by date:\n") for _, k := range keysByKeyAsc(countsByDate) { fmt.Fprintf(w, " %s: % 4d\n", k, countsByDate[k]) } fmt.Fprintf(w, "\nDisturbance reports, counted by hour of day (across all dates):\n") for i, n := range countsByHour { fmt.Fprintf(w, " %02d: % 4d\n", i, n) } fmt.Fprintf(w, "\nFull dump of all disturbance reports:\n\n") for _, s := range complaintStrings { fmt.Fprint(w, s+"\n") } }
func emailHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) session := sessions.Get(r) cdb := complaintdb.ComplaintDB{C: c} cp, err := cdb.GetProfileByEmailAddress(session.Values["email"].(string)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } start, end := date.WindowForYesterday() // end = time.Now() complaints, err2 := cdb.GetComplaintsInSpanByEmailAddress(cp.EmailAddress, start, end) if err2 != nil { http.Error(w, err2.Error(), http.StatusInternalServerError) return } var cap = types.ComplaintsAndProfile{ Profile: *cp, Complaints: complaints, } if err := templates.ExecuteTemplate(w, "email-update", map[string]interface{}{}); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } else { return } msg, err3 := GenerateEmail(c, cap) if err3 != nil { http.Error(w, err3.Error(), http.StatusInternalServerError) return } cap.Profile.CcSfo = true if len(cap.Complaints) == 0 { http.Error(w, "No complaints found ?!", http.StatusInternalServerError) return } msg2, err4 := GenerateSingleComplaintEmail(c, cap.Profile, cap.Complaints[len(cap.Complaints)-1]) if err4 != nil { http.Error(w, err4.Error(), http.StatusInternalServerError) return } var params = map[string]interface{}{ "Cap": cap, "EmailBundle": msg, "EmailSingle": msg2, "EmailBundleBody": template.HTML(msg.HTMLBody), "EmailSingleBody": template.HTML(msg2.HTMLBody), } /* if err4 := sendViaHTTPGateway(c, msg); err4 != nil { http.Error(w, err4.Error(), http.StatusInternalServerError) return } */ if err := templates.ExecuteTemplate(w, "email-debug", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
func rootHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) session := sessions.Get(r) // No session ? Get them to login if session.Values["email"] == nil { fb.AppId = kFacebookAppId fb.AppSecret = kFacebookAppSecret loginUrls := map[string]string{ "googlefromscratch": g.GetLoginUrl(r, true), "google": g.GetLoginUrl(r, false), "facebook": fb.GetLoginUrl(r), } if err := templates.ExecuteTemplate(w, "landing", loginUrls); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } modes := map[string]bool{} // The rootHandler is the URL wildcard. Except Fragments, which are broken. if r.URL.Path == "/full" { modes["expanded"] = true } else if r.URL.Path == "/edit" { modes["edit"] = true } else if r.URL.Path == "/debug" { modes["debug"] = true } else if r.URL.Path != "/" { // This is a request for apple_icon or somesuch junk. Just say no. http.NotFound(w, r) return } cdb := complaintdb.ComplaintDB{C: c} cap, err := cdb.GetAllByEmailAddress(session.Values["email"].(string), modes["expanded"]) if cap == nil && err == nil { // No profile exists; daisy-chain into profile page http.Redirect(w, r, "/profile", http.StatusFound) return } else if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } modes["admin"] = user.Current(c) != nil && user.Current(c).Admin modes["superuser"] = modes["admin"] || cap.Profile.EmailAddress == "*****@*****.**" // Default to "", unless we had a complaint in the past hour. lastActivity := "" if len(cap.Complaints) > 0 && time.Since(cap.Complaints[0].Timestamp) < time.Hour { lastActivity = cap.Complaints[0].Activity } var complaintDefaults = map[string]interface{}{ "ActivityList": kActivities, // lives in add-complaint "DefaultActivity": lastActivity, "DefaultLoudness": 1, "NewForm": true, } message := "" if cap.Profile.FullName == "" { message += "<li>We don't have your full name</li>" } if cap.Profile.StructuredAddress.Zip == "" { message += "<li>We don't have an accurate address</li>" } if message != "" { message = fmt.Sprintf("<p><b>We've found some problems with your profile:</b></p><ul>%s</ul>"+ "<p> Without this data, your complaints might be exluded, so please "+ "<a href=\"/profile\"><b>update your profile</b></a> !</p>", message) } var params = map[string]interface{}{ //"Message": template.HTML("Hi!"), "Cap": *cap, "Complaints": hintComplaints(cap.Complaints, modes["superuser"]), "Now": date.NowInPdt(), "Modes": modes, "ComplaintDefaults": complaintDefaults, "Message": template.HTML(message), } if err := templates.ExecuteTemplate(w, "main", params); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }