Skip to content

Commit 8fa197b

Browse files
committed
imapserver: for the "bodystructure" fetch response item, add the content-type parameters for multiparts so clients will get the mime boundary without having to parse the message themselves
"bodystructure" is like "body", but bodystructure allows returning more information. we chose not to do that, initially because it was easier to implement, and more recently because we can't easily return the additional content-md5 field for leaf parts (since we don't have it in parsed form). but now we just return the extended form for multiparts, and non-extended form for leaf parts. likely no one would be looking for any content-md5-value for leaf parts anyway. knowing the boundary is much more likely to be useful. for issue #217 by danieleggert, thanks for reporting!
1 parent 598c5ea commit 8fa197b

File tree

2 files changed

+43
-25
lines changed

2 files changed

+43
-25
lines changed

imapserver/fetch.go

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ func (cmd *fetchCmd) xprocessAtt(a fetchAtt) []token {
406406

407407
case "BODYSTRUCTURE":
408408
_, part := cmd.xensureParsed()
409-
bs := xbodystructure(part)
409+
bs := xbodystructure(part, true)
410410
return []token{bare("BODYSTRUCTURE"), bs}
411411

412412
case "BODY":
@@ -660,7 +660,7 @@ func (cmd *fetchCmd) xbody(a fetchAtt) (string, token) {
660660

661661
if a.section == nil {
662662
// Non-extensible form of BODYSTRUCTURE.
663-
return a.field, xbodystructure(part)
663+
return a.field, xbodystructure(part, false)
664664
}
665665

666666
cmd.peekOrSeen(a.peek)
@@ -865,20 +865,33 @@ func bodyFldEnc(s string) token {
865865

866866
// xbodystructure returns a "body".
867867
// calls itself for multipart messages and message/{rfc822,global}.
868-
func xbodystructure(p *message.Part) token {
868+
func xbodystructure(p *message.Part, extensible bool) token {
869869
if p.MediaType == "MULTIPART" {
870870
// Multipart, ../rfc/9051:6355 ../rfc/9051:6411
871871
var bodies concat
872872
for i := range p.Parts {
873-
bodies = append(bodies, xbodystructure(&p.Parts[i]))
873+
bodies = append(bodies, xbodystructure(&p.Parts[i], extensible))
874874
}
875-
return listspace{bodies, string0(p.MediaSubType)}
875+
r := listspace{bodies, string0(p.MediaSubType)}
876+
if extensible {
877+
if len(p.ContentTypeParams) == 0 {
878+
r = append(r, nilt)
879+
} else {
880+
params := make(listspace, 0, 2*len(p.ContentTypeParams))
881+
for k, v := range p.ContentTypeParams {
882+
params = append(params, string0(k), string0(v))
883+
}
884+
r = append(r, params)
885+
}
886+
}
887+
return r
876888
}
877889

878890
// ../rfc/9051:6355
891+
var r listspace
879892
if p.MediaType == "TEXT" {
880893
// ../rfc/9051:6404 ../rfc/9051:6418
881-
return listspace{
894+
r = listspace{
882895
dquote("TEXT"), string0(p.MediaSubType), // ../rfc/9051:6739
883896
// ../rfc/9051:6376
884897
bodyFldParams(p.ContentTypeParams), // ../rfc/9051:6401
@@ -891,7 +904,7 @@ func xbodystructure(p *message.Part) token {
891904
} else if p.MediaType == "MESSAGE" && (p.MediaSubType == "RFC822" || p.MediaSubType == "GLOBAL") {
892905
// ../rfc/9051:6415
893906
// note: we don't have to prepare p.Message for reading, because we aren't going to read from it.
894-
return listspace{
907+
r = listspace{
895908
dquote("MESSAGE"), dquote(p.MediaSubType), // ../rfc/9051:6732
896909
// ../rfc/9051:6376
897910
bodyFldParams(p.ContentTypeParams), // ../rfc/9051:6401
@@ -900,25 +913,28 @@ func xbodystructure(p *message.Part) token {
900913
bodyFldEnc(p.ContentTransferEncoding),
901914
number(p.EndOffset - p.BodyOffset),
902915
xenvelope(p.Message),
903-
xbodystructure(p.Message),
916+
xbodystructure(p.Message, extensible),
904917
number(p.RawLineCount), // todo: or mp.RawLineCount?
905918
}
919+
} else {
920+
var media token
921+
switch p.MediaType {
922+
case "APPLICATION", "AUDIO", "IMAGE", "FONT", "MESSAGE", "MODEL", "VIDEO":
923+
media = dquote(p.MediaType)
924+
default:
925+
media = string0(p.MediaType)
926+
}
927+
// ../rfc/9051:6404 ../rfc/9051:6407
928+
r = listspace{
929+
media, string0(p.MediaSubType), // ../rfc/9051:6723
930+
// ../rfc/9051:6376
931+
bodyFldParams(p.ContentTypeParams), // ../rfc/9051:6401
932+
nilOrString(p.ContentID),
933+
nilOrString(p.ContentDescription),
934+
bodyFldEnc(p.ContentTransferEncoding),
935+
number(p.EndOffset - p.BodyOffset),
936+
}
906937
}
907-
var media token
908-
switch p.MediaType {
909-
case "APPLICATION", "AUDIO", "IMAGE", "FONT", "MESSAGE", "MODEL", "VIDEO":
910-
media = dquote(p.MediaType)
911-
default:
912-
media = string0(p.MediaType)
913-
}
914-
// ../rfc/9051:6404 ../rfc/9051:6407
915-
return listspace{
916-
media, string0(p.MediaSubType), // ../rfc/9051:6723
917-
// ../rfc/9051:6376
918-
bodyFldParams(p.ContentTypeParams), // ../rfc/9051:6401
919-
nilOrString(p.ContentID),
920-
nilOrString(p.ContentDescription),
921-
bodyFldEnc(p.ContentTransferEncoding),
922-
number(p.EndOffset - p.BodyOffset),
923-
}
938+
// todo: if "extensible", we could add the value of the "content-md5" header. we don't have it in our parsed data structure, so we don't add it. likely no one would use it, also not any of the other optional fields. ../rfc/9051:6366
939+
return r
924940
}

imapserver/fetch_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ func TestFetch(t *testing.T) {
241241
imapclient.BodyTypeBasic{MediaType: "IMAGE", MediaSubtype: "JPEG", BodyFields: imapclient.BodyFields{CTE: "BASE64"}},
242242
},
243243
MediaSubtype: "PARALLEL",
244+
Ext: &imapclient.BodyExtensionMpart{Params: [][2]string{{"boundary", "unique-boundary-2"}}},
244245
},
245246
imapclient.BodyTypeText{MediaType: "TEXT", MediaSubtype: "ENRICHED", BodyFields: imapclient.BodyFields{Octets: 145}, Lines: 5},
246247
imapclient.BodyTypeMsg{
@@ -260,6 +261,7 @@ func TestFetch(t *testing.T) {
260261
},
261262
},
262263
MediaSubtype: "MIXED",
264+
Ext: &imapclient.BodyExtensionMpart{Params: [][2]string{{"boundary", "unique-boundary-1"}}},
263265
},
264266
}
265267
tc.client.Append("inbox", nil, &received, []byte(nestedMessage))

0 commit comments

Comments
 (0)