1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <package lib import ( "bytes" "encoding/base64" "fmt" "io" "mime/quotedprintable" "strings" "git.sr.ht/~sircmpwn/aerc/models" "github.com/emersion/go-message" _ "github.com/emersion/go-message/charset" "github.com/emersion/go-message/mail" ) func FetchEntityPartReader(e *message.Entity, index []int) (io.Reader, error) { if len(index) < 1 { return nil, fmt.Errorf("no part to read") } if mpr := e.MultipartReader(); mpr != nil { idx := 0 for { idx++ part, err := mpr.NextPart() if err != nil { return nil, err } if idx == index[0] { rest := index[1:] if len(rest) < 1 { return fetchEntityReader(part) } return FetchEntityPartReader(part, index[1:]) } } } if index[0] != 1 { return nil, fmt.Errorf("cannont return non-first part of non-multipart") } return fetchEntityReader(e) } // fetchEntityReader makes an io.Reader for the given entity. Since the // go-message package decodes the body for us, and the UI expects to deal with // a reader whose bytes are encoded with the part's encoding, we are in the // interesting position of needing to re-encode the reader before sending it // off to the UI layer. // // TODO: probably change the UI to expect an already-decoded reader and decode // in the IMAP worker. func fetchEntityReader(e *message.Entity) (io.Reader, error) { enc := e.Header.Get("content-transfer-encoding") var buf bytes.Buffer // base64 if strings.EqualFold(enc, "base64") { wc := base64.NewEncoder(base64.StdEncoding, &buf) defer wc.Close() if _, err := io.Copy(wc, e.Body); err != nil { return nil, fmt.Errorf("could not base64 encode: %v", err) } return &buf, nil } // quoted-printable if strings.EqualFold(enc, "quoted-printable") { wc := quotedprintable.NewWriter(&buf) defer wc.Close() if _, err := io.Copy(wc, e.Body); err != nil { return nil, fmt.Errorf("could not quoted-printable encode: %v", err) } return &buf, nil } // other general encoding if _, err := io.Copy(&buf, e.Body); err != nil { return nil, err } return &buf, nil } // split a MIME type into its major and minor parts func splitMIME(m string) (string, string) { parts := strings.Split(m, "/") if len(parts) != 2 { return parts[0], "" } return parts[0], parts[1] } func parseEntityStructure(e *message.Entity) (*models.BodyStructure, error) { var body models.BodyStructure contentType, ctParams, err := e.Header.ContentType() if err != nil { return nil, fmt.Errorf("could not parse content type: %v", err) } mimeType, mimeSubType := splitMIME(contentType) body.MIMEType = mimeType body.MIMESubType = mimeSubType body.Params = ctParams body.Description = e.Header.Get("content-description") body.Encoding = e.Header.Get("content-transfer-encoding") if cd := e.Header.Get("content-disposition"); cd != "" { contentDisposition, cdParams, err := e.Header.ContentDisposition() if err != nil { return nil, fmt.Errorf("could not parse content disposition: %v", err) } body.Disposition = contentDisposition body.DispositionParams = cdParams } body.Parts = []*models.BodyStructure{} if mpr := e.MultipartReader(); mpr != nil { for { part, err := mpr.NextPart() if err == io.EOF { return &body, nil } else if err != nil { return nil, err } ps, err := parseEntityStructure(part) if err != nil { return nil, fmt.Errorf("could not parse child entity structure: %v", err) } body.Parts = append(body.Parts, ps) } } return &body, nil } func parseEnvelope(h *mail.Header) (*models.Envelope, error) { date, err := h.Date() if err != nil { return nil, fmt.Errorf("could not parse date header: %v", err) } from, err := parseAddressList(h, "from") if err != nil { return nil, fmt.Errorf("could not read from address: %v", err) } to, err := parseAddressList(h, "to") if err != nil { return nil, fmt.Errorf("could not read to address: %v", err) } cc, err := parseAddressList(h, "cc") if err != nil { return nil, fmt.Errorf("could not read cc address: %v", err) } bcc, err := parseAddressList(h, "bcc") if err != nil { return nil, fmt.Errorf("could not read bcc address: %v", err) } replyTo, err := parseAddressList(h, "reply-to") if err != nil { return nil, fmt.Errorf("could not read reply-to address: %v", err) } subj, err := h.Subject() if err != nil { return nil, fmt.Errorf("could not read subject: %v", err) } msgID, err := h.Text("message-id") if err != nil { return nil, fmt.Errorf("could not read message id: %v", err) } return &models.Envelope{ Date: date, Subject: subj, MessageId: msgID, From: from, ReplyTo: replyTo, To: to, Cc: cc, Bcc: bcc, }, nil } func parseAddressList(h *mail.Header, key string) ([]*models.Address, error) { var converted []*models.Address addrs, err := h.AddressList(key) if err != nil { if hdr, err := h.Text(key); err != nil && strings.Contains(hdr, "@") { return []*models.Address{&models.Address{ Name: hdr, }}, nil } return nil, err } for _, addr := range addrs { parts := strings.Split(addr.Address, "@") var mbox, host string if len(parts) > 1 { mbox = strings.Join(parts[0:len(parts)-1], "@") host = parts[len(parts)-1] } else { mbox = addr.Address } converted = append(converted, &models.Address{ Name: addr.Name, Mailbox: mbox, Host: host, }) } return converted, nil } // RawMessage is an interface that describes a raw message type RawMessage interface { NewReader() (io.Reader, error) ModelFlags() ([]models.Flag, error) UID() uint32 } // MessageInfo populates a models.MessageInfo struct for the message. // based on the reader returned by NewReader func MessageInfo(raw RawMessage) (*models.MessageInfo, error) { r, err := raw.NewReader() if err != nil { return nil, err } msg, err := message.Read(r) if err != nil { return nil, fmt.Errorf("could not read message: %v", err) } bs, err := parseEntityStructure(msg) if err != nil { return nil, fmt.Errorf("could not get structure: %v", err) } env, err := parseEnvelope(&mail.Header{msg.Header}) if err != nil { return nil, fmt.Errorf("could not get envelope: %v", err) } flags, err := raw.ModelFlags() if err != nil { return nil, err } return &models.MessageInfo{ BodyStructure: bs, Envelope: env, Flags: flags, InternalDate: env.Date, RFC822Headers: &mail.Header{msg.Header}, Size: 0, Uid: raw.UID(), }, nil }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="theme-color" content="#000000" /> <package lib import ( "bytes" "encoding/base64" "fmt" "io" "mime/quotedprintable" "strings" "git.sr.ht/~sircmpwn/aerc/models" "github.com/emersion/go-message" _ "github.com/emersion/go-message/charset" "github.com/emersion/go-message/mail" ) func FetchEntityPartReader(e *message.Entity, index []int) (io.Reader, error) { if len(index) < 1 { return nil, fmt.Errorf("no part to read") } if mpr := e.MultipartReader(); mpr != nil { idx := 0 for { idx++ part, err := mpr.NextPart() if err != nil { return nil, err } if idx == index[0] { rest := index[1:] if len(rest) < 1 { return fetchEntityReader(part) } return FetchEntityPartReader(part, index[1:]) } } } if index[0] != 1 { return nil, fmt.Errorf("cannont return non-first part of non-multipart") } return fetchEntityReader(e) } // fetchEntityReader makes an io.Reader for the given entity. Since the // go-message package decodes the body for us, and the UI expects to deal with // a reader whose bytes are encoded with the part's encoding, we are in the // interesting position of needing to re-encode the reader before sending it // off to the UI layer. // // TODO: probably change the UI to expect an already-decoded reader and decode // in the IMAP worker. func fetchEntityReader(e *message.Entity) (io.Reader, error) { enc := e.Header.Get("content-transfer-encoding") var buf bytes.Buffer // base64 if strings.EqualFold(enc, "base64") { wc := base64.NewEncoder(base64.StdEncoding, &buf) defer wc.Close() if _, err := io.Copy(wc, e.Body); err != nil { return nil, fmt.Errorf("could not base64 encode: %v", err) } return &buf, nil } // quoted-printable if strings.EqualFold(enc, "quoted-printable") { wc := quotedprintable.NewWriter(&buf) defer wc.Close() if _, err := io.Copy(wc, e.Body); err != nil { return nil, fmt.Errorf("could not quoted-printable encode: %v", err) } return &buf, nil } // other general encoding if _, err := io.Copy(&buf, e.Body); err != nil { return nil, err } return &buf, nil } // split a MIME type into its major and minor parts func splitMIME(m string) (string, string) { parts := strings.Split(m, "/") if len(parts) != 2 { return parts[0], "" } return parts[0], parts[1] } func parseEntityStructure(e *message.Entity) (*models.BodyStructure, error) { var body models.BodyStructure contentType, ctParams, err := e.Header.ContentType() if err != nil { return nil, fmt.Errorf("could not parse content type: %v", err) } mimeType, mimeSubType := splitMIME(contentType) body.MIMEType = mimeType body.MIMESubType = mimeSubType body.Params = ctParams body.Description = e.Header.Get("content-description") body.Encoding = e.Header.Get("content-transfer-encoding") if cd := e.Header.Get("content-disposition"); cd != "" { contentDisposition, cdParams, err := e.Header.ContentDisposition() if err != nil { return nil, fmt.Errorf("could not parse content disposition: %v", err) } body.Disposition = contentDisposition body.DispositionParams = cdParams } body.Parts = []*models.BodyStructure{} if mpr := e.MultipartReader(); mpr != nil { for { part, err := mpr.NextPart() if err == io.EOF { return &body, nil } else if err != nil { return nil, err } ps, err := parseEntityStructure(part) if err != nil { return nil, fmt.Errorf("could not parse child entity structure: %v", err) } body.Parts = append(body.Parts, ps) } } return &body, nil } func parseEnvelope(h *mail.Header) (*models.Envelope, error) { date, err := h.Date() if err != nil { return nil, fmt.Errorf("could not parse date header: %v", err) } from, err := parseAddressList(h, "from") if err != nil { return nil, fmt.Errorf("could not read from address: %v", err) } to, err := parseAddressList(h, "to") if err != nil { return nil, fmt.Errorf("could not read to address: %v", err) } cc, err := parseAddressList(h, "cc") if err != nil { return nil, fmt.Errorf("could not read cc address: %v", err) } bcc, err := parseAddressList(h, "bcc") if err != nil { return nil, fmt.Errorf("could not read bcc address: %v", err) } replyTo, err := parseAddressList(h, "reply-to") if err != nil { return nil, fmt.Errorf("could not read reply-to address: %v", err) } subj, err := h.Subject() if err != nil { return nil, fmt.Errorf("could not read subject: %v", err) } msgID, err := h.Text("message-id") if err != nil { return nil, fmt.Errorf("could not read message id: %v", err) } return &models.Envelope{ Date: date, Subject: subj, MessageId: msgID, From: from, ReplyTo: replyTo, To: to, Cc: cc, Bcc: bcc, }, nil } func parseAddressList(h *mail.Header, key string) ([]*models.Address, error) { var converted []*models.Address addrs, err := h.AddressList(key) if err != nil { if hdr, err := h.Text(key); err != nil && strings.Contains(hdr, "@") { return []*models.Address{&models.Address{ Name: hdr, }}, nil } return nil, err } for _, addr := range addrs { parts := strings.Split(addr.Address, "@") var mbox, host string if len(parts) > 1 { mbox = strings.Join(parts[0:len(parts)-1], "@") host = parts[len(parts)-1] } else { mbox = addr.Address } converted = append(converted, &models.Address{ Name: addr.Name, Mailbox: mbox, Host: host, }) } return converted, nil } // RawMessage is an interface that describes a raw message type RawMessage interface { NewReader() (io.Reader, error) ModelFlags() ([]models.Flag, error) UID() uint32 } // MessageInfo populates a models.MessageInfo struct for the message. // based on the reader returned by NewReader func MessageInfo(raw RawMessage) (*models.MessageInfo, error) { r, err := raw.NewReader() if err != nil { return nil, err } msg, err := message.Read(r) if err != nil { return nil, fmt.Errorf("could not read message: %v", err) } bs, err := parseEntityStructure(msg) if err != nil { return nil, fmt.Errorf("could not get structure: %v", err) } env, err := parseEnvelope(&mail.Header{msg.Header}) if err != nil { return nil, fmt.Errorf("could not get envelope: %v", err) } flags, err := raw.ModelFlags() if err != nil { return nil, err } return &models.MessageInfo{ BodyStructure: bs, Envelope: env, Flags: flags, InternalDate: env.Date, RFC822Headers: &mail.Header{msg.Header}, Size: 0, Uid: raw.UID(), }, nil }
package lib import ( "bytes" "encoding/base64" "fmt" "io" "mime/quotedprintable" "strings" "git.sr.ht/~sircmpwn/aerc/models" "github.com/emersion/go-message" _ "github.com/emersion/go-message/charset" "github.com/emersion/go-message/mail" ) func FetchEntityPartReader(e *message.Entity, index []int) (io.Reader, error) { if len(index) < 1 { return nil, fmt.Errorf("no part to read") } if mpr := e.MultipartReader(); mpr != nil { idx := 0 for { idx++ part, err := mpr.NextPart() if err != nil { return nil, err } if idx == index[0] { rest := index[1:] if len(rest) < 1 { return fetchEntityReader(part) } return FetchEntityPartReader(part, index[1:]) } } } if index[0] != 1 { return nil, fmt.Errorf("cannont return non-first part of non-multipart") } return fetchEntityReader(e) } // fetchEntityReader makes an io.Reader for the given entity. Since the // go-message package decodes the body for us, and the UI expects to deal with // a reader whose bytes are encoded with the part's encoding, we are in the // interesting position of needing to re-encode the reader before sending it // off to the UI layer. // // TODO: probably change the UI to expect an already-decoded reader and decode // in the IMAP worker. func fetchEntityReader(e *message.Entity) (io.Reader, error) { enc := e.Header.Get("content-transfer-encoding") var buf bytes.Buffer // base64 if strings.EqualFold(enc, "base64") { wc := base64.NewEncoder(base64.StdEncoding, &buf) defer wc.Close() if _, err := io.Copy(wc, e.Body); err != nil { return nil, fmt.Errorf("could not base64 encode: %v", err) } return &buf, nil } // quoted-printable if strings.EqualFold(enc, "quoted-printable") { wc := quotedprintable.NewWriter(&buf) defer wc.Close() if _, err := io.Copy(wc, e.Body); err != nil { return nil, fmt.Errorf("could not quoted-printable encode: %v", err) } return &buf, nil } // other general encoding if _, err := io.Copy(&buf, e.Body); err != nil { return nil, err } return &buf, nil } // split a MIME type into its major and minor parts func splitMIME(m string) (string, string) { parts := strings.Split(m, "/") if len(parts) != 2 { return parts[0], "" } return parts[0], parts[1] } func parseEntityStructure(e *message.Entity) (*models.BodyStructure, error) { var body models.BodyStructure contentType, ctParams, err := e.Header.ContentType() if err != nil { return nil, fmt.Errorf("could not parse content type: %v", err) } mimeType, mimeSubType := splitMIME(contentType) body.MIMEType = mimeType body.MIMESubType = mimeSubType body.Params = ctParams body.Description = e.Header.Get("content-description") body.Encoding = e.Header.Get("content-transfer-encoding") if cd := e.Header.Get("content-disposition"); cd != "" { contentDisposition, cdParams, err := e.Header.ContentDisposition() if err != nil { return nil, fmt.Errorf("could not parse content disposition: %v", err) } body.Disposition = contentDisposition body.DispositionParams = cdParams } body.Parts = []*models.BodyStructure{} if mpr := e.MultipartReader(); mpr != nil { for { part, err := mpr.NextPart() if err == io.EOF { return &body, nil } else if err != nil { return nil, err } ps, err := parseEntityStructure(part) if err != nil { return nil, fmt.Errorf("could not parse child entity structure: %v", err) } body.Parts = append(body.Parts, ps) } } return &body, nil } func parseEnvelope(h *mail.Header) (*models.Envelope, error) { date, err := h.Date() if err != nil { return nil, fmt.Errorf("could not parse date header: %v", err) } from, err := parseAddressList(h, "from") if err != nil { return nil, fmt.Errorf("could not read from address: %v", err) } to, err := parseAddressList(h, "to") if err != nil { return nil, fmt.Errorf("could not read to address: %v", err) } cc, err := parseAddressList(h, "cc") if err != nil { return nil, fmt.Errorf("could not read cc address: %v", err) } bcc, err := parseAddressList(h, "bcc") if err != nil { return nil, fmt.Errorf("could not read bcc address: %v", err) } replyTo, err := parseAddressList(h, "reply-to") if err != nil { return nil, fmt.Errorf("could not read reply-to address: %v", err) } subj, err := h.Subject() if err != nil { return nil, fmt.Errorf("could not read subject: %v", err) } msgID, err := h.Text("message-id") if err != nil { return nil, fmt.Errorf("could not read message id: %v", err) } return &models.Envelope{ Date: date, Subject: subj, MessageId: msgID, From: from, ReplyTo: replyTo, To: to, Cc: cc, Bcc: bcc, }, nil } func parseAddressList(h *mail.Header, key string) ([]*models.Address, error) { var converted []*models.Address addrs, err := h.AddressList(key) if err != nil { if hdr, err := h.Text(key); err != nil && strings.Contains(hdr, "@") { return []*models.Address{&models.Address{ Name: hdr, }}, nil } return nil, err } for _, addr := range addrs { parts := strings.Split(addr.Address, "@") var mbox, host string if len(parts) > 1 { mbox = strings.Join(parts[0:len(parts)-1], "@") host = parts[len(parts)-1] } else { mbox = addr.Address } converted = append(converted, &models.Address{ Name: addr.Name, Mailbox: mbox, Host: host, }) } return converted, nil } // RawMessage is an interface that describes a raw message type RawMessage interface { NewReader() (io.Reader, error) ModelFlags() ([]models.Flag, error) UID() uint32 } // MessageInfo populates a models.MessageInfo struct for the message. // based on the reader returned by NewReader func MessageInfo(raw RawMessage) (*models.MessageInfo, error) { r, err := raw.NewReader() if err != nil { return nil, err } msg, err := message.Read(r) if err != nil { return nil, fmt.Errorf("could not read message: %v", err) } bs, err := parseEntityStructure(msg) if err != nil { return nil, fmt.Errorf("could not get structure: %v", err) } env, err := parseEnvelope(&mail.Header{msg.Header}) if err != nil { return nil, fmt.Errorf("could not get envelope: %v", err) } flags, err := raw.ModelFlags() if err != nil { return nil, err } return &models.MessageInfo{ BodyStructure: bs, Envelope: env, Flags: flags, InternalDate: env.Date, RFC822Headers: &mail.Header{msg.Header}, Size: 0, Uid: raw.UID(), }, nil }