Package: IMAPBodyPart
IMAPBodyPart
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
IMAPBodyPart(BODYSTRUCTURE, String, IMAPMessage) |
|
|
|
|
|
||||||||||||||||||||
addHeader(String, String) |
|
|
|
|
|
||||||||||||||||||||
addHeaderLine(String) |
|
|
|
|
|
||||||||||||||||||||
getAllHeaderLines() |
|
|
|
|
|
||||||||||||||||||||
getAllHeaders() |
|
|
|
|
|
||||||||||||||||||||
getContentID() |
|
|
|
|
|
||||||||||||||||||||
getContentMD5() |
|
|
|
|
|
||||||||||||||||||||
getContentStream() |
|
|
|
|
|
||||||||||||||||||||
getContentType() |
|
|
|
|
|
||||||||||||||||||||
getDataHandler() |
|
|
|
|
|
||||||||||||||||||||
getDescription() |
|
|
|
|
|
||||||||||||||||||||
getDisposition() |
|
|
|
|
|
||||||||||||||||||||
getEncoding() |
|
|
|
|
|
||||||||||||||||||||
getFileName() |
|
|
|
|
|
||||||||||||||||||||
getHeader(String) |
|
|
|
|
|
||||||||||||||||||||
getHeaderStream() |
|
|
|
|
|
||||||||||||||||||||
getLineCount() |
|
|
|
|
|
||||||||||||||||||||
getMatchingHeaderLines(String[]) |
|
|
|
|
|
||||||||||||||||||||
getMatchingHeaders(String[]) |
|
|
|
|
|
||||||||||||||||||||
getMimeStream() |
|
|
|
|
|
||||||||||||||||||||
getNonMatchingHeaderLines(String[]) |
|
|
|
|
|
||||||||||||||||||||
getNonMatchingHeaders(String[]) |
|
|
|
|
|
||||||||||||||||||||
getSize() |
|
|
|
|
|
||||||||||||||||||||
loadHeaders() |
|
|
|
|
|
||||||||||||||||||||
removeHeader(String) |
|
|
|
|
|
||||||||||||||||||||
setContent(Multipart) |
|
|
|
|
|
||||||||||||||||||||
setContent(Object, String) |
|
|
|
|
|
||||||||||||||||||||
setContentMD5(String) |
|
|
|
|
|
||||||||||||||||||||
setDataHandler(DataHandler) |
|
|
|
|
|
||||||||||||||||||||
setDescription(String, String) |
|
|
|
|
|
||||||||||||||||||||
setDisposition(String) |
|
|
|
|
|
||||||||||||||||||||
setFileName(String) |
|
|
|
|
|
||||||||||||||||||||
setHeader(String, String) |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
||||||||||||||||||||
updateHeaders() |
|
|
|
|
|
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;
18:
19: import jakarta.activation.DataHandler;
20: import jakarta.mail.FolderClosedException;
21: import jakarta.mail.Header;
22: import jakarta.mail.IllegalWriteException;
23: import jakarta.mail.MessagingException;
24: import jakarta.mail.Multipart;
25: import jakarta.mail.internet.ContentType;
26: import jakarta.mail.internet.InternetHeaders;
27: import jakarta.mail.internet.MimeBodyPart;
28: import jakarta.mail.internet.MimeUtility;
29:
30: import java.io.ByteArrayInputStream;
31: import java.io.IOException;
32: import java.io.InputStream;
33: import java.io.SequenceInputStream;
34: import java.io.UnsupportedEncodingException;
35: import java.util.Enumeration;
36:
37: import org.eclipse.angus.mail.iap.ConnectionException;
38: import org.eclipse.angus.mail.iap.ProtocolException;
39: import org.eclipse.angus.mail.imap.protocol.BODY;
40: import org.eclipse.angus.mail.imap.protocol.BODYSTRUCTURE;
41: import org.eclipse.angus.mail.imap.protocol.IMAPProtocol;
42: import org.eclipse.angus.mail.util.LineOutputStream;
43: import org.eclipse.angus.mail.util.SharedByteArrayOutputStream;
44: import org.eclipse.angus.mail.util.PropUtil;
45: import org.eclipse.angus.mail.util.ReadableMime;
46:
47: /**
48: * An IMAP body part.
49: *
50: * @author John Mani
51: * @author Bill Shannon
52: */
53:
54: public class IMAPBodyPart extends MimeBodyPart implements ReadableMime {
55: private IMAPMessage message;
56: private BODYSTRUCTURE bs;
57: private String sectionId;
58:
59: // processed values ..
60: private String type;
61: private String description;
62:
63: private boolean headersLoaded = false;
64:
65: private static final boolean decodeFileName =
66:         PropUtil.getBooleanSystemProperty("mail.mime.decodefilename", false);
67:
68: protected IMAPBodyPart(BODYSTRUCTURE bs, String sid, IMAPMessage message) {
69:         super();
70:         this.bs = bs;
71:         this.sectionId = sid;
72:         this.message = message;
73:         // generate content-type
74:         ContentType ct = new ContentType(bs.type, bs.subtype, bs.cParams);
75:         type = ct.toString();
76: }
77:
78: /* Override this method to make it a no-op, rather than throw
79: * an IllegalWriteException. This will permit IMAPBodyParts to
80: * be inserted in newly crafted MimeMessages, especially when
81: * forwarding or replying to messages.
82: */
83: @Override
84: protected void updateHeaders() {
85:         return;
86: }
87:
88: @Override
89: public int getSize() throws MessagingException {
90:         return bs.size;
91: }
92:
93: @Override
94: public int getLineCount() throws MessagingException {
95:         return bs.lines;
96: }
97:
98: @Override
99: public String getContentType() throws MessagingException {
100:         return type;
101: }
102:
103: @Override
104: public String getDisposition() throws MessagingException {
105:         return bs.disposition;
106: }
107:
108: @Override
109: public void setDisposition(String disposition) throws MessagingException {
110:         throw new IllegalWriteException("IMAPBodyPart is read-only");
111: }
112:
113: @Override
114: public String getEncoding() throws MessagingException {
115:         return bs.encoding;
116: }
117:
118: @Override
119: public String getContentID() throws MessagingException {
120:         return bs.id;
121: }
122:
123: @Override
124: public String getContentMD5() throws MessagingException {
125:         return bs.md5;
126: }
127:
128: @Override
129: public void setContentMD5(String md5) throws MessagingException {
130:         throw new IllegalWriteException("IMAPBodyPart is read-only");
131: }
132:
133: @Override
134: public String getDescription() throws MessagingException {
135:•        if (description != null) // cached value ?
136:          return description;
137:
138:•        if (bs.description == null)
139:          return null;
140:         
141:         try {
142:          description = MimeUtility.decodeText(bs.description);
143:         } catch (UnsupportedEncodingException ex) {
144:          description = bs.description;
145:         }
146:
147:         return description;
148: }
149:
150: @Override
151: public void setDescription(String description, String charset)
152:                         throws MessagingException {
153:         throw new IllegalWriteException("IMAPBodyPart is read-only");
154: }
155:
156: @Override
157: public String getFileName() throws MessagingException {
158:         String filename = null;
159:•        if (bs.dParams != null)
160:          filename = bs.dParams.get("filename");
161:•        if ((filename == null || filename.isEmpty()) && bs.cParams != null)
162:          filename = bs.cParams.get("name");
163:•        if (decodeFileName && filename != null) {
164:          try {
165:                 filename = MimeUtility.decodeText(filename);
166:          } catch (UnsupportedEncodingException ex) {
167:                 throw new MessagingException("Can't decode filename", ex);
168:          }
169:         }
170:         return filename;
171: }
172:
173: @Override
174: public void setFileName(String filename) throws MessagingException {
175:         throw new IllegalWriteException("IMAPBodyPart is read-only");
176: }
177:
178: @Override
179: protected InputStream getContentStream() throws MessagingException {
180:         InputStream is = null;
181:         boolean pk = message.getPeek();        // acquire outside of message cache lock
182:
183: // Acquire MessageCacheLock, to freeze seqnum.
184: synchronized(message.getMessageCacheLock()) {
185:          try {
186:                 IMAPProtocol p = message.getProtocol();
187:
188:                 // Check whether this message is expunged
189:                 message.checkExpunged();
190:
191:•                if (p.isREV1() && (message.getFetchBlockSize() != -1))
192:                  return new IMAPInputStream(message, sectionId,
193:•                        message.ignoreBodyStructureSize() ? -1 : bs.size, pk);
194:
195:                 // Else, vanila IMAP4, no partial fetch
196:
197:                 int seqnum = message.getSequenceNumber();
198:                 BODY b;
199:•                if (pk)
200:                  b = p.peekBody(seqnum, sectionId);
201:                 else
202:                  b = p.fetchBody(seqnum, sectionId);
203:•                if (b != null)
204:                  is = b.getByteArrayInputStream();
205:          } catch (ConnectionException cex) {
206:                 throw new FolderClosedException(
207:                         message.getFolder(), cex.getMessage());
208:          } catch (ProtocolException pex) {
209:                 throw new MessagingException(pex.getMessage(), pex);
210:          }
211:         }
212:
213:•        if (is == null) {
214:          message.forceCheckExpunged(); // may throw MessageRemovedException
215:          // nope, the server doesn't think it's expunged.
216:          // can't tell the difference between the server returning NIL
217:          // and some other error that caused null to be returned above,
218:          // so we'll just assume it was empty content.
219:          is = new ByteArrayInputStream(new byte[0]);
220:         }
221:         return is;
222: }
223:
224: /**
225: * Return the MIME format stream of headers for this body part.
226: */
227: private InputStream getHeaderStream() throws MessagingException {
228:•        if (!message.isREV1())
229:          loadHeaders();        // will be needed below
230:
231:         // Acquire MessageCacheLock, to freeze seqnum.
232:         synchronized(message.getMessageCacheLock()) {
233:          try {
234:                 IMAPProtocol p = message.getProtocol();
235:
236:                 // Check whether this message got expunged
237:                 message.checkExpunged();
238:
239:•                if (p.isREV1()) {
240:                  int seqnum = message.getSequenceNumber();
241:                  BODY b = p.peekBody(seqnum, sectionId + ".MIME");
242:
243:•                 if (b == null)
244:                         throw new MessagingException("Failed to fetch headers");
245:
246:                  ByteArrayInputStream bis = b.getByteArrayInputStream();
247:•                 if (bis == null)
248:                         throw new MessagingException("Failed to fetch headers");
249:                  return bis;
250:
251:                 } else {
252:                  // Can't read it from server, have to fake it
253:                  SharedByteArrayOutputStream bos =
254:                         new SharedByteArrayOutputStream(0);
255:                  LineOutputStream los = new LineOutputStream(bos);
256:
257:                  try {
258:                         // Write out the header
259:                         Enumeration<String> hdrLines
260:                                 = super.getAllHeaderLines();
261:•                        while (hdrLines.hasMoreElements())
262:                          los.writeln(hdrLines.nextElement());
263:
264:                         // The CRLF separator between header and content
265:                         los.writeln();
266:                  } catch (IOException ioex) {
267:                         // should never happen
268:                  } finally {
269:                         try {
270:                          los.close();
271:                         } catch (IOException cex) { }
272:                  }
273:                  return bos.toStream();
274:                 }
275:          } catch (ConnectionException cex) {
276:                 throw new FolderClosedException(
277:                          message.getFolder(), cex.getMessage());
278:          } catch (ProtocolException pex) {
279:                 throw new MessagingException(pex.getMessage(), pex);
280:          }
281:         }
282: }
283:
284: /**
285: * Return the MIME format stream corresponding to this message part.
286: *
287: * @return        the MIME format stream
288: * @since        JavaMail 1.4.5
289: */
290: @Override
291: public InputStream getMimeStream() throws MessagingException {
292:         /*
293:          * The IMAP protocol doesn't support returning the entire
294:          * part content in one operation so we have to fake it by
295:          * concatenating the header stream and the content stream.
296:          */
297:         return new SequenceInputStream(getHeaderStream(), getContentStream());
298: }
299:         
300: @Override
301: public synchronized DataHandler getDataHandler()
302:                 throws MessagingException {
303:•        if (dh == null) {
304:•         if (bs.isMulti())
305:                 dh = new DataHandler(
306:                         new IMAPMultipartDataSource(
307:                                 this, bs.bodies, sectionId, message)
308:                  );
309:•         else if (bs.isNested() && message.isREV1() && bs.envelope != null)
310:                 dh = new DataHandler(
311:                         new IMAPNestedMessage(message,
312:                                          bs.bodies[0],
313:                                          bs.envelope,
314:                                          sectionId),
315:                         type
316:                  );
317:         }
318:
319:         return super.getDataHandler();
320: }
321:
322: @Override
323: public void setDataHandler(DataHandler content) throws MessagingException {
324:         throw new IllegalWriteException("IMAPBodyPart is read-only");
325: }
326:
327: @Override
328: public void setContent(Object o, String type) throws MessagingException {
329:         throw new IllegalWriteException("IMAPBodyPart is read-only");
330: }
331:
332: @Override
333: public void setContent(Multipart mp) throws MessagingException {
334:         throw new IllegalWriteException("IMAPBodyPart is read-only");
335: }
336:
337: @Override
338: public String[] getHeader(String name) throws MessagingException {
339:         loadHeaders();
340:         return super.getHeader(name);
341: }
342:
343: @Override
344: public void setHeader(String name, String value)
345:                 throws MessagingException {
346:         throw new IllegalWriteException("IMAPBodyPart is read-only");
347: }
348:
349: @Override
350: public void addHeader(String name, String value)
351:                 throws MessagingException {
352:         throw new IllegalWriteException("IMAPBodyPart is read-only");
353: }
354:
355: @Override
356: public void removeHeader(String name) throws MessagingException {
357:         throw new IllegalWriteException("IMAPBodyPart is read-only");
358: }
359:
360: @Override
361: public Enumeration<Header> getAllHeaders() throws MessagingException {
362:         loadHeaders();
363:         return super.getAllHeaders();
364: }
365:
366: @Override
367: public Enumeration<Header> getMatchingHeaders(String[] names)
368:                 throws MessagingException {
369:         loadHeaders();
370:         return super.getMatchingHeaders(names);
371: }
372:
373: @Override
374: public Enumeration<Header> getNonMatchingHeaders(String[] names)
375:                 throws MessagingException {
376:         loadHeaders();
377:         return super.getNonMatchingHeaders(names);
378: }
379:
380: @Override
381: public void addHeaderLine(String line) throws MessagingException {
382:         throw new IllegalWriteException("IMAPBodyPart is read-only");
383: }
384:
385: @Override
386: public Enumeration<String> getAllHeaderLines() throws MessagingException {
387:         loadHeaders();
388:         return super.getAllHeaderLines();
389: }
390:
391: @Override
392: public Enumeration<String> getMatchingHeaderLines(String[] names)
393:                 throws MessagingException {
394:         loadHeaders();
395:         return super.getMatchingHeaderLines(names);
396: }
397:
398: @Override
399: public Enumeration<String> getNonMatchingHeaderLines(String[] names)
400:                 throws MessagingException {
401:         loadHeaders();
402:         return super.getNonMatchingHeaderLines(names);
403: }
404:
405: private synchronized void loadHeaders() throws MessagingException {
406:•        if (headersLoaded)
407:          return;
408:
409:         // "headers" should never be null since it's set in the constructor.
410:         // If something did go wrong this will fix it, but is an unsynchronized
411:         // assignment of "headers".
412:•        if (headers == null)
413:          headers = new InternetHeaders();
414:
415:         // load headers
416:
417:         // Acquire MessageCacheLock, to freeze seqnum.
418:         synchronized(message.getMessageCacheLock()) {
419:          try {
420:                 IMAPProtocol p = message.getProtocol();
421:
422:                 // Check whether this message got expunged
423:                 message.checkExpunged();
424:
425:•                if (p.isREV1()) {
426:                  int seqnum = message.getSequenceNumber();
427:                  BODY b = p.peekBody(seqnum, sectionId + ".MIME");
428:
429:•                 if (b == null)
430:                         throw new MessagingException("Failed to fetch headers");
431:
432:                  ByteArrayInputStream bis = b.getByteArrayInputStream();
433:•                 if (bis == null)
434:                         throw new MessagingException("Failed to fetch headers");
435:
436:                  headers.load(bis);
437:
438:                 } else {
439:
440:                  // RFC 1730 does not provide for fetching BodyPart headers
441:                  // So, just dump the RFC1730 BODYSTRUCTURE into the
442:                  // headerStore
443:                 
444:                  // Content-Type
445:                  headers.addHeader("Content-Type", type);
446:                  // Content-Transfer-Encoding
447:                  headers.addHeader("Content-Transfer-Encoding", bs.encoding);
448:                  // Content-Description
449:•                 if (bs.description != null)
450:                         headers.addHeader("Content-Description",
451:                                                          bs.description);
452:                  // Content-ID
453:•                 if (bs.id != null)
454:                         headers.addHeader("Content-ID", bs.id);
455:                  // Content-MD5
456:•                 if (bs.md5 != null)
457:                         headers.addHeader("Content-MD5", bs.md5);
458:                 }
459:          } catch (ConnectionException cex) {
460:                 throw new FolderClosedException(
461:                          message.getFolder(), cex.getMessage());
462:          } catch (ProtocolException pex) {
463:                 throw new MessagingException(pex.getMessage(), pex);
464:          }
465:         }
466:         headersLoaded = true;
467: }
468: }