func getThreads(c *imap.Client) ([]Thread, error) { set, err := imap.NewSeqSet("1:*") cmd, err := imap.Wait(c.Fetch(set, "X-GM-THRID", "UID")) if err != nil { fmt.Println(err) return nil, ErrBadConnection } var result []Thread seen := make(map[string]int) for _, rsp := range cmd.Data { thrid := imap.AsString(rsp.MessageInfo().Attrs["X-GM-THRID"]) uid := imap.AsNumber(rsp.MessageInfo().Attrs["UID"]) if i, ok := seen[thrid]; ok { result[i] = append(result[i], uid) } else { result = append(result, Thread{uid}) seen[thrid] = len(result) - 1 } } return result, nil }
func fetch(c *imap.Client, user string, thread Thread) ([]*ParsedMail, error) { var set imap.SeqSet for _, uid := range thread { set.AddNum(uid) } cmd, err := imap.Wait(c.UIDFetch(&set, "BODY[]", "X-GM-MSGID", "X-GM-THRID")) if err != nil { return nil, ErrBadConnection } parsed := make([]*ParsedMail, len(cmd.Data)) for i, rsp := range cmd.Data { p, err := parseMail(imap.AsBytes(rsp.MessageInfo().Attrs["BODY[]"]), user) if err != nil { return nil, err } link, err := gmailLink(imap.AsString(rsp.MessageInfo().Attrs["X-GM-MSGID"])) if err != nil { return nil, err } p.GmailLink = link p.Thrid = imap.AsString(rsp.MessageInfo().Attrs["X-GM-THRID"]) parsed[i] = p } return parsed, nil }
func parseContent(mi *imap.MessageInfo, mimeHeader PMIMEHeader) (Content, error) { // 1) If no content is given, error and return if nil == mi { return nil, errors.New("[watney] ERROR: Couldn't parse mail content due to missing content.") } var ( content string = imap.AsString(mi.Attrs["RFC822.TEXT"]) parts Content = make(Content, 1) ) // 2) Simple Case: We have no MIME protocol, simply assume the content is plain text if 0 == mimeHeader.MimeVersion { parts["text/plain"] = ContentPart{ Encoding: "quoted-printable", Charset: "UTF-8", Body: content, } return parts, nil } // 3) Otherwise, we have to check the Content-Type: If its NOT a multipart, just add it as is if !strings.Contains(mimeHeader.ContentType, "multipart") { parts[mimeHeader.ContentType] = ContentPart{ Encoding: mimeHeader.Encoding, Charset: "UTF-8", Body: content, } return parts, nil } // 4) Otherwise, in case we have a multipart Content-Type, parse all parts return parseMultipartContent(content, mimeHeader.MultipartBoundary) }
// newEmailMessage will parse an imap.FieldMap into an map[string]interface{}. This // will expect the message to container the internaldate and the body with // all headers included. func newEmailMessage(msgFields imap.FieldMap) (map[string]interface{}, error) { var email map[string]interface{} // parse the header rawHeader := imap.AsBytes(msgFields["RFC822.HEADER"]) msg, err := mail.ReadMessage(bytes.NewReader(rawHeader)) if err != nil { return email, err } email = map[string]interface{}{ "internal_date": imap.AsDateTime(msgFields["INTERNALDATE"]), "body": imap.AsString(msgFields["BODY[]"]), "from": msg.Header.Get("From"), "to": msg.Header.Get("To"), "subject": msg.Header.Get("Subject"), } return email, nil }
func parseHeader(mi *imap.MessageInfo) (*Header, error) { // 1) If no MessageInfo was passed => return and error if nil == mi { return nil, errors.New("Couldn't parse Mail Header, because the given MessageInfo object is nil") } // 2) If the given MessageInfo doesn't contain a header string => return and error var ( mailHeader imap.Field curHeader *Header err error ) if mailHeader = mi.Attrs["RFC822.HEADER"]; nil == mailHeader { return nil, errors.New("Couldn't parse Mail Header, because no header was provided " + "in the given MessageInfo object") } if curHeader, err = parseHeaderStr(imap.AsString(mailHeader)); err == nil { curHeader.Size = mi.Size } return curHeader, err }