diff --git a/apidiff/next.txt b/apidiff/next.txt index e69de29..84f6fe9 100644 --- a/apidiff/next.txt +++ b/apidiff/next.txt @@ -0,0 +1,7 @@ +Below are the incompatible changes between v0.0.14 and next, per package. + +# message +- Part.ContentDescription: changed from string to *string +- Part.ContentID: changed from string to *string +- Part.ContentTransferEncoding: changed from string to *string + diff --git a/dsn/dsn_test.go b/dsn/dsn_test.go index 9fab9b8..ea6f3c2 100644 --- a/dsn/dsn_test.go +++ b/dsn/dsn_test.go @@ -50,8 +50,8 @@ func tcheckType(t *testing.T, p *message.Part, mt, mst, cte string) { if !strings.EqualFold(p.MediaSubType, mst) { t.Fatalf("got mediasubtype %q, expected %q", p.MediaSubType, mst) } - if !strings.EqualFold(p.ContentTransferEncoding, cte) { - t.Fatalf("got content-transfer-encoding %q, expected %q", p.ContentTransferEncoding, cte) + if !(cte == "" && p.ContentTransferEncoding == nil || cte != "" && p.ContentTransferEncoding != nil && strings.EqualFold(cte, *p.ContentTransferEncoding)) { + t.Fatalf("got content-transfer-encoding %v, expected %v", p.ContentTransferEncoding, cte) } } diff --git a/imapserver/fetch.go b/imapserver/fetch.go index feb1545..3042cf5 100644 --- a/imapserver/fetch.go +++ b/imapserver/fetch.go @@ -776,11 +776,15 @@ func (cmd *fetchCmd) xbinary(a fetchAtt) (string, token) { cmd.xerrorf("binary only allowed on leaf parts, not multipart/* or message/rfc822 or message/global") } - switch p.ContentTransferEncoding { + var cte string + if p.ContentTransferEncoding != nil { + cte = *p.ContentTransferEncoding + } + switch cte { case "", "7BIT", "8BIT", "BINARY", "BASE64", "QUOTED-PRINTABLE": default: // ../rfc/9051:5913 - xusercodeErrorf("UNKNOWN-CTE", "unknown Content-Transfer-Encoding %q", p.ContentTransferEncoding) + xusercodeErrorf("UNKNOWN-CTE", "unknown Content-Transfer-Encoding %q", cte) } r := p.Reader() @@ -992,7 +996,11 @@ func bodyFldParams(p *message.Part) token { return params } -func bodyFldEnc(s string) token { +func bodyFldEnc(cte *string) token { + var s string + if cte != nil { + s = *cte + } up := strings.ToUpper(s) switch up { case "7BIT", "8BIT", "BINARY", "BASE64", "QUOTED-PRINTABLE": @@ -1002,14 +1010,14 @@ func bodyFldEnc(s string) token { } func bodyFldMd5(p *message.Part) token { - if p.ContentMD5 == "" { + if p.ContentMD5 == nil { return nilt } - return string0(p.ContentMD5) + return string0(*p.ContentMD5) } func bodyFldDisp(log mlog.Log, p *message.Part) token { - if p.ContentDisposition == "" { + if p.ContentDisposition == nil { return nilt } @@ -1019,12 +1027,12 @@ func bodyFldDisp(log mlog.Log, p *message.Part) token { // And decodes character sets and removes language tags, like // "title*0*=us-ascii'en'hello%20world. ../rfc/2231:210 - disp, params, err := mime.ParseMediaType(p.ContentDisposition) + disp, params, err := mime.ParseMediaType(*p.ContentDisposition) if err != nil { - log.Debugx("parsing content-disposition, ignoring", err, slog.String("header", p.ContentDisposition)) + log.Debugx("parsing content-disposition, ignoring", err, slog.String("header", *p.ContentDisposition)) return nilt } else if len(params) == 0 { - log.Debug("content-disposition has no parameters, ignoring", slog.String("header", p.ContentDisposition)) + log.Debug("content-disposition has no parameters, ignoring", slog.String("header", *p.ContentDisposition)) return nilt } var fields listspace @@ -1036,14 +1044,14 @@ func bodyFldDisp(log mlog.Log, p *message.Part) token { func bodyFldLang(p *message.Part) token { // todo: ../rfc/3282:86 ../rfc/5646:218 we currently just split on comma and trim space, should properly parse header. - if p.ContentLanguage == "" { + if p.ContentLanguage == nil { return nilt } var l listspace - for _, s := range strings.Split(p.ContentLanguage, ",") { + for _, s := range strings.Split(*p.ContentLanguage, ",") { s = strings.TrimSpace(s) if s == "" { - return string0(p.ContentLanguage) + return string0(*p.ContentLanguage) } l = append(l, string0(s)) } @@ -1051,10 +1059,10 @@ func bodyFldLang(p *message.Part) token { } func bodyFldLoc(p *message.Part) token { - if p.ContentLocation == "" { + if p.ContentLocation == nil { return nilt } - return string0(p.ContentLocation) + return string0(*p.ContentLocation) } // xbodystructure returns a "body". diff --git a/imapserver/pack.go b/imapserver/pack.go index f6b934b..e68c415 100644 --- a/imapserver/pack.go +++ b/imapserver/pack.go @@ -34,11 +34,11 @@ func (t niltoken) xwriteTo(c *conn, xw io.Writer) { xw.Write([]byte(t.pack(c))) } -func nilOrString(s string) token { - if s == "" { +func nilOrString(s *string) token { + if s == nil { return nilt } - return string0(s) + return string0(*s) } type string0 string diff --git a/message/part.go b/message/part.go index f05920b..3b586c1 100644 --- a/message/part.go +++ b/message/part.go @@ -65,14 +65,14 @@ type Part struct { MediaType string // From Content-Type, upper case. E.g. "TEXT". Can be empty because content-type may be absent. In this case, the part may be treated as TEXT/PLAIN. MediaSubType string // From Content-Type, upper case. E.g. "PLAIN". ContentTypeParams map[string]string // E.g. holds "boundary" for multipart messages. Has lower-case keys, and original case values. - ContentID string - ContentDescription string - ContentTransferEncoding string // In upper case. - ContentDisposition string - ContentMD5 string - ContentLanguage string - ContentLocation string - Envelope *Envelope // Email message headers. Not for non-message parts. + ContentID *string `json:",omitempty"` + ContentDescription *string `json:",omitempty"` + ContentTransferEncoding *string `json:",omitempty"` // In upper case. + ContentDisposition *string `json:",omitempty"` + ContentMD5 *string `json:",omitempty"` + ContentLanguage *string `json:",omitempty"` + ContentLocation *string `json:",omitempty"` + Envelope *Envelope `json:",omitempty"` // Email message headers. Not for non-message parts. Parts []Part // Parts if this is a multipart. @@ -362,13 +362,18 @@ func newPart(log mlog.Log, strict bool, r io.ReaderAt, offset int64, parent *Par } } - p.ContentID = p.header.Get("Content-Id") - p.ContentDescription = p.header.Get("Content-Description") - p.ContentTransferEncoding = strings.ToUpper(p.header.Get("Content-Transfer-Encoding")) - p.ContentDisposition = p.header.Get("Content-Disposition") - p.ContentMD5 = p.header.Get("Content-Md5") - p.ContentLanguage = p.header.Get("Content-Language") - p.ContentLocation = p.header.Get("Content-Location") + p.ContentID = p.headerGet("Content-Id") + p.ContentDescription = p.headerGet("Content-Description") + cte := p.headerGet("Content-Transfer-Encoding") + if cte != nil { + s := strings.ToUpper(*cte) + cte = &s + } + p.ContentTransferEncoding = cte + p.ContentDisposition = p.headerGet("Content-Disposition") + p.ContentMD5 = p.headerGet("Content-Md5") + p.ContentLanguage = p.headerGet("Content-Language") + p.ContentLocation = p.headerGet("Content-Location") if parent == nil { p.Envelope, err = parseEnvelope(log, mail.Header(p.header)) @@ -424,6 +429,15 @@ func (p *Part) Header() (textproto.MIMEHeader, error) { return h, err } +func (p *Part) headerGet(k string) *string { + l := p.header.Values(k) + if len(l) == 0 { + return nil + } + s := l[0] + return &s +} + // HeaderReader returns a reader for the header section of this part, including ending bare CRLF. func (p *Part) HeaderReader() io.Reader { return io.NewSectionReader(p.r, p.HeaderOffset, p.BodyOffset-p.HeaderOffset) @@ -657,17 +671,10 @@ var ErrParamEncoding = errors.New("bad header parameter encoding") // and a filename may still be returned. func (p *Part) DispositionFilename() (disposition string, filename string, err error) { cd := p.ContentDisposition - if cd == "" { - h, err := p.Header() - if err != nil { - return "", "", fmt.Errorf("parsing header: %w", err) - } - cd = h.Get("Content-Disposition") - } var disp string var params map[string]string - if cd != "" { - disp, params, err = mime.ParseMediaType(cd) + if cd != nil && *cd != "" { + disp, params, err = mime.ParseMediaType(*cd) } if err != nil { return "", "", fmt.Errorf("%w: parsing disposition header: %v", ErrParamEncoding, err) @@ -786,9 +793,13 @@ func (tr *textReader) Read(buf []byte) (int, error) { return o, nil } -func newDecoder(cte string, r io.Reader) io.Reader { +func newDecoder(cte *string, r io.Reader) io.Reader { + var s string + if cte != nil { + s = *cte + } // ../rfc/2045:775 - switch cte { + switch s { case "BASE64": return base64.NewDecoder(base64.StdEncoding, r) case "QUOTED-PRINTABLE": diff --git a/queue/hook.go b/queue/hook.go index 1a9d526..43a5116 100644 --- a/queue/hook.go +++ b/queue/hook.go @@ -929,10 +929,14 @@ func PartStructure(log mlog.Log, p *message.Part) (webhook.Structure, error) { } else if err != nil { return webhook.Structure{}, err } + var cid string + if p.ContentID != nil { + cid = *p.ContentID + } s := webhook.Structure{ ContentType: strings.ToLower(p.MediaType + "/" + p.MediaSubType), ContentTypeParams: p.ContentTypeParams, - ContentID: p.ContentID, + ContentID: cid, ContentDisposition: strings.ToLower(disp), Filename: filename, DecodedSize: p.DecodedSize, diff --git a/webmail/api.json b/webmail/api.json index e20e724..b305f1c 100644 --- a/webmail/api.json +++ b/webmail/api.json @@ -1151,6 +1151,7 @@ "Name": "ContentID", "Docs": "", "Typewords": [ + "nullable", "string" ] }, @@ -1158,6 +1159,7 @@ "Name": "ContentDescription", "Docs": "", "Typewords": [ + "nullable", "string" ] }, @@ -1165,6 +1167,7 @@ "Name": "ContentTransferEncoding", "Docs": "In upper case.", "Typewords": [ + "nullable", "string" ] }, @@ -1172,6 +1175,7 @@ "Name": "ContentDisposition", "Docs": "", "Typewords": [ + "nullable", "string" ] }, @@ -1179,6 +1183,7 @@ "Name": "ContentMD5", "Docs": "", "Typewords": [ + "nullable", "string" ] }, @@ -1186,6 +1191,7 @@ "Name": "ContentLanguage", "Docs": "", "Typewords": [ + "nullable", "string" ] }, @@ -1193,6 +1199,7 @@ "Name": "ContentLocation", "Docs": "", "Typewords": [ + "nullable", "string" ] }, diff --git a/webmail/api.ts b/webmail/api.ts index 82f5958..201e18e 100644 --- a/webmail/api.ts +++ b/webmail/api.ts @@ -84,13 +84,13 @@ export interface Part { MediaType: string // From Content-Type, upper case. E.g. "TEXT". Can be empty because content-type may be absent. In this case, the part may be treated as TEXT/PLAIN. MediaSubType: string // From Content-Type, upper case. E.g. "PLAIN". ContentTypeParams?: { [key: string]: string } // E.g. holds "boundary" for multipart messages. Has lower-case keys, and original case values. - ContentID: string - ContentDescription: string - ContentTransferEncoding: string // In upper case. - ContentDisposition: string - ContentMD5: string - ContentLanguage: string - ContentLocation: string + ContentID?: string | null + ContentDescription?: string | null + ContentTransferEncoding?: string | null // In upper case. + ContentDisposition?: string | null + ContentMD5?: string | null + ContentLanguage?: string | null + ContentLocation?: string | null Envelope?: Envelope | null // Email message headers. Not for non-message parts. Parts?: Part[] | null // Parts if this is a multipart. Message?: Part | null // Only for message/rfc822 and message/global. This part may have a buffer as backing io.ReaderAt, because a message/global can have a non-identity content-transfer-encoding. This part has a nil parent. @@ -617,7 +617,7 @@ export const types: TypenameMap = { "NotFilter": {"Name":"NotFilter","Docs":"","Fields":[{"Name":"Words","Docs":"","Typewords":["[]","string"]},{"Name":"From","Docs":"","Typewords":["[]","string"]},{"Name":"To","Docs":"","Typewords":["[]","string"]},{"Name":"Subject","Docs":"","Typewords":["[]","string"]},{"Name":"Attachments","Docs":"","Typewords":["AttachmentType"]},{"Name":"Labels","Docs":"","Typewords":["[]","string"]}]}, "Page": {"Name":"Page","Docs":"","Fields":[{"Name":"AnchorMessageID","Docs":"","Typewords":["int64"]},{"Name":"Count","Docs":"","Typewords":["int32"]},{"Name":"DestMessageID","Docs":"","Typewords":["int64"]}]}, "ParsedMessage": {"Name":"ParsedMessage","Docs":"","Fields":[{"Name":"ID","Docs":"","Typewords":["int64"]},{"Name":"Part","Docs":"","Typewords":["Part"]},{"Name":"Headers","Docs":"","Typewords":["{}","[]","string"]},{"Name":"ViewMode","Docs":"","Typewords":["ViewMode"]},{"Name":"Texts","Docs":"","Typewords":["[]","string"]},{"Name":"HasHTML","Docs":"","Typewords":["bool"]},{"Name":"ListReplyAddress","Docs":"","Typewords":["nullable","MessageAddress"]},{"Name":"TextPaths","Docs":"","Typewords":["[]","[]","int32"]},{"Name":"HTMLPath","Docs":"","Typewords":["[]","int32"]}]}, - "Part": {"Name":"Part","Docs":"","Fields":[{"Name":"BoundaryOffset","Docs":"","Typewords":["int64"]},{"Name":"HeaderOffset","Docs":"","Typewords":["int64"]},{"Name":"BodyOffset","Docs":"","Typewords":["int64"]},{"Name":"EndOffset","Docs":"","Typewords":["int64"]},{"Name":"RawLineCount","Docs":"","Typewords":["int64"]},{"Name":"DecodedSize","Docs":"","Typewords":["int64"]},{"Name":"MediaType","Docs":"","Typewords":["string"]},{"Name":"MediaSubType","Docs":"","Typewords":["string"]},{"Name":"ContentTypeParams","Docs":"","Typewords":["{}","string"]},{"Name":"ContentID","Docs":"","Typewords":["string"]},{"Name":"ContentDescription","Docs":"","Typewords":["string"]},{"Name":"ContentTransferEncoding","Docs":"","Typewords":["string"]},{"Name":"ContentDisposition","Docs":"","Typewords":["string"]},{"Name":"ContentMD5","Docs":"","Typewords":["string"]},{"Name":"ContentLanguage","Docs":"","Typewords":["string"]},{"Name":"ContentLocation","Docs":"","Typewords":["string"]},{"Name":"Envelope","Docs":"","Typewords":["nullable","Envelope"]},{"Name":"Parts","Docs":"","Typewords":["[]","Part"]},{"Name":"Message","Docs":"","Typewords":["nullable","Part"]}]}, + "Part": {"Name":"Part","Docs":"","Fields":[{"Name":"BoundaryOffset","Docs":"","Typewords":["int64"]},{"Name":"HeaderOffset","Docs":"","Typewords":["int64"]},{"Name":"BodyOffset","Docs":"","Typewords":["int64"]},{"Name":"EndOffset","Docs":"","Typewords":["int64"]},{"Name":"RawLineCount","Docs":"","Typewords":["int64"]},{"Name":"DecodedSize","Docs":"","Typewords":["int64"]},{"Name":"MediaType","Docs":"","Typewords":["string"]},{"Name":"MediaSubType","Docs":"","Typewords":["string"]},{"Name":"ContentTypeParams","Docs":"","Typewords":["{}","string"]},{"Name":"ContentID","Docs":"","Typewords":["nullable","string"]},{"Name":"ContentDescription","Docs":"","Typewords":["nullable","string"]},{"Name":"ContentTransferEncoding","Docs":"","Typewords":["nullable","string"]},{"Name":"ContentDisposition","Docs":"","Typewords":["nullable","string"]},{"Name":"ContentMD5","Docs":"","Typewords":["nullable","string"]},{"Name":"ContentLanguage","Docs":"","Typewords":["nullable","string"]},{"Name":"ContentLocation","Docs":"","Typewords":["nullable","string"]},{"Name":"Envelope","Docs":"","Typewords":["nullable","Envelope"]},{"Name":"Parts","Docs":"","Typewords":["[]","Part"]},{"Name":"Message","Docs":"","Typewords":["nullable","Part"]}]}, "Envelope": {"Name":"Envelope","Docs":"","Fields":[{"Name":"Date","Docs":"","Typewords":["timestamp"]},{"Name":"Subject","Docs":"","Typewords":["string"]},{"Name":"From","Docs":"","Typewords":["[]","Address"]},{"Name":"Sender","Docs":"","Typewords":["[]","Address"]},{"Name":"ReplyTo","Docs":"","Typewords":["[]","Address"]},{"Name":"To","Docs":"","Typewords":["[]","Address"]},{"Name":"CC","Docs":"","Typewords":["[]","Address"]},{"Name":"BCC","Docs":"","Typewords":["[]","Address"]},{"Name":"InReplyTo","Docs":"","Typewords":["string"]},{"Name":"MessageID","Docs":"","Typewords":["string"]}]}, "Address": {"Name":"Address","Docs":"","Fields":[{"Name":"Name","Docs":"","Typewords":["string"]},{"Name":"User","Docs":"","Typewords":["string"]},{"Name":"Host","Docs":"","Typewords":["string"]}]}, "MessageAddress": {"Name":"MessageAddress","Docs":"","Fields":[{"Name":"Name","Docs":"","Typewords":["string"]},{"Name":"User","Docs":"","Typewords":["string"]},{"Name":"Domain","Docs":"","Typewords":["Domain"]}]}, diff --git a/webmail/msg.js b/webmail/msg.js index 241b3b0..d28e6e2 100644 --- a/webmail/msg.js +++ b/webmail/msg.js @@ -299,7 +299,7 @@ var api; "NotFilter": { "Name": "NotFilter", "Docs": "", "Fields": [{ "Name": "Words", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "From", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Subject", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Attachments", "Docs": "", "Typewords": ["AttachmentType"] }, { "Name": "Labels", "Docs": "", "Typewords": ["[]", "string"] }] }, "Page": { "Name": "Page", "Docs": "", "Fields": [{ "Name": "AnchorMessageID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Count", "Docs": "", "Typewords": ["int32"] }, { "Name": "DestMessageID", "Docs": "", "Typewords": ["int64"] }] }, "ParsedMessage": { "Name": "ParsedMessage", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Part", "Docs": "", "Typewords": ["Part"] }, { "Name": "Headers", "Docs": "", "Typewords": ["{}", "[]", "string"] }, { "Name": "ViewMode", "Docs": "", "Typewords": ["ViewMode"] }, { "Name": "Texts", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "HasHTML", "Docs": "", "Typewords": ["bool"] }, { "Name": "ListReplyAddress", "Docs": "", "Typewords": ["nullable", "MessageAddress"] }, { "Name": "TextPaths", "Docs": "", "Typewords": ["[]", "[]", "int32"] }, { "Name": "HTMLPath", "Docs": "", "Typewords": ["[]", "int32"] }] }, - "Part": { "Name": "Part", "Docs": "", "Fields": [{ "Name": "BoundaryOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "HeaderOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "BodyOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "EndOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "RawLineCount", "Docs": "", "Typewords": ["int64"] }, { "Name": "DecodedSize", "Docs": "", "Typewords": ["int64"] }, { "Name": "MediaType", "Docs": "", "Typewords": ["string"] }, { "Name": "MediaSubType", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTypeParams", "Docs": "", "Typewords": ["{}", "string"] }, { "Name": "ContentID", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentDescription", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTransferEncoding", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentDisposition", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentMD5", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentLanguage", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentLocation", "Docs": "", "Typewords": ["string"] }, { "Name": "Envelope", "Docs": "", "Typewords": ["nullable", "Envelope"] }, { "Name": "Parts", "Docs": "", "Typewords": ["[]", "Part"] }, { "Name": "Message", "Docs": "", "Typewords": ["nullable", "Part"] }] }, + "Part": { "Name": "Part", "Docs": "", "Fields": [{ "Name": "BoundaryOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "HeaderOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "BodyOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "EndOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "RawLineCount", "Docs": "", "Typewords": ["int64"] }, { "Name": "DecodedSize", "Docs": "", "Typewords": ["int64"] }, { "Name": "MediaType", "Docs": "", "Typewords": ["string"] }, { "Name": "MediaSubType", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTypeParams", "Docs": "", "Typewords": ["{}", "string"] }, { "Name": "ContentID", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentDescription", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentTransferEncoding", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentDisposition", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentMD5", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentLanguage", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentLocation", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "Envelope", "Docs": "", "Typewords": ["nullable", "Envelope"] }, { "Name": "Parts", "Docs": "", "Typewords": ["[]", "Part"] }, { "Name": "Message", "Docs": "", "Typewords": ["nullable", "Part"] }] }, "Envelope": { "Name": "Envelope", "Docs": "", "Fields": [{ "Name": "Date", "Docs": "", "Typewords": ["timestamp"] }, { "Name": "Subject", "Docs": "", "Typewords": ["string"] }, { "Name": "From", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "Sender", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "ReplyTo", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "CC", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "BCC", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "InReplyTo", "Docs": "", "Typewords": ["string"] }, { "Name": "MessageID", "Docs": "", "Typewords": ["string"] }] }, "Address": { "Name": "Address", "Docs": "", "Fields": [{ "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "User", "Docs": "", "Typewords": ["string"] }, { "Name": "Host", "Docs": "", "Typewords": ["string"] }] }, "MessageAddress": { "Name": "MessageAddress", "Docs": "", "Fields": [{ "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "User", "Docs": "", "Typewords": ["string"] }, { "Name": "Domain", "Docs": "", "Typewords": ["Domain"] }] }, diff --git a/webmail/text.js b/webmail/text.js index 30f0056..8299644 100644 --- a/webmail/text.js +++ b/webmail/text.js @@ -299,7 +299,7 @@ var api; "NotFilter": { "Name": "NotFilter", "Docs": "", "Fields": [{ "Name": "Words", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "From", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Subject", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Attachments", "Docs": "", "Typewords": ["AttachmentType"] }, { "Name": "Labels", "Docs": "", "Typewords": ["[]", "string"] }] }, "Page": { "Name": "Page", "Docs": "", "Fields": [{ "Name": "AnchorMessageID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Count", "Docs": "", "Typewords": ["int32"] }, { "Name": "DestMessageID", "Docs": "", "Typewords": ["int64"] }] }, "ParsedMessage": { "Name": "ParsedMessage", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Part", "Docs": "", "Typewords": ["Part"] }, { "Name": "Headers", "Docs": "", "Typewords": ["{}", "[]", "string"] }, { "Name": "ViewMode", "Docs": "", "Typewords": ["ViewMode"] }, { "Name": "Texts", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "HasHTML", "Docs": "", "Typewords": ["bool"] }, { "Name": "ListReplyAddress", "Docs": "", "Typewords": ["nullable", "MessageAddress"] }, { "Name": "TextPaths", "Docs": "", "Typewords": ["[]", "[]", "int32"] }, { "Name": "HTMLPath", "Docs": "", "Typewords": ["[]", "int32"] }] }, - "Part": { "Name": "Part", "Docs": "", "Fields": [{ "Name": "BoundaryOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "HeaderOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "BodyOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "EndOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "RawLineCount", "Docs": "", "Typewords": ["int64"] }, { "Name": "DecodedSize", "Docs": "", "Typewords": ["int64"] }, { "Name": "MediaType", "Docs": "", "Typewords": ["string"] }, { "Name": "MediaSubType", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTypeParams", "Docs": "", "Typewords": ["{}", "string"] }, { "Name": "ContentID", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentDescription", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTransferEncoding", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentDisposition", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentMD5", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentLanguage", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentLocation", "Docs": "", "Typewords": ["string"] }, { "Name": "Envelope", "Docs": "", "Typewords": ["nullable", "Envelope"] }, { "Name": "Parts", "Docs": "", "Typewords": ["[]", "Part"] }, { "Name": "Message", "Docs": "", "Typewords": ["nullable", "Part"] }] }, + "Part": { "Name": "Part", "Docs": "", "Fields": [{ "Name": "BoundaryOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "HeaderOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "BodyOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "EndOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "RawLineCount", "Docs": "", "Typewords": ["int64"] }, { "Name": "DecodedSize", "Docs": "", "Typewords": ["int64"] }, { "Name": "MediaType", "Docs": "", "Typewords": ["string"] }, { "Name": "MediaSubType", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTypeParams", "Docs": "", "Typewords": ["{}", "string"] }, { "Name": "ContentID", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentDescription", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentTransferEncoding", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentDisposition", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentMD5", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentLanguage", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentLocation", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "Envelope", "Docs": "", "Typewords": ["nullable", "Envelope"] }, { "Name": "Parts", "Docs": "", "Typewords": ["[]", "Part"] }, { "Name": "Message", "Docs": "", "Typewords": ["nullable", "Part"] }] }, "Envelope": { "Name": "Envelope", "Docs": "", "Fields": [{ "Name": "Date", "Docs": "", "Typewords": ["timestamp"] }, { "Name": "Subject", "Docs": "", "Typewords": ["string"] }, { "Name": "From", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "Sender", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "ReplyTo", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "CC", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "BCC", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "InReplyTo", "Docs": "", "Typewords": ["string"] }, { "Name": "MessageID", "Docs": "", "Typewords": ["string"] }] }, "Address": { "Name": "Address", "Docs": "", "Fields": [{ "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "User", "Docs": "", "Typewords": ["string"] }, { "Name": "Host", "Docs": "", "Typewords": ["string"] }] }, "MessageAddress": { "Name": "MessageAddress", "Docs": "", "Fields": [{ "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "User", "Docs": "", "Typewords": ["string"] }, { "Name": "Domain", "Docs": "", "Typewords": ["Domain"] }] }, diff --git a/webmail/webmail.go b/webmail/webmail.go index 7948056..0693e8c 100644 --- a/webmail/webmail.go +++ b/webmail/webmail.go @@ -898,7 +898,7 @@ func inlineSanitizeHTML(log mlog.Log, setHeaders func(), w io.Writer, p *message func findCID(p *message.Part, parents []*message.Part, cid string) *message.Part { for i := len(parents) - 1; i >= 0; i-- { for j, pp := range parents[i].Parts { - if strings.EqualFold(pp.ContentID, cid) { + if pp.ContentID != nil && strings.EqualFold(*pp.ContentID, cid) { return &parents[i].Parts[j] } } @@ -911,7 +911,7 @@ func findCID(p *message.Part, parents []*message.Part, cid string) *message.Part } func findCIDAll(p *message.Part, cid string) *message.Part { - if strings.EqualFold(p.ContentID, cid) { + if p.ContentID != nil && strings.EqualFold(*p.ContentID, cid) { return p } for i := range p.Parts { diff --git a/webmail/webmail.js b/webmail/webmail.js index 4bf9b40..cf41deb 100644 --- a/webmail/webmail.js +++ b/webmail/webmail.js @@ -299,7 +299,7 @@ var api; "NotFilter": { "Name": "NotFilter", "Docs": "", "Fields": [{ "Name": "Words", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "From", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Subject", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "Attachments", "Docs": "", "Typewords": ["AttachmentType"] }, { "Name": "Labels", "Docs": "", "Typewords": ["[]", "string"] }] }, "Page": { "Name": "Page", "Docs": "", "Fields": [{ "Name": "AnchorMessageID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Count", "Docs": "", "Typewords": ["int32"] }, { "Name": "DestMessageID", "Docs": "", "Typewords": ["int64"] }] }, "ParsedMessage": { "Name": "ParsedMessage", "Docs": "", "Fields": [{ "Name": "ID", "Docs": "", "Typewords": ["int64"] }, { "Name": "Part", "Docs": "", "Typewords": ["Part"] }, { "Name": "Headers", "Docs": "", "Typewords": ["{}", "[]", "string"] }, { "Name": "ViewMode", "Docs": "", "Typewords": ["ViewMode"] }, { "Name": "Texts", "Docs": "", "Typewords": ["[]", "string"] }, { "Name": "HasHTML", "Docs": "", "Typewords": ["bool"] }, { "Name": "ListReplyAddress", "Docs": "", "Typewords": ["nullable", "MessageAddress"] }, { "Name": "TextPaths", "Docs": "", "Typewords": ["[]", "[]", "int32"] }, { "Name": "HTMLPath", "Docs": "", "Typewords": ["[]", "int32"] }] }, - "Part": { "Name": "Part", "Docs": "", "Fields": [{ "Name": "BoundaryOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "HeaderOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "BodyOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "EndOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "RawLineCount", "Docs": "", "Typewords": ["int64"] }, { "Name": "DecodedSize", "Docs": "", "Typewords": ["int64"] }, { "Name": "MediaType", "Docs": "", "Typewords": ["string"] }, { "Name": "MediaSubType", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTypeParams", "Docs": "", "Typewords": ["{}", "string"] }, { "Name": "ContentID", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentDescription", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTransferEncoding", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentDisposition", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentMD5", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentLanguage", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentLocation", "Docs": "", "Typewords": ["string"] }, { "Name": "Envelope", "Docs": "", "Typewords": ["nullable", "Envelope"] }, { "Name": "Parts", "Docs": "", "Typewords": ["[]", "Part"] }, { "Name": "Message", "Docs": "", "Typewords": ["nullable", "Part"] }] }, + "Part": { "Name": "Part", "Docs": "", "Fields": [{ "Name": "BoundaryOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "HeaderOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "BodyOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "EndOffset", "Docs": "", "Typewords": ["int64"] }, { "Name": "RawLineCount", "Docs": "", "Typewords": ["int64"] }, { "Name": "DecodedSize", "Docs": "", "Typewords": ["int64"] }, { "Name": "MediaType", "Docs": "", "Typewords": ["string"] }, { "Name": "MediaSubType", "Docs": "", "Typewords": ["string"] }, { "Name": "ContentTypeParams", "Docs": "", "Typewords": ["{}", "string"] }, { "Name": "ContentID", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentDescription", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentTransferEncoding", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentDisposition", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentMD5", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentLanguage", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "ContentLocation", "Docs": "", "Typewords": ["nullable", "string"] }, { "Name": "Envelope", "Docs": "", "Typewords": ["nullable", "Envelope"] }, { "Name": "Parts", "Docs": "", "Typewords": ["[]", "Part"] }, { "Name": "Message", "Docs": "", "Typewords": ["nullable", "Part"] }] }, "Envelope": { "Name": "Envelope", "Docs": "", "Fields": [{ "Name": "Date", "Docs": "", "Typewords": ["timestamp"] }, { "Name": "Subject", "Docs": "", "Typewords": ["string"] }, { "Name": "From", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "Sender", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "ReplyTo", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "To", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "CC", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "BCC", "Docs": "", "Typewords": ["[]", "Address"] }, { "Name": "InReplyTo", "Docs": "", "Typewords": ["string"] }, { "Name": "MessageID", "Docs": "", "Typewords": ["string"] }] }, "Address": { "Name": "Address", "Docs": "", "Fields": [{ "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "User", "Docs": "", "Typewords": ["string"] }, { "Name": "Host", "Docs": "", "Typewords": ["string"] }] }, "MessageAddress": { "Name": "MessageAddress", "Docs": "", "Fields": [{ "Name": "Name", "Docs": "", "Typewords": ["string"] }, { "Name": "User", "Docs": "", "Typewords": ["string"] }, { "Name": "Domain", "Docs": "", "Typewords": ["Domain"] }] },