예제 #1
0
파일: g.go 프로젝트: skypies/complaints
// 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)
}
예제 #2
0
파일: main.go 프로젝트: skypies/complaints
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)
}
예제 #3
0
파일: profile.go 프로젝트: hugoh/complaints
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)
}
예제 #4
0
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()
}
예제 #5
0
파일: fb.go 프로젝트: skypies/complaints
// 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)
}
예제 #6
0
파일: main.go 프로젝트: skypies/complaints
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)
}
예제 #7
0
파일: main.go 프로젝트: hugoh/complaints
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)
}
예제 #8
0
파일: g.go 프로젝트: hugoh/complaints
// 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)
}
예제 #9
0
파일: fb.go 프로젝트: hugoh/complaints
// 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)
}
예제 #10
0
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)
	}
}
예제 #11
0
파일: profile.go 프로젝트: hugoh/complaints
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)
	}
}
예제 #12
0
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")
	}
}
예제 #13
0
파일: email.go 프로젝트: hugoh/complaints
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)
	}
}
예제 #14
0
파일: main.go 프로젝트: hugoh/complaints
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)
	}
}