Skip to content

Package: IMAPBodyPart

IMAPBodyPart

nameinstructionbranchcomplexitylinemethod
IMAPBodyPart(BODYSTRUCTURE, String, IMAPMessage)
M: 29 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
addHeader(String, String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
addHeaderLine(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getAllHeaderLines()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getAllHeaders()
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getContentID()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getContentMD5()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getContentStream()
M: 106 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 25 C: 0
0%
M: 1 C: 0
0%
getContentType()
M: 3 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getDataHandler()
M: 61 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 6 C: 0
0%
M: 1 C: 0
0%
getDescription()
M: 28 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 9 C: 0
0%
M: 1 C: 0
0%
getDisposition()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getEncoding()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getFileName()
M: 44 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 11 C: 0
0%
M: 1 C: 0
0%
getHeader(String)
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getHeaderStream()
M: 112 C: 0
0%
M: 10 C: 0
0%
M: 6 C: 0
0%
M: 31 C: 0
0%
M: 1 C: 0
0%
getLineCount()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getMatchingHeaderLines(String[])
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getMatchingHeaders(String[])
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getMimeStream()
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
getNonMatchingHeaderLines(String[])
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getNonMatchingHeaders(String[])
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
getSize()
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
loadHeaders()
M: 139 C: 0
0%
M: 16 C: 0
0%
M: 9 C: 0
0%
M: 34 C: 0
0%
M: 1 C: 0
0%
removeHeader(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setContent(Multipart)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setContent(Object, String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setContentMD5(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setDataHandler(DataHandler)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setDescription(String, String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setDisposition(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setFileName(String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
setHeader(String, String)
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 5 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
updateHeaders()
M: 1 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%

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: import org.eclipse.angus.mail.iap.ConnectionException;
30: import org.eclipse.angus.mail.iap.ProtocolException;
31: import org.eclipse.angus.mail.imap.protocol.BODY;
32: import org.eclipse.angus.mail.imap.protocol.BODYSTRUCTURE;
33: import org.eclipse.angus.mail.imap.protocol.IMAPProtocol;
34: import org.eclipse.angus.mail.util.LineOutputStream;
35: import org.eclipse.angus.mail.util.PropUtil;
36: import org.eclipse.angus.mail.util.ReadableMime;
37: import org.eclipse.angus.mail.util.SharedByteArrayOutputStream;
38:
39: import java.io.ByteArrayInputStream;
40: import java.io.IOException;
41: import java.io.InputStream;
42: import java.io.SequenceInputStream;
43: import java.io.UnsupportedEncodingException;
44: import java.util.Enumeration;
45:
46: /**
47: * An IMAP body part.
48: *
49: * @author John Mani
50: * @author Bill Shannon
51: */
52:
53: public class IMAPBodyPart extends MimeBodyPart implements ReadableMime {
54: private IMAPMessage message;
55: private BODYSTRUCTURE bs;
56: private String sectionId;
57:
58: // processed values ..
59: private String type;
60: private String description;
61:
62: private boolean headersLoaded = false;
63:
64: private static final boolean decodeFileName =
65: PropUtil.getBooleanSystemProperty("mail.mime.decodefilename", false);
66:
67: protected IMAPBodyPart(BODYSTRUCTURE bs, String sid, IMAPMessage message) {
68: super();
69: this.bs = bs;
70: this.sectionId = sid;
71: this.message = message;
72: // generate content-type
73: ContentType ct = new ContentType(bs.type, bs.subtype, bs.cParams);
74: type = ct.toString();
75: }
76:
77: /* Override this method to make it a no-op, rather than throw
78: * an IllegalWriteException. This will permit IMAPBodyParts to
79: * be inserted in newly crafted MimeMessages, especially when
80: * forwarding or replying to messages.
81: */
82: @Override
83: protected void updateHeaders() {
84: return;
85: }
86:
87: @Override
88: public int getSize() throws MessagingException {
89: return bs.size;
90: }
91:
92: @Override
93: public int getLineCount() throws MessagingException {
94: return bs.lines;
95: }
96:
97: @Override
98: public String getContentType() throws MessagingException {
99: return type;
100: }
101:
102: @Override
103: public String getDisposition() throws MessagingException {
104: return bs.disposition;
105: }
106:
107: @Override
108: public void setDisposition(String disposition) throws MessagingException {
109: throw new IllegalWriteException("IMAPBodyPart is read-only");
110: }
111:
112: @Override
113: public String getEncoding() throws MessagingException {
114: return bs.encoding;
115: }
116:
117: @Override
118: public String getContentID() throws MessagingException {
119: return bs.id;
120: }
121:
122: @Override
123: public String getContentMD5() throws MessagingException {
124: return bs.md5;
125: }
126:
127: @Override
128: public void setContentMD5(String md5) throws MessagingException {
129: throw new IllegalWriteException("IMAPBodyPart is read-only");
130: }
131:
132: @Override
133: public String getDescription() throws MessagingException {
134:• if (description != null) // cached value ?
135: return description;
136:
137:• if (bs.description == null)
138: return null;
139:
140: try {
141: description = MimeUtility.decodeText(bs.description);
142: } catch (UnsupportedEncodingException ex) {
143: description = bs.description;
144: }
145:
146: return description;
147: }
148:
149: @Override
150: public void setDescription(String description, String charset)
151: throws MessagingException {
152: throw new IllegalWriteException("IMAPBodyPart is read-only");
153: }
154:
155: @Override
156: public String getFileName() throws MessagingException {
157: String filename = null;
158:• if (bs.dParams != null)
159: filename = bs.dParams.get("filename");
160:• if ((filename == null || filename.isEmpty()) && bs.cParams != null)
161: filename = bs.cParams.get("name");
162:• if (decodeFileName && filename != null) {
163: try {
164: filename = MimeUtility.decodeText(filename);
165: } catch (UnsupportedEncodingException ex) {
166: throw new MessagingException("Can't decode filename", ex);
167: }
168: }
169: return filename;
170: }
171:
172: @Override
173: public void setFileName(String filename) throws MessagingException {
174: throw new IllegalWriteException("IMAPBodyPart is read-only");
175: }
176:
177: @Override
178: protected InputStream getContentStream() throws MessagingException {
179: InputStream is = null;
180: boolean pk = message.getPeek(); // acquire outside of message cache lock
181:
182: // Acquire MessageCacheLock, to freeze seqnum.
183: synchronized (message.getMessageCacheLock()) {
184: try {
185: IMAPProtocol p = message.getProtocol();
186:
187: // Check whether this message is expunged
188: message.checkExpunged();
189:
190:• if (p.isREV1() && (message.getFetchBlockSize() != -1))
191: return new IMAPInputStream(message, sectionId,
192:• message.ignoreBodyStructureSize() ? -1 : bs.size, pk);
193:
194: // Else, vanila IMAP4, no partial fetch
195:
196: int seqnum = message.getSequenceNumber();
197: BODY b;
198:• if (pk)
199: b = p.peekBody(seqnum, sectionId);
200: else
201: b = p.fetchBody(seqnum, sectionId);
202:• if (b != null)
203: is = b.getByteArrayInputStream();
204: } catch (ConnectionException cex) {
205: throw new FolderClosedException(
206: message.getFolder(), cex.getMessage());
207: } catch (ProtocolException pex) {
208: throw new MessagingException(pex.getMessage(), pex);
209: }
210: }
211:
212:• if (is == null) {
213: message.forceCheckExpunged(); // may throw MessageRemovedException
214: // nope, the server doesn't think it's expunged.
215: // can't tell the difference between the server returning NIL
216: // and some other error that caused null to be returned above,
217: // so we'll just assume it was empty content.
218: is = new ByteArrayInputStream(new byte[0]);
219: }
220: return is;
221: }
222:
223: /**
224: * Return the MIME format stream of headers for this body part.
225: */
226: private InputStream getHeaderStream() throws MessagingException {
227:• if (!message.isREV1())
228: loadHeaders(); // will be needed below
229:
230: // Acquire MessageCacheLock, to freeze seqnum.
231: synchronized (message.getMessageCacheLock()) {
232: try {
233: IMAPProtocol p = message.getProtocol();
234:
235: // Check whether this message got expunged
236: message.checkExpunged();
237:
238:• if (p.isREV1()) {
239: int seqnum = message.getSequenceNumber();
240: BODY b = p.peekBody(seqnum, sectionId + ".MIME");
241:
242:• if (b == null)
243: throw new MessagingException("Failed to fetch headers");
244:
245: ByteArrayInputStream bis = b.getByteArrayInputStream();
246:• if (bis == null)
247: throw new MessagingException("Failed to fetch headers");
248: return bis;
249:
250: } else {
251: // Can't read it from server, have to fake it
252: SharedByteArrayOutputStream bos =
253: new SharedByteArrayOutputStream(0);
254: LineOutputStream los = new LineOutputStream(bos);
255:
256: try {
257: // Write out the header
258: Enumeration<String> hdrLines
259: = super.getAllHeaderLines();
260:• while (hdrLines.hasMoreElements())
261: los.writeln(hdrLines.nextElement());
262:
263: // The CRLF separator between header and content
264: los.writeln();
265: } catch (IOException ioex) {
266: // should never happen
267: } finally {
268: try {
269: los.close();
270: } catch (IOException cex) {
271: }
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: }