|
| 1 | +package imapclient |
| 2 | + |
| 3 | +import ( |
| 4 | + "bufio" |
| 5 | + "encoding/json" |
| 6 | + "reflect" |
| 7 | + "strings" |
| 8 | + "testing" |
| 9 | + |
| 10 | + "github.com/emersion/go-imap/v2" |
| 11 | + "github.com/emersion/go-imap/v2/internal/imapwire" |
| 12 | +) |
| 13 | + |
| 14 | +var bodyStructureCases = []struct { |
| 15 | + description string |
| 16 | + data string |
| 17 | + parsed imap.BodyStructure |
| 18 | +}{ |
| 19 | + { |
| 20 | + description: "example from RFC 3501", |
| 21 | + data: `(("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 23)("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") "<[email protected]>" "Compiler diff" "BASE64" 4554 73) "MIXED") `, |
| 22 | + parsed: &imap.BodyStructureMultiPart{ |
| 23 | + Children: []imap.BodyStructure{ |
| 24 | + &imap.BodyStructureSinglePart{ |
| 25 | + Type: "TEXT", |
| 26 | + Subtype: "PLAIN", |
| 27 | + Params: map[string]string{"charset": "US-ASCII"}, |
| 28 | + ID: "", |
| 29 | + Description: "", |
| 30 | + Encoding: "7BIT", |
| 31 | + Size: 1152, |
| 32 | + Text: &imap.BodyStructureText{ |
| 33 | + NumLines: 23, |
| 34 | + }, |
| 35 | + }, |
| 36 | + &imap.BodyStructureSinglePart{ |
| 37 | + Type: "TEXT", |
| 38 | + Subtype: "PLAIN", |
| 39 | + Params: map[string]string{ |
| 40 | + "charset": "US-ASCII", |
| 41 | + "name": "cc.diff", |
| 42 | + }, |
| 43 | + |
| 44 | + Description: "Compiler diff", |
| 45 | + Encoding: "BASE64", |
| 46 | + Size: 4554, |
| 47 | + Text: &imap.BodyStructureText{ |
| 48 | + NumLines: 73, |
| 49 | + }, |
| 50 | + }, |
| 51 | + }, |
| 52 | + Subtype: "MIXED", |
| 53 | + }, |
| 54 | + }, |
| 55 | + { |
| 56 | + description: "issue #678", |
| 57 | + data: `("text" "html" ("charset" "utf-8") NIL NIL "quoted-printable" 5606 133 NIL NIL NIL NIL)`, |
| 58 | + parsed: &imap.BodyStructureSinglePart{ |
| 59 | + Type: "text", |
| 60 | + Subtype: "html", |
| 61 | + Params: map[string]string{"charset": "utf-8"}, |
| 62 | + ID: "", |
| 63 | + Description: "", |
| 64 | + Encoding: "quoted-printable", |
| 65 | + Size: 5606, |
| 66 | + Text: &imap.BodyStructureText{ |
| 67 | + NumLines: 133, |
| 68 | + }, |
| 69 | + Extended: &imap.BodyStructureSinglePartExt{}, |
| 70 | + }, |
| 71 | + }, |
| 72 | + { |
| 73 | + description: "message/global attachment in Dovecot", |
| 74 | + data: `(((("text" "plain" ("charset" "UTF-8") NIL NIL "quoted-printable" 576 31 NIL NIL NIL NIL)("text" "html" ("charset" "UTF-8") NIL NIL "quoted-printable" 4691 112 NIL NIL NIL NIL) "alternative" ("boundary" "----=_NextPart_002_0063_01D85E63.43189F50") NIL NIL NIL)("image" "png" ("name" "image001.png") "<[email protected]>" NIL "base64" 3832 NIL NIL NIL NIL) "related" ("boundary" "----=_NextPart_001_0062_01D85E63.43189F50") NIL NIL NIL)("message" "delivery-status" ("name" "details.txt") NIL NIL "7bit" 594 NIL ("attachment" ("filename" "details.txt")) NIL NIL)("message" "global" ("name" "Untitled attachment 00019.dat") NIL NIL "7bit" 6726 NIL ("attachment" ("filename" "Untitled attachment 00019.dat")) NIL NIL) "mixed" ("boundary" "----=_NextPart_000_0061_01D85E63.43189F50") NIL ("en-us") NIL)`, |
| 75 | + parsed: &imap.BodyStructureMultiPart{ |
| 76 | + Children: []imap.BodyStructure{ |
| 77 | + &imap.BodyStructureMultiPart{ |
| 78 | + Children: []imap.BodyStructure{ |
| 79 | + &imap.BodyStructureMultiPart{ |
| 80 | + Children: []imap.BodyStructure{ |
| 81 | + &imap.BodyStructureSinglePart{ |
| 82 | + Type: "text", |
| 83 | + Subtype: "plain", |
| 84 | + Params: map[string]string{ |
| 85 | + "charset": "UTF-8", |
| 86 | + }, |
| 87 | + ID: "", |
| 88 | + Description: "", |
| 89 | + Encoding: "quoted-printable", |
| 90 | + Size: 576, |
| 91 | + Text: &imap.BodyStructureText{ |
| 92 | + NumLines: 31, |
| 93 | + }, |
| 94 | + Extended: &imap.BodyStructureSinglePartExt{}, |
| 95 | + }, |
| 96 | + &imap.BodyStructureSinglePart{ |
| 97 | + Type: "text", |
| 98 | + Subtype: "html", |
| 99 | + Params: map[string]string{ |
| 100 | + "charset": "UTF-8", |
| 101 | + }, |
| 102 | + ID: "", |
| 103 | + Description: "", |
| 104 | + Encoding: "quoted-printable", |
| 105 | + Size: 4691, |
| 106 | + Text: &imap.BodyStructureText{ |
| 107 | + NumLines: 112, |
| 108 | + }, |
| 109 | + Extended: &imap.BodyStructureSinglePartExt{}, |
| 110 | + }, |
| 111 | + }, |
| 112 | + Subtype: "alternative", |
| 113 | + Extended: &imap.BodyStructureMultiPartExt{ |
| 114 | + Params: map[string]string{ |
| 115 | + "boundary": "----=_NextPart_002_0063_01D85E63.43189F50", |
| 116 | + }, |
| 117 | + }, |
| 118 | + }, |
| 119 | + &imap.BodyStructureSinglePart{ |
| 120 | + Type: "image", |
| 121 | + Subtype: "png", |
| 122 | + Params: map[string]string{ |
| 123 | + "name": "image001.png", |
| 124 | + }, |
| 125 | + |
| 126 | + Description: "", |
| 127 | + Encoding: "base64", |
| 128 | + Size: 3832, |
| 129 | + Extended: &imap.BodyStructureSinglePartExt{}, |
| 130 | + }, |
| 131 | + }, |
| 132 | + Subtype: "related", |
| 133 | + Extended: &imap.BodyStructureMultiPartExt{ |
| 134 | + Params: map[string]string{ |
| 135 | + "boundary": "----=_NextPart_001_0062_01D85E63.43189F50", |
| 136 | + }, |
| 137 | + }, |
| 138 | + }, |
| 139 | + &imap.BodyStructureSinglePart{ |
| 140 | + Type: "message", |
| 141 | + Subtype: "delivery-status", |
| 142 | + Params: map[string]string{ |
| 143 | + "name": "details.txt", |
| 144 | + }, |
| 145 | + ID: "", |
| 146 | + Description: "", |
| 147 | + Encoding: "7bit", |
| 148 | + Size: 594, |
| 149 | + Extended: &imap.BodyStructureSinglePartExt{ |
| 150 | + Disposition: &imap.BodyStructureDisposition{ |
| 151 | + Value: "attachment", |
| 152 | + Params: map[string]string{ |
| 153 | + "filename": "details.txt", |
| 154 | + }, |
| 155 | + }, |
| 156 | + }, |
| 157 | + }, |
| 158 | + &imap.BodyStructureSinglePart{ |
| 159 | + Type: "message", |
| 160 | + Subtype: "global", |
| 161 | + Params: map[string]string{ |
| 162 | + "name": "Untitled attachment 00019.dat", |
| 163 | + }, |
| 164 | + ID: "", |
| 165 | + Description: "", |
| 166 | + Encoding: "7bit", |
| 167 | + Size: 6726, |
| 168 | + MessageRFC822: nil, |
| 169 | + Extended: &imap.BodyStructureSinglePartExt{ |
| 170 | + Disposition: &imap.BodyStructureDisposition{ |
| 171 | + Value: "attachment", |
| 172 | + Params: map[string]string{ |
| 173 | + "filename": "Untitled attachment 00019.dat", |
| 174 | + }, |
| 175 | + }, |
| 176 | + }, |
| 177 | + }, |
| 178 | + }, |
| 179 | + Subtype: "mixed", |
| 180 | + Extended: &imap.BodyStructureMultiPartExt{ |
| 181 | + Params: map[string]string{ |
| 182 | + "boundary": "----=_NextPart_000_0061_01D85E63.43189F50", |
| 183 | + }, |
| 184 | + Language: []string{ |
| 185 | + "en-us", |
| 186 | + }, |
| 187 | + }, |
| 188 | + }, |
| 189 | + }, |
| 190 | +} |
| 191 | + |
| 192 | +func TestParseBodyStructure(t *testing.T) { |
| 193 | + for _, c := range bodyStructureCases { |
| 194 | + dec := imapwire.NewDecoder(bufio.NewReader(strings.NewReader(c.data)), imapwire.ConnSideClient) |
| 195 | + s, err := readBody(dec, &Options{}) |
| 196 | + if err != nil { |
| 197 | + t.Fatalf("%s: error parsing body structure: %v", c.description, err) |
| 198 | + } |
| 199 | + if !reflect.DeepEqual(s, c.parsed) { |
| 200 | + t.Fatalf("%s: parsed structure doesn't match: want %s, got %s", c.description, toJSON(c.parsed), toJSON(s)) |
| 201 | + } |
| 202 | + } |
| 203 | +} |
| 204 | + |
| 205 | +func toJSON(v any) []byte { |
| 206 | + data, err := json.Marshal(v) |
| 207 | + if err != nil { |
| 208 | + panic(err) |
| 209 | + } |
| 210 | + return data |
| 211 | +} |
0 commit comments