Example #1
0
func SendAlerts(ctx context.Context, stocksForAlert []PortfolioStock) {
	if len(stocksForAlert) == 0 {
		log.Debugf(ctx, "SendAlerts: Alert stocks are empty")
		return
	}
	//group by user emails to send a consolidated email
	groupedStockAlerts := make(map[string][]PortfolioStock)
	for _, alert := range stocksForAlert {
		userPortfolioStocks := groupedStockAlerts[alert.Email]
		userPortfolioStocks = append(userPortfolioStocks, alert)
		groupedStockAlerts[alert.Email] = userPortfolioStocks
	}
	log.Debugf(ctx, "Will send alerts for ", Jsonify(groupedStockAlerts))
	for email, alerts := range groupedStockAlerts {
		msg := &mail.Message{
			Sender:  "NewTechFellas Stock Alerts <*****@*****.**>",
			To:      []string{email},
			Subject: "Newtechfellas stock alerts for your stocks - " + DateString(),
			Body:    getStocksAlertMailBody(alerts),
		}
		if err := mail.Send(ctx, msg); err != nil {
			log.Debugf(ctx, "Couldn't send email: %v", err)
		}
	}

	for _, portfolioStock := range stocksForAlert {
		portfolioStock.AlertSentTime = time.Now()
		//Save stocksForAlert to update last alert sent time
		CreateOrUpdate(ctx, &portfolioStock, portfolioStock.kind(), portfolioStock.stringId(), 0)
	}
}
Example #2
0
func emailMentions(ctx context.Context, tweet *Tweet) {
	u := user.Current(ctx)
	var words []string
	words = strings.Fields(tweet.Message)
	for _, value := range words {
		if strings.HasPrefix(value, "@") {
			username := value[1:]
			profile, err := getProfileByUsername(ctx, username)
			if err != nil {
				// they don't have a profile, so skip it
				continue
			}
			msg := &mail.Message{
				Sender:  u.Email,
				To:      []string{profile.Username + " <" + profile.Email + ">"},
				Subject: "You were mentioned in a tweet",
				Body:    tweet.Message + " from " + tweet.Username + " - " + humanize.Time(tweet.Time),
			}
			if err := mail.Send(ctx, msg); err != nil {
				log.Errorf(ctx, "Alas, my user, the email failed to sendeth: %v", err)
				continue
			}

		}
	}
}
Example #3
0
func emailSend(w http.ResponseWriter, r *http.Request, m map[string]string) {

	c := appengine.NewContext(r)
	//addr := r.FormValue("email")

	if _, ok := m["subject"]; !ok {
		m["subject"] = "empty subject line"
	}

	email_thread_id := []string{"3223"}

	msg := &ae_mail.Message{
		//Sender:  "Peter Buchmann <*****@*****.**",
		//		Sender: "*****@*****.**",
		Sender: "*****@*****.**",
		//To:	   []string{addr},
		To: []string{"*****@*****.**"},

		Subject: m["subject"],
		Body:    "some_body some_body2",
		Headers: go_mail.Header{"References": email_thread_id},
	}
	err := ae_mail.Send(c, msg)
	loghttp.E(w, r, err, false, "could not send the email")

}
func handleOOBAction(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	resp, err := gitkitClient.GenerateOOBCode(c, r)
	if err != nil {
		aelog.Errorf(c, "Failed to get an OOB code: %s", err)
		w.Write([]byte(gitkit.ErrorResponse(err)))
		return
	}
	msg := &mail.Message{
		Sender: "FavWeekday Support <*****@*****.**>",
	}
	switch resp.Action {
	case gitkit.OOBActionResetPassword:
		msg.Subject = "Reset your FavWeekday account password"
		msg.HTMLBody = fmt.Sprintf(emailTemplateResetPassword, resp.Email, resp.OOBCodeURL.String())
		msg.To = []string{resp.Email}
	case gitkit.OOBActionChangeEmail:
		msg.Subject = "FavWeekday account email address change confirmation"
		msg.HTMLBody = fmt.Sprintf(emailTemplateChangeEmail, resp.Email, resp.NewEmail, resp.OOBCodeURL.String())
		msg.To = []string{resp.NewEmail}
	case gitkit.OOBActionVerifyEmail:
		msg.Subject = "FavWeekday account registration confirmation"
		msg.HTMLBody = fmt.Sprintf(emailTemplateVerifyEmail, resp.OOBCodeURL.String())
		msg.To = []string{resp.Email}
	}
	if err := mail.Send(c, msg); err != nil {
		aelog.Errorf(c, "Failed to send %s message to user %s: %s", resp.Action, resp.Email, err)
		w.Write([]byte(gitkit.ErrorResponse(err)))
		return
	}
	w.Write([]byte(gitkit.SuccessResponse()))
}
Example #5
0
func SendEmailToAllUsers(r *http.Request, subject string) int {
	ctx := req2ctx(r)
	cdb := complaintdb.NewDB(ctx)

	if cps, err := cdb.GetAllProfiles(); err != nil {
		cdb.Errorf("SendEmailToAllUsers/GetAllProfiles: %v", err)
		return 0

	} else {
		buf := new(bytes.Buffer)
		params := map[string]interface{}{}
		if err := templates.ExecuteTemplate(buf, "email-update", params); err != nil {
			return 0
		}

		n := 0
		for _, cp := range cps {
			msg := &mail.Message{
				Sender:   kSenderEmail,
				ReplyTo:  kSenderEmail,
				To:       []string{cp.EmailAddress},
				Subject:  subject,
				HTMLBody: buf.String(),
			}
			if err := mail.Send(cdb.Ctx(), msg); err != nil {
				cdb.Errorf("Could not send useremail to <%s>: %v", cp.EmailAddress, err)
			}
			n++
		}
		return n
	}
}
Example #6
0
func sendMail(ctx context.Context, mentionProfile *Profile) error {
	msg := &mail.Message{
		Sender:  "<*****@*****.**>",
		To:      []string{mentionProfile.Email},
		Subject: "Someone mentioned you!",
		Body:    fmt.Sprintf("Someone mentioned you!"),
	}
	return mail.Send(ctx, msg)
}
func sendVerificationCodeEmail(ctx context.Context, user User) {
	msg := &mail.Message{
		Sender:  "NewTechFellas Stock Alerts Admin <*****@*****.**>",
		To:      []string{user.Email},
		Subject: "Newtechfellas stock alerts verify user",
		Body:    fmt.Sprintf("Your confirmation code is %v", user.VerificationCode),
	}
	if err := mail.Send(ctx, msg); err != nil {
		log.Debugf(ctx, "Couldn't send email: %v", err)
	}
}
Example #8
0
func SendEmailToAdmin(c context.Context, subject, htmlbody string) {
	msg := &mail.Message{
		Sender:   kSenderEmail, // cap.Profile.EmailAddress,
		To:       []string{kAdminEmail},
		Subject:  subject,
		HTMLBody: htmlbody,
	}

	if err := mail.Send(c, msg); err != nil {
		log.Errorf(c, "Could not send adminemail to <%s>: %v", kAdminEmail, err)
	}
}
Example #9
0
File: sub.go Project: pbochis/api
func (sub Subscription) RequestConfirmation(ctx context.Context) error {
	buf := new(bytes.Buffer)
	if err := subscription.Execute(buf, sub); err != nil {
		return err
	}
	return appmail.Send(ctx, &appmail.Message{
		Sender:  "Lorenz Leutgeb <*****@*****.**>",
		To:      []string{sub.Address.String()},
		Subject: "Hello from Coduno",
		Body:    buf.String(),
	})
}
Example #10
0
func followedEmail(w http.ResponseWriter, r *http.Request, email string) {
	ctx := appengine.NewContext(r)
	msg := &mail.Message{
		Sender:  "TwitClone Support <*****@*****.**>",
		To:      []string{email},
		Subject: "You are being followed",
		Body:    fmt.Sprintf(confirmMessage),
	}
	if err := mail.Send(ctx, msg); err != nil {
		log.Errorf(ctx, "Couldn't send email: %v", err)
	}
}
Example #11
0
func sendEmail(email string, c context.Context) {
	msg := &mail.Message{
		Sender:  "*****@*****.**",
		To:      []string{email},
		Subject: "Agradecimento",
		Body:    "Muito obrigada, farás muitos rostinhos sorrirem!",
	}
	if err := mail.Send(c, msg); err != nil {
		log.Errorf(c, "Couldn't send email to %s: %v", email, err)
	} else {
		log.Infof(c, "Sent email to: %s", email, err)
	}
}
Example #12
0
func sendReminderEmail(ctx context.Context,
	email string,
	description string,
	dueDate time.Time) bool {
	msg := &mail.Message{
		Sender:  "Tada <*****@*****.**>",
		To:      []string{email},
		Subject: fmt.Sprintf("[Tada reminder] %s", description),
		Body: fmt.Sprintf(`This is a friendly reminder about your task: \n
                       %s \n
                       (due %s)\n`, description, dueDate),
	}
	return (mail.Send(ctx, msg) == nil)
}
Example #13
0
func confirm(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	addr := r.FormValue("email")
	url := createConfirmationURL(r)
	msg := &mail.Message{
		Sender:  "Example.com Support <*****@*****.**>",
		To:      []string{addr},
		Subject: "Confirm your registration",
		Body:    fmt.Sprintf(confirmMessage, url),
	}
	if err := mail.Send(ctx, msg); err != nil {
		log.Errorf(ctx, "Couldn't send email: %v", err)
	}
}
Example #14
0
// sendEmail with the AppEngine API.
func sendEmail(r *http.Request, from, to, subject, body string) {
	// Use AppEngine to send our thank you cards
	c := appengine.NewContext(r)
	msg := &mail.Message{
		Sender:  from,
		To:      []string{to},
		Subject: subject,
		Body:    body,
	}

	if err := mail.Send(c, msg); err != nil {
		log.Printf("[ERROR] Could not send email: %v\n", err)
	}
}
Example #15
0
func handleIndex(res http.ResponseWriter, req *http.Request) {
	ctx := appengine.NewContext(req)
	u := user.Current(ctx)

	msg := &mail.Message{
		Sender:  u.Email,
		To:      []string{"Caleb Doxsey <*****@*****.**>"},
		Subject: "See you tonight",
		Body:    "Don't forget our plans. Hark, 'til later.",
	}
	if err := mail.Send(ctx, msg); err != nil {
		log.Errorf(ctx, "Alas, my user, the email failed to sendeth: %v", err)
	}
}
Example #16
0
func sendEmail(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {

	if r.Method == "POST" {
		c := appengine.NewContext(r)

		addr := r.FormValue("email")
		firstName := r.FormValue("firstName")
		lastName := r.FormValue("lastName")
		message := r.FormValue("message")
		pickupDate := r.FormValue("pickupDate")
		orderType := r.FormValue("orderType")

		msgToBB := &mail.Message{
			Sender:   "The Bakers Box Cafe <*****@*****.**>",
			ReplyTo:  addr,
			To:       []string{"*****@*****.**"},
			Subject:  "New Inquiry from: " + firstName + " " + lastName + "!",
			HTMLBody: message + "order by date: " + pickupDate + "\norder type: " + orderType,
		}
		if err := mail.Send(c, msgToBB); err != nil {
			log.Errorf(c, "Message failed to send: %v", err)
		}

		msg := &mail.Message{
			Sender:   "The Bakers Box Cafe <*****@*****.**>",
			To:       []string{addr},
			Subject:  "Thank you for contacting me!",
			HTMLBody: confirmMessage,
		}
		if err := mail.Send(c, msg); err != nil {
			log.Errorf(c, "Alas, my user, the email failed to sendeth: %v", err)
		}
	}

	// Redirect user to home page after sent email
	http.Redirect(w, r, "/", 303)
}
Example #17
0
func NotifyAdmin(c *gin.Context, user *auth.User) {
	r := c.Request
	ctx := appengine.NewContext(r)
	addr := "*****@*****.**"
	msg := &mail.Message{
		Sender:  "*****@*****.**",
		To:      []string{addr},
		Subject: fmt.Sprintf("User %s raised volunteer request", user.Email),
		Body:    fmt.Sprintf("User Msg %s %s", user.Message),
	}
	log.Debugf(ctx, "Mail msg %v", msg)
	if err := mail.Send(ctx, msg); err != nil {
		log.Errorf(ctx, "Couldn't send email: %v", err)
	}
}
Example #18
0
func SendEmailVolunteer(c *gin.Context, user *auth.User) {
	r := c.Request
	ctx := appengine.NewContext(r)
	addr := user.Email
	url := createConfirmationURL(user)
	msg := &mail.Message{
		Sender:  "*****@*****.**",
		To:      []string{addr},
		Subject: "Confirm your volunteer request",
		Body:    fmt.Sprintf(confirmMessage, url),
	}
	log.Debugf(ctx, "Mail msg %v", msg)
	if err := mail.Send(ctx, msg); err != nil {
		log.Errorf(ctx, "Couldn't send email: %v", err)
	}
}
Example #19
0
func emailUser(ctx context.Context, username string, t *tweet) error {
	to, err := getProfileByUsername(ctx, username)
	if err == datastore.ErrNoSuchEntity {
		return nil
	} else if err != nil {
		return err
	}
	msg := &mail.Message{
		Sender:  "*****@*****.**",
		To:      []string{fmt.Sprintf("%s <%s>", to.Username, to.Email)},
		Subject: fmt.Sprintf("Mention from %s", t.Username),
		Body:    fmt.Sprintf("%s has mentioned you in a tweet: %s", t.Username, t.Message),
	}
	err = mail.Send(ctx, msg)
	return err
}
Example #20
0
func echoMail(ctx context.Context, m mail.Message) {
	from, err := m.Header.AddressList("From")
	if err != nil {
		log.Warningf(ctx, "Failed getting sender of mail: %+v", m)
		return
	}

	b, _ := ioutil.ReadAll(m.Body)

	am := &appmail.Message{
		Sender:  "*****@*****.**",
		To:      []string{from[0].String()},
		Body:    string(b),
		Headers: m.Header,
	}

	err = appmail.Send(ctx, am)
	if err != nil {
		log.Errorf(ctx, err.Error())
	}
}
Example #21
0
func sendEmail(email, link, name string, r *http.Request) {
	var err error
	var doc bytes.Buffer
	///Props to Nathan Leclaire for his post on Golang Emails
	const emailTemplate = `
{{.Body}}
{{.Link}}
Sincerely,
{{.From}}
  `
	context := &SmtpTemplateData{
		From:    "Albert Hermida",
		To:      email,
		Subject: "Authorize your account for Writer",
		Body:    "Hey, Thanks for signing up! Click on the link to authorize your account:",
		Link:    link,
	}
	t := template.New("emailTemplate")
	t, err = t.Parse(emailTemplate)
	if err != nil {
		//  log.Print("error trying to parse mail template")
	}
	err = t.Execute(&doc, context)
	if err != nil {
		// log.Print("error trying to execute mail template")
	}
	//Google App Engine Handling
	ctx := appengine.NewContext(r)
	msg := &mail.Message{
		Sender:  "Albert Hermida <*****@*****.**>",
		To:      []string{context.To},
		Subject: context.Subject,
		Body:    doc.String(),
	}
	if err := mail.Send(ctx, msg); err != nil {
		log.Errorf(ctx, "Couldn't send email: %v", err)
		//  log.Print("ERROR: attempting to send a mail ", err)
	}

}
Example #22
0
func contactHandler(w http.ResponseWriter, r *http.Request) {
	t := template.Must(template.ParseGlob("template/*")) // add sub-templates in /template
	if r.Method == "GET" {
		t.ParseFiles("contact.html")
		err := t.ExecuteTemplate(w, "base", nil)
		if err != nil {
			w.Write([]byte(err.Error()))
		}
	}
	if r.Method == "POST" {
		r.ParseForm()
		ctx := appengine.NewContext(r)
		addr := r.FormValue("email")
		msg := &mail.Message{
			Sender:  "Example.com Support <" + addr + ">",
			To:      []string{"*****@*****.**"},
			Subject: "Confirm your registration",
			Body:    fmt.Sprintf(r.FormValue("message"), nil),
		}
		if err := mail.Send(ctx, msg); err != nil {
			fmt.Println(err.Error())
		}
	}
}
Example #23
0
// SendMail to send an email message.
func SendMail(req *wcg.Request, msg *mail.Message) error {
	return mail.Send(NewContext(req), msg)
}
Example #24
0
func (m mailImpl) SendToAdmins(msg *gae_mail.Message) error {
	return mail.Send(m.aeCtx, msg.ToSDKMessage())
}
Example #25
0
func SendComplaintsWithSpan(r *http.Request, start, end time.Time) (err error, str string) {
	ctx := req2ctx(r)
	cdb := complaintdb.NewDB(ctx)
	cdb.Infof("--- Emails, %s -> %s", start, end)

	blacklist := map[string]bool{}
	for _, e := range blacklistAddrs {
		blacklist[e] = true
	}

	var cps = []types.ComplainerProfile{}
	cps, err = cdb.GetAllProfiles()
	if err != nil {
		return
	}

	complaints_private, complaints_submitted, no_data, sent_ok, sent_fail := 0, 0, 0, 0, 0
	sent_single_ok, sent_single_fail := 0, 0

	for _, cp := range cps {
		var complaints = []types.Complaint{}
		complaints, err = cdb.GetComplaintsInSpanByEmailAddress(cp.EmailAddress, start, end)

		if err != nil {
			cdb.Errorf("Could not get complaints [%v->%v] for <%s>: %v", start, end, cp.EmailAddress, err)
			no_data++
			continue
		}
		if len(complaints) == 0 {
			no_data++
			continue
		}

		var cap = types.ComplaintsAndProfile{
			Profile:    cp,
			Complaints: complaints,
		}

		var msg *mail.Message
		if msg, err = GenerateEmail(ctx, cap); err != nil {
			cdb.Errorf("Could not generate email to <%s>: %v", cp.EmailAddress, err)
			sent_fail++
			continue
		}

		if blacklist[cp.EmailAddress] {
			sent_fail++
		} else {
			if err = mail.Send(ctx, msg); err != nil {
				cdb.Errorf("Could not send email to <%s>: %v", cp.EmailAddress, err)
				sent_fail++
				continue
			}
		}

		complaints_submitted += len(cap.Complaints)
		sent_ok++
	}

	subject := fmt.Sprintf("Daily report stats: users:%d/%d  reports:%d/%d  emails:%d:%d",
		sent_ok, (sent_ok + no_data),
		complaints_submitted, (complaints_submitted + complaints_private),
		sent_single_ok, sent_single_fail)

	SendEmailToAdmin(ctx, subject, "")

	dc := complaintdb.DailyCount{
		Datestring:     date.Time2Datestring(start.Add(time.Hour)),
		NumComplaints:  complaints_submitted + complaints_private,
		NumComplainers: sent_ok,
	}
	cdb.AddDailyCount(dc)

	str = fmt.Sprintf("email wrapup: %d ok, %d fail (%d no data) : %d reports submitted (%d kept back)  single[%d/%d]", sent_ok, sent_fail, no_data, complaints_submitted, complaints_private, sent_single_ok, sent_single_fail)

	cdb.Infof("--- %s", str)

	return
}
Example #26
0
func handleContactSend(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	var m Meta
	err := datastore.Get(c, datastore.NewKey(c, "Meta", "main", 0, nil), &m)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Errorf(c, "get meta: %s", err.Error())
		return
	}
	m.Path = r.URL.Path
	name := strings.TrimSpace(r.FormValue("name"))
	email := strings.TrimSpace(r.FormValue("email"))
	message := strings.TrimSpace(r.FormValue("message"))
	var cd ContactData
	cd.Meta = &m
	cd.Name = name
	cd.Email = email
	cd.Message = message

	defer func() {
		err = t.ExecuteTemplate(w, "contact.html", cd)
		if err != nil {
			log.Errorf(c, "render contact.html: %s", err.Error())
		}
	}()

	if name == "" {
		cd.ErrorMessage = "Name is required"
		return
	}
	addr, err := mail.ParseAddress(email)
	if err != nil {
		cd.ErrorMessage = "Valid email address is required: " + err.Error()
		return
	}
	if message == "" {
		cd.ErrorMessage = "Message is required"
		return
	}
	addr.Name = name

	msg := &aemail.Message{
		Sender:  addr.String(),
		To:      []string{m.ContactEmail},
		Subject: "Contact",
		Body:    message,
	}

	err = aemail.Send(c, msg)
	if err != nil {
		cd.ErrorMessage = "Could not send message, try again later"
		log.Errorf(c, "send message: %s", err.Error())
		return
	}

	cd.Email = ""
	cd.Name = ""
	cd.Message = ""
	cd.SuccessMessage = "Message Sent"

}
Example #27
0
// Invitation handles the creation of a new invitation and sends an e-mail to
// the user.
func Invitation(ctx context.Context, w http.ResponseWriter, r *http.Request) (status int, err error) {
	if r.Method != "POST" {
		return http.StatusMethodNotAllowed, nil
	}

	if err := initInvitationTemplate(); err != nil {
		return http.StatusInternalServerError, err
	}

	p, ok := passenger.FromContext(ctx)
	if !ok {
		return http.StatusUnauthorized, nil
	}

	var u model.User
	if err = datastore.Get(ctx, p.User, &u); err != nil {
		return http.StatusInternalServerError, nil
	}

	cKey := u.Company
	if cKey == nil {
		return http.StatusUnauthorized, nil
	}

	var company model.Company
	if err = datastore.Get(ctx, cKey, &company); err != nil {
		return http.StatusInternalServerError, err
	}

	var params = struct {
		Address, Challenge string
	}{}

	if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
		return http.StatusBadRequest, err
	}

	address, err := mail.ParseAddress(params.Address)
	if err != nil {
		return http.StatusBadRequest, err
	}

	challengeKey, err := datastore.DecodeKey(params.Challenge)
	if err != nil {
		return http.StatusBadRequest, err
	}

	var challenge model.Challenge
	if err := datastore.Get(ctx, challengeKey, &challenge); err != nil {
		// TODO(flowlo): Actually look into err. If it is just something like
		// "not found", an internal server error is not appropriate.
		return http.StatusInternalServerError, err
	}

	// TODO(flowlo): Check whether the parent of the current user is the
	// parent of the challenge (if any), and check whether the challenge
	// even exists.

	var users model.Users
	keys, err := model.NewQueryForUser().
		Filter("Address=", address.Address).
		Limit(1).
		GetAll(ctx, &users)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	var key *datastore.Key
	var user model.User
	if len(keys) == 1 {
		key = keys[0]
		user = users[0]
	} else {
		user = model.User{Address: *address}
		key, err = user.Put(ctx, nil)
		if err != nil {
			return http.StatusInternalServerError, err
		}
		profile := model.Profile{}
		if _, err = profile.PutWithParent(ctx, key); err != nil {
			return http.StatusInternalServerError, err
		}
	}

	// NOTE: We are creating a new, orphaned Passenger here, because a
	// Passenger can only issue tokens for the encapsulated user.
	np := passenger.Passenger{
		User: key,
	}

	now := time.Now()
	token := &model.Token{
		Creation:    now,
		Expiry:      now.Add(time.Hour * 24 * 365),
		Description: "Initialization Token",
	}

	value, err := np.IssueToken(ctx, token)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	query := base64.URLEncoding.EncodeToString([]byte(params.Challenge + ":" + value))

	i := model.Invitation{
		User: key,
	}

	// If we're on dev, generate a URL that will point at the dev instance, not
	// production.
	// TODO(flowlo): Move this magic constant somewhere to be configured, as port
	// 6060 is no official thing.
	prefix := "https://app.cod.uno"
	if appengine.IsDevAppServer() {
		prefix = "http://*****:*****@cod.uno>",
		To:      []string{user.Address.String()},
		Subject: "We challenge you!",
		Body:    buf.String(),
	}); err != nil {
		return http.StatusInternalServerError, err
	}

	key, err = i.PutWithParent(ctx, cKey)
	if err != nil {
		return http.StatusInternalServerError, err
	}

	json.NewEncoder(w).Encode(i.Key(key))
	return http.StatusOK, nil
}
Example #28
0
func adminMailUnpaid(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	total := 0
	people := map[string]mailTask{}

	q := datastore.NewQuery("LoggedTask").
		Filter("Paid = ", false).
		Order("Name")

	for t := q.Run(c); ; {
		var x LoggedTask
		_, err := t.Next(&x)
		if err != nil {
			break
		}
		total += x.Amount
		p := people[x.Who]
		p.Amount += x.Amount

		mut, ok := p.Tasks[x.Name]
		if !ok {
			mut = &mailUserTask{Task: x}
			if p.Tasks == nil {
				p.Tasks = map[string]*mailUserTask{}
			}
			p.Tasks[x.Name] = mut
		}

		mut.Quantity++
		mut.Subtotal += x.Amount
		people[x.Who] = p
	}

	buf := &bytes.Buffer{}
	tw := tabwriter.NewWriter(buf, 0, 2, 1, ' ', 0)
	err := execTemplate(c, tw, "mail.txt",
		struct {
			Total  int
			People map[string]mailTask
		}{total, people})
	if err != nil {
		log.Errorf(c, "Template error: %v", err)
		return
	}
	tw.Flush()

	msg := &mail.Message{
		Sender:  "Dustin Sallings <*****@*****.**>",
		To:      []string{"*****@*****.**"},
		Subject: "Payment Report",
		Body:    string(buf.Bytes()),
	}
	log.Infof(c, "Sending:\n%s\n", msg.Body)
	if err := mail.Send(c, msg); err != nil {
		log.Errorf(c, "Couldn't send email: %v", err)
		http.Error(w, err.Error(), 500)
		return
	}

	w.WriteHeader(204)
}