// Handler is the main loop that handles incoming mail - It satisfies the DeliverFunc // interface required by imapclient but is a method attached to a set of rich state // objects. func (eng *Engine) Handler(r io.ReadSeeker, uid uint32, sha1 []byte) error { thismail, err := email.NewEmailFromReader(r) if err != nil { r.Seek(0, 0) ioutil.ReadAll(r) erroneousBody, err2 := ioutil.ReadAll(r) if err2 != nil { panic("Error getting body from bad email, to report actual error: " + err2.Error()) } log15.Error("Received email but failed to parse", log15.Ctx{"context": "imap", "error": err, "email": string(erroneousBody)}) return err } // Check for header indicating this was sent BY the list to itself (common pattern) if thismail.Headers.Get("sent-from-listless") == eng.Config.ListAddress { log15.Info("Received mail with a sent-from-listless header matching own. Ignoring.", log15.Ctx{"context": "imap"}) return nil } log15.Info("Received mail addressed to..", log15.Ctx{"context": "imap", "to": strings.Join(thismail.To, ", ")}) luaMail := WrapEmail(thismail) if luaMail == nil || !luaMail.isValid() { log15.Error("Received email but failed to wrap", log15.Ctx{"context": "imap", "error": ErrEmailInvalid, "email": thismail}) return ErrEmailInvalid } log15.Info("Email about to be processed", log15.Ctx{"context": "imap", "email": luaMail}) ok, err := eng.ProcessMail(luaMail) if err != nil { log15.Error("Error calling ProcessMail handler", log15.Ctx{"context": "lua", "error": err}) return err } if !ok { log15.Debug("No error occurred, but not sending message on instruction from Lua", log15.Ctx{"context": "smtp"}) return nil } // Verify that using the actual sender is OK according to SPF records for // sender Domain, otherwise fall back to list address. newSender := eng.ChooseListSenderEmail(luaMail.Sender) if newSender != luaMail.Sender { log15.Info("Outgoing email sender changed for SPF policy", log15.Ctx{"context": "smtp", "original": luaMail.Sender, "new": newSender}) } luaMail.Email.From = newSender log15.Info("Outgoing email", log15.Ctx{"context": "smtp", "subject": luaMail.Subject}) // Set header to indicate that this was sent by Listless, in case it loops around // somehow (some lists retain the "To: <*****@*****.**>" header unchanged). luaMail.Headers.Set("sent-from-listless", eng.Config.ListAddress) auth := smtp.PlainAuth("", eng.Config.SMTPUsername, eng.Config.SMTPPassword, eng.Config.SMTPHost) //auth := smtp.PlainAuth(eng.Config.SMTPUsername, eng.Config.SMTPUsername, eng.Config.SMTPPassword, eng.Config.SMTPHost) // Patched to allow excluding of variadic emails added after auth. err = luaMail.Send(eng.Config.smtpAddr, auth, eng.Config.ListAddress) if err != nil { log15.Error("Error sending message by SMTP", log15.Ctx{"context": "smtp", "error": err}) return err } log15.Info("Sent message successfully", log15.Ctx{"context": "smtp", "subject": luaMail.Subject}) return nil }
// API_Import_Email allows for the importing of email. // Returns a Message object func API_Import_Email(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) return } ir := struct { Content string `json:"content"` ConvertLinks bool `json:"convert_links"` }{} err := json.NewDecoder(r.Body).Decode(&ir) if err != nil { JSONResponse(w, models.Response{Success: false, Message: "Error decoding JSON Request"}, http.StatusBadRequest) return } e, err := email.NewEmailFromReader(strings.NewReader(ir.Content)) if err != nil { Logger.Println(err) } // If the user wants to convert links to point to // the landing page, let's make it happen by changing up // e.HTML if ir.ConvertLinks { d, err := goquery.NewDocumentFromReader(bytes.NewReader(e.HTML)) if err != nil { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusBadRequest) return } d.Find("a").Each(func(i int, a *goquery.Selection) { a.SetAttr("href", "{{.URL}}") }) h, err := d.Html() if err != nil { JSONResponse(w, models.Response{Success: false, Message: err.Error()}, http.StatusInternalServerError) return } e.HTML = []byte(h) } er := emailResponse{ Subject: e.Subject, Text: string(e.Text), HTML: string(e.HTML), } JSONResponse(w, er, http.StatusOK) return }
// API_Import_Email allows for the importing of email. // Returns a Message object func API_Import_Email(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { JSONResponse(w, models.Response{Success: false, Message: "Method not allowed"}, http.StatusBadRequest) return } defer r.Body.Close() e, err := email.NewEmailFromReader(r.Body) if err != nil { Logger.Println(err) } er := emailResponse{ Subject: e.Subject, Text: string(e.Text), HTML: string(e.HTML), } JSONResponse(w, er, http.StatusOK) return }
func (g *Gmuch) getMessage(m *notmuch.Message) (*model.Message, error) { msg := &model.Message{ ID: m.ID(), ThreadID: m.ThreadID(), Emails: make([]*email.Email, 0, 1), } var fn string fns := m.Filenames() for fns.Next(&fn) { f, err := os.Open(fn) if err != nil { g.logger.Log( "method", "getMessage", "messageID", msg.ID, "operation", "open email file", "path", fn, "error", err, ) return nil, err } defer f.Close() e, err := email.NewEmailFromReader(f) if err != nil { g.logger.Log( "method", "getMessage", "messageID", msg.ID, "operation", "parse email file", "path", fn, "error", err, ) return nil, err } msg.Emails = append(msg.Emails, e) } tags := m.Tags() tag := ¬much.Tag{} for tags.Next(tag) { msg.Tags = append(msg.Tags, tag.String()) } return msg, nil }