Package: BODYSTRUCTURE
BODYSTRUCTURE
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
BODYSTRUCTURE(FetchResponse) |
|
|
|
|
|
||||||||||||||||||||
isMulti() |
|
|
|
|
|
||||||||||||||||||||
isNested() |
|
|
|
|
|
||||||||||||||||||||
isSingle() |
|
|
|
|
|
||||||||||||||||||||
parseBodyExtension(Response) |
|
|
|
|
|
||||||||||||||||||||
parseParameters(Response) |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
Coverage
1: /*
2: * Copyright (c) 1997, 2023 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package org.eclipse.angus.mail.imap.protocol;
18:
19: import java.util.List;
20: import java.util.ArrayList;
21: import jakarta.mail.internet.ParameterList;
22:
23: import org.eclipse.angus.mail.iap.*;
24: import org.eclipse.angus.mail.util.PropUtil;
25: import org.eclipse.angus.mail.iap.ParsingException;
26: import org.eclipse.angus.mail.iap.Response;
27:
28: /**
29: * A BODYSTRUCTURE response.
30: *
31: * @author John Mani
32: * @author Bill Shannon
33: */
34:
35: public class BODYSTRUCTURE implements Item {
36:
37: static final char[] name =
38:         {'B','O','D','Y','S','T','R','U','C','T','U','R','E'};
39: public int msgno;
40:
41: public String type;                // Type
42: public String subtype;        // Subtype
43: public String encoding;        // Encoding
44: public int lines = -1;        // Size in lines
45: public int size = -1;        // Size in bytes
46: public String disposition;        // Disposition
47: public String id;                // Content-ID
48: public String description;        // Content-Description
49: public String md5;                // MD-5 checksum
50: public String attachment;        // Attachment name
51: public ParameterList cParams; // Body parameters
52: public ParameterList dParams; // Disposition parameters
53: public String[] language;        // Language
54: public BODYSTRUCTURE[] bodies; // array of BODYSTRUCTURE objects
55:                                  // for multipart & message/rfc822
56: public ENVELOPE envelope;        // for message/rfc822
57:
58: private static int SINGLE        = 1;
59: private static int MULTI        = 2;
60: private static int NESTED        = 3;
61: private int processedType;        // MULTI | SINGLE | NESTED
62:
63: // special debugging output to debug parsing errors
64: private static final boolean parseDebug =
65:         PropUtil.getBooleanSystemProperty("mail.imap.parse.debug", false);
66:
67:
68: public BODYSTRUCTURE(FetchResponse r) throws ParsingException {
69:•        if (parseDebug)
70:          System.out.println("DEBUG IMAP: parsing BODYSTRUCTURE");
71:         msgno = r.getNumber();
72:•        if (parseDebug)
73:          System.out.println("DEBUG IMAP: msgno " + msgno);
74:
75:         r.skipSpaces();
76:
77:•        if (r.readByte() != '(')
78:          throw new ParsingException(
79:                 "BODYSTRUCTURE parse error: missing ``('' at start");
80:
81:•        if (r.peekByte() == '(') { // multipart
82:•         if (parseDebug)
83:                 System.out.println("DEBUG IMAP: parsing multipart");
84:          type = "multipart";
85:          processedType = MULTI;
86:          List<BODYSTRUCTURE> v = new ArrayList<>(1);
87:          do {
88:                 v.add(new BODYSTRUCTURE(r));
89:                 /*
90:                  * Even though the IMAP spec says there can't be any spaces
91:                  * between parts, some servers erroneously put a space in
92:                  * here. In the spirit of "be liberal in what you accept",
93:                  * we skip it.
94:                  */
95:                 r.skipSpaces();
96:•         } while (r.peekByte() == '(');
97:
98:          // setup bodies.
99:          bodies = v.toArray(new BODYSTRUCTURE[v.size()]);
100:
101:          subtype = r.readString(); // subtype
102:•         if (parseDebug)
103:                 System.out.println("DEBUG IMAP: subtype " + subtype);
104:
105:•         if (r.isNextNonSpace(')')) { // done
106:•                if (parseDebug)
107:                  System.out.println("DEBUG IMAP: parse DONE");
108:                 return;
109:          }
110:
111:          // Else, we have extension data
112:
113:•         if (parseDebug)
114:                 System.out.println("DEBUG IMAP: parsing extension data");
115:          // Body parameters
116:          cParams = parseParameters(r);
117:•         if (r.isNextNonSpace(')')) { // done
118:•                if (parseDebug)
119:                  System.out.println("DEBUG IMAP: body parameters DONE");
120:                 return;
121:          }
122:         
123:          // Disposition
124:          byte b = r.peekByte();
125:•         if (b == '(') {
126:•                if (parseDebug)
127:                  System.out.println("DEBUG IMAP: parse disposition");
128:                 r.readByte();
129:                 disposition = r.readString();
130:•                if (parseDebug)
131:                  System.out.println("DEBUG IMAP: disposition " +
132:                                                         disposition);
133:                 dParams = parseParameters(r);
134:•                if (!r.isNextNonSpace(')')) // eat the end ')'
135:                  throw new ParsingException(
136:                         "BODYSTRUCTURE parse error: " +
137:                         "missing ``)'' at end of disposition in multipart");
138:•                if (parseDebug)
139:                  System.out.println("DEBUG IMAP: disposition DONE");
140:•         } else if (b == 'N' || b == 'n') {
141:•                if (parseDebug)
142:                  System.out.println("DEBUG IMAP: disposition NIL");
143:                 r.skip(3); // skip 'NIL'
144:          } else {
145:                 /*
146:                 throw new ParsingException(
147:                  "BODYSTRUCTURE parse error: " +
148:                  type + "/" + subtype + ": " +
149:                  "bad multipart disposition, b " + b);
150:                 */
151:•                if (parseDebug)
152:                  System.out.println("DEBUG IMAP: bad multipart disposition" +
153:                                         ", applying Exchange bug workaround");
154:                 description = r.readString();
155:•                if (parseDebug)
156:                  System.out.println("DEBUG IMAP: multipart description " +
157:                                         description);
158:                 // Throw away whatever comes after it, since we have no
159:                 // idea what it's supposed to be
160:•                while (r.readByte() == ' ')
161:                  parseBodyExtension(r);
162:                 return;
163:          }
164:
165:          // RFC3501 allows no body-fld-lang after body-fld-disp,
166:          // even though RFC2060 required it
167:•         if (r.isNextNonSpace(')')) {
168:•                if (parseDebug)
169:                  System.out.println("DEBUG IMAP: no body-fld-lang");
170:                 return; // done
171:          }
172:
173:          // Language
174:•         if (r.peekByte() == '(') { // a list follows
175:                 language = r.readStringList();
176:•                if (parseDebug)
177:                  System.out.println(
178:                         "DEBUG IMAP: language len " + language.length);
179:          } else {
180:                 String l = r.readString();
181:•                if (l != null) {
182:                  String[] la = { l };
183:                  language = la;
184:•                 if (parseDebug)
185:                         System.out.println("DEBUG IMAP: language " + l);
186:                 }
187:          }
188:
189:          // RFC3501 defines an optional "body location" next,
190:          // but for now we ignore it along with other extensions.
191:
192:          // Throw away any further extension data
193:•         while (r.readByte() == ' ')
194:                 parseBodyExtension(r);
195:•        } else if (r.peekByte() == ')') {        // (illegal) empty body
196:          /*
197:          * Domino will fail to return the body structure of nested messages.
198:          * Fake it by providing an empty message. Could probably do better
199:          * with more work...
200:          */
201:          /*
202:          * XXX - this prevents the exception, but without the exception
203:          * the application has no way to know the data from the message
204:          * is missing.
205:          *
206:          if (parseDebug)
207:                 System.out.println("DEBUG IMAP: empty body, fake it");
208:          r.readByte();
209:          type = "text";
210:          subtype = "plain";
211:          lines = 0;
212:          size = 0;
213:          */
214:          throw new ParsingException(
215:                          "BODYSTRUCTURE parse error: missing body content");
216:         } else { // Single part
217:•         if (parseDebug)
218:                 System.out.println("DEBUG IMAP: single part");
219:          type = r.readString();
220:•         if (parseDebug)
221:                 System.out.println("DEBUG IMAP: type " + type);
222:          processedType = SINGLE;
223:          subtype = r.readString();
224:•         if (parseDebug)
225:                 System.out.println("DEBUG IMAP: subtype " + subtype);
226:
227:          // SIMS 4.0 returns NIL for a Content-Type of "binary", fix it here
228:•         if (type == null) {
229:                 type = "application";
230:                 subtype = "octet-stream";
231:          }
232:          cParams = parseParameters(r);
233:•         if (parseDebug)
234:                 System.out.println("DEBUG IMAP: cParams " + cParams);
235:          id = r.readString();
236:•         if (parseDebug)
237:                 System.out.println("DEBUG IMAP: id " + id);
238:          description = r.readString();
239:•         if (parseDebug)
240:                 System.out.println("DEBUG IMAP: description " + description);
241:          /*
242:          * XXX - Work around bug in Exchange 2010 that
243:          * returns unquoted string.
244:          */
245:          encoding = r.readAtomString();
246:•         if (encoding != null && encoding.equalsIgnoreCase("NIL")) {
247:•                if (parseDebug)
248:                  System.out.println("DEBUG IMAP: NIL encoding" +
249:                                         ", applying Exchange bug workaround");
250:                 encoding = null;
251:          }
252:          /*
253:          * XXX - Work around bug in office365.com that returns
254:          *         a string with a trailing space in some cases.
255:          */
256:•         if (encoding != null)
257:                 encoding = encoding.trim();
258:•         if (parseDebug)
259:                 System.out.println("DEBUG IMAP: encoding " + encoding);
260:          size = r.readNumber();
261:•         if (parseDebug)
262:                 System.out.println("DEBUG IMAP: size " + size);
263:•         if (size < 0)
264:                 throw new ParsingException(
265:                          "BODYSTRUCTURE parse error: bad ``size'' element");
266:
267:          // "text/*" & "message/rfc822" types have additional data ..
268:•         if (type.equalsIgnoreCase("text")) {
269:                 lines = r.readNumber();
270:•                if (parseDebug)
271:                  System.out.println("DEBUG IMAP: lines " + lines);
272:•                if (lines < 0)
273:                  throw new ParsingException(
274:                          "BODYSTRUCTURE parse error: bad ``lines'' element");
275:•         } else if (type.equalsIgnoreCase("message") &&
276:•                 subtype.equalsIgnoreCase("rfc822")) {
277:                 // Nested message
278:                 processedType = NESTED;
279:                 // The envelope comes next, but sadly Gmail handles nested
280:                 // messages just like simple body parts and fails to return
281:                 // the envelope and body structure of the message (sort of
282:                 // like IMAP4 before rev1).
283:                 r.skipSpaces();
284:•                if (r.peekByte() == '(') {        // the envelope follows
285:                  envelope = new ENVELOPE(r);
286:•                 if (parseDebug)
287:                         System.out.println(
288:                          "DEBUG IMAP: got envelope of nested message");
289:                  BODYSTRUCTURE[] bs = { new BODYSTRUCTURE(r) };
290:                  bodies = bs;
291:                  lines = r.readNumber();
292:•                 if (parseDebug)
293:                         System.out.println("DEBUG IMAP: lines " + lines);
294:•                 if (lines < 0)
295:                         throw new ParsingException(
296:                          "BODYSTRUCTURE parse error: bad ``lines'' element");
297:                 } else {
298:•                 if (parseDebug)
299:                         System.out.println("DEBUG IMAP: " +
300:                          "missing envelope and body of nested message");
301:                 }
302:          } else {
303:                 // Detect common error of including lines element on other types
304:                 r.skipSpaces();
305:                 byte bn = r.peekByte();
306:•                if (Character.isDigit((char)bn)) // number
307:                  throw new ParsingException(
308:                          "BODYSTRUCTURE parse error: server erroneously " +
309:                                 "included ``lines'' element with type " +
310:                                 type + "/" + subtype);
311:          }
312:
313:•         if (r.isNextNonSpace(')')) {
314:•                if (parseDebug)
315:                  System.out.println("DEBUG IMAP: parse DONE");
316:                 return; // done
317:          }
318:
319:          // Optional extension data
320:
321:          // MD5
322:          md5 = r.readString();
323:•         if (r.isNextNonSpace(')')) {
324:•                if (parseDebug)
325:                  System.out.println("DEBUG IMAP: no MD5 DONE");
326:                 return; // done
327:          }
328:         
329:          // Disposition
330:          byte b = r.readByte();
331:•         if (b == '(') {
332:                 disposition = r.readString();
333:•                if (parseDebug)
334:                  System.out.println("DEBUG IMAP: disposition " +
335:                                                         disposition);
336:                 dParams = parseParameters(r);
337:•                if (parseDebug)
338:                  System.out.println("DEBUG IMAP: dParams " + dParams);
339:•                if (!r.isNextNonSpace(')')) // eat the end ')'
340:                  throw new ParsingException(
341:                         "BODYSTRUCTURE parse error: " +
342:                         "missing ``)'' at end of disposition");
343:•         } else if (b == 'N' || b == 'n') {
344:•                if (parseDebug)
345:                  System.out.println("DEBUG IMAP: disposition NIL");
346:                 r.skip(2); // skip 'NIL'
347:          } else {
348:                 throw new ParsingException(
349:                  "BODYSTRUCTURE parse error: " +
350:                  type + "/" + subtype + ": " +
351:                  "bad single part disposition, b " + b);
352:          }
353:
354:•         if (r.isNextNonSpace(')')) {
355:•                if (parseDebug)
356:                  System.out.println("DEBUG IMAP: disposition DONE");
357:                 return; // done
358:          }
359:         
360:          // Language
361:•         if (r.peekByte() == '(') { // a list follows
362:                 language = r.readStringList();
363:•                if (parseDebug)
364:                  System.out.println("DEBUG IMAP: language len " +
365:                                                         language.length);
366:          } else { // protocol is unnessarily complex here
367:                 String l = r.readString();
368:•                if (l != null) {
369:                  String[] la = { l };
370:                  language = la;
371:•                 if (parseDebug)
372:                         System.out.println("DEBUG IMAP: language " + l);
373:                 }
374:          }
375:
376:          // RFC3501 defines an optional "body location" next,
377:          // but for now we ignore it along with other extensions.
378:
379:          // Throw away any further extension data
380:•         while (r.readByte() == ' ')
381:                 parseBodyExtension(r);
382:•         if (parseDebug)
383:                 System.out.println("DEBUG IMAP: all DONE");
384:         }
385: }
386:
387: public boolean isMulti() {
388:•        return processedType == MULTI;
389: }
390:
391: public boolean isSingle() {
392:•        return processedType == SINGLE;
393: }
394:
395: public boolean isNested() {
396:•        return processedType == NESTED;
397: }
398:
399: private ParameterList parseParameters(Response r)
400:                         throws ParsingException {
401:         r.skipSpaces();
402:
403:         ParameterList list = null;
404:         byte b = r.readByte();
405:•        if (b == '(') {
406:          list = new ParameterList();
407:          do {
408:                 String name = r.readString();
409:•                if (parseDebug)
410:                  System.out.println("DEBUG IMAP: parameter name " + name);
411:•                if (name == null)
412:                  throw new ParsingException(
413:                         "BODYSTRUCTURE parse error: " +
414:                         type + "/" + subtype + ": " +
415:                         "null name in parameter list");
416:                 String value = r.readString();
417:•                if (parseDebug)
418:                  System.out.println("DEBUG IMAP: parameter value " + value);
419:•                if (value == null) {        // work around buggy servers
420:•                 if (parseDebug)
421:                         System.out.println("DEBUG IMAP: NIL parameter value" +
422:                                         ", applying Exchange bug workaround");
423:                  value = "";
424:                 }
425:                 list.set(name, value);
426:•         } while (!r.isNextNonSpace(')'));
427:          list.combineSegments();
428:•        } else if (b == 'N' || b == 'n') {
429:•         if (parseDebug)
430:                 System.out.println("DEBUG IMAP: parameter list NIL");
431:          r.skip(2);
432:         } else
433:          throw new ParsingException("Parameter list parse error");
434:
435:         return list;
436: }
437:
438: private void parseBodyExtension(Response r) throws ParsingException {
439:         r.skipSpaces();
440:
441:         byte b = r.peekByte();
442:•        if (b == '(') {
443:          r.skip(1); // skip '('
444:          do {
445:                 parseBodyExtension(r);
446:•         } while (!r.isNextNonSpace(')'));
447:•        } else if (Character.isDigit((char)b)) // number
448:          r.readNumber();
449:         else // nstring
450:          r.readString();
451: }
452: }