Package: IMAPMessage$FetchProfileCondition
IMAPMessage$FetchProfileCondition
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
IMAPMessage.FetchProfileCondition(FetchProfile, FetchItem[]) |
|
|
|
|
|
||||||||||||||||||||
test(IMAPMessage) |
|
|
|
|
|
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 java.util.Date;
20: import java.io.*;
21: import java.util.*;
22:
23: import jakarta.mail.*;
24: import jakarta.mail.internet.*;
25: import jakarta.activation.*;
26:
27: import org.eclipse.angus.mail.imap.protocol.BODY;
28: import org.eclipse.angus.mail.imap.protocol.BODYSTRUCTURE;
29: import org.eclipse.angus.mail.imap.protocol.FetchItem;
30: import org.eclipse.angus.mail.imap.protocol.FetchResponse;
31: import org.eclipse.angus.mail.imap.protocol.IMAPProtocol;
32: import org.eclipse.angus.mail.imap.protocol.INTERNALDATE;
33: import org.eclipse.angus.mail.imap.protocol.Item;
34: import org.eclipse.angus.mail.imap.protocol.MODSEQ;
35: import org.eclipse.angus.mail.imap.protocol.RFC822DATA;
36: import org.eclipse.angus.mail.imap.protocol.RFC822SIZE;
37: import org.eclipse.angus.mail.imap.protocol.UID;
38: import org.eclipse.angus.mail.util.ReadableMime;
39: import org.eclipse.angus.mail.iap.*;
40: import org.eclipse.angus.mail.imap.protocol.*;
41: import org.eclipse.angus.mail.iap.ConnectionException;
42: import org.eclipse.angus.mail.iap.ProtocolException;
43: import org.eclipse.angus.mail.iap.Response;
44: import org.eclipse.angus.mail.imap.protocol.ENVELOPE;
45:
46: /**
47: * This class implements an IMAPMessage object. <p>
48: *
49: * An IMAPMessage object starts out as a light-weight object. It gets
50: * filled-in incrementally when a request is made for some item. Or
51: * when a prefetch is done using the FetchProfile. <p>
52: *
53: * An IMAPMessage has a messageNumber and a sequenceNumber. The
54: * messageNumber is its index into its containing folder's messageCache.
55: * The sequenceNumber is its IMAP sequence-number.
56: *
57: * @author John Mani
58: * @author Bill Shannon
59: */
60: /*
61: * The lock hierarchy is that the lock on the IMAPMessage object, if
62: * it's acquired at all, must be acquired before the message cache lock.
63: * The IMAPMessage lock protects the message flags, sort of.
64: *
65: * XXX - I'm not convinced that all fields of IMAPMessage are properly
66: * protected by locks.
67: */
68:
69: public class IMAPMessage extends MimeMessage implements ReadableMime {
70: protected BODYSTRUCTURE bs;                // BODYSTRUCTURE
71: protected ENVELOPE envelope;        // ENVELOPE
72:
73: /**
74: * A map of the extension FETCH items. In addition to saving the
75: * data in this map, an entry in this map indicates that we *have*
76: * the data, and so it doesn't need to be fetched again. The map
77: * is created only when needed, to avoid significantly increasing
78: * the effective size of an IMAPMessage object.
79: *
80: * @since JavaMail 1.4.6
81: */
82: protected Map<String, Object> items;                // Map<String,Object>
83:
84: private Date receivedDate;                // INTERNALDATE
85: private long size = -1;                // RFC822.SIZE
86:
87: private Boolean peek;                // use BODY.PEEK when fetching content?
88:
89: // this message's IMAP UID
90: private volatile long uid = -1;
91:
92: // this message's IMAP MODSEQ - RFC 4551 CONDSTORE
93: private volatile long modseq = -1;
94:
95: // this message's IMAP sectionId (null for toplevel message,
96: //         non-null for a nested message)
97: protected String sectionId;
98:
99: // processed values
100: private String type;                // Content-Type (with params)
101: private String subject;                // decoded (Unicode) subject
102: private String description;                // decoded (Unicode) desc
103:
104: // Indicates that we've loaded *all* headers for this message
105: private volatile boolean headersLoaded = false;
106:
107: // Indicates that we've cached the body of this message
108: private volatile boolean bodyLoaded = false;
109:
110: /* Hashtable of names of headers we've loaded from the server.
111: * Used in isHeaderLoaded() and getHeaderLoaded() to keep track
112: * of those headers we've attempted to load from the server. We
113: * need this table of names to avoid multiple attempts at loading
114: * headers that don't exist for a particular message.
115: *
116: * Could this somehow be included in the InternetHeaders object ??
117: */
118: private Hashtable<String, String> loadedHeaders
119:          = new Hashtable<>(1);
120:
121: // This is our Envelope
122: static final String EnvelopeCmd = "ENVELOPE INTERNALDATE RFC822.SIZE";
123:
124: /**
125: * Constructor.
126: *
127: * @param        folder        the folder containing this message
128: * @param        msgnum        the message sequence number
129: */
130: protected IMAPMessage(IMAPFolder folder, int msgnum) {
131:         super(folder, msgnum);
132:         flags = null;
133: }
134:
135: /**
136: * Constructor, for use by IMAPNestedMessage.
137: *
138: * @param        session        the Session
139: */
140: protected IMAPMessage(Session session) {
141:         super(session);
142: }
143:
144: /**
145: * Get this message's folder's protocol connection.
146: * Throws FolderClosedException, if the protocol connection
147: * is not available.
148: *
149: * ASSERT: Must hold the messageCacheLock.
150: *
151: * @return        the IMAPProtocol object for the containing folder
152: * @exception ProtocolException for protocol errors
153: * @exception        FolderClosedException if the folder is closed
154: */
155: protected IMAPProtocol getProtocol()
156:                          throws ProtocolException, FolderClosedException {
157:         ((IMAPFolder)folder).waitIfIdle();
158:         IMAPProtocol p = ((IMAPFolder)folder).protocol;
159:         if (p == null)
160:          throw new FolderClosedException(folder);
161:         else
162:          return p;
163: }
164:
165: /*
166: * Is this an IMAP4 REV1 server?
167: */
168: protected boolean isREV1() throws FolderClosedException {
169:         // access the folder's protocol object without waiting
170:         // for IDLE to complete
171:         IMAPProtocol p = ((IMAPFolder)folder).protocol;
172:         if (p == null)
173:          throw new FolderClosedException(folder);
174:         else
175:          return p.isREV1();
176: }
177:
178: /**
179: * Get the messageCacheLock, associated with this Message's
180: * Folder.
181: *
182: * @return        the message cache lock object
183: */
184: protected Object getMessageCacheLock() {
185:         return ((IMAPFolder)folder).messageCacheLock;
186: }
187:
188: /**
189: * Get this message's IMAP sequence number.
190: *
191: * ASSERT: This method must be called only when holding the
192: *         messageCacheLock.
193: *
194: * @return        the message sequence number
195: */
196: protected int getSequenceNumber() {
197:         return ((IMAPFolder)folder).messageCache.seqnumOf(getMessageNumber());
198: }
199:
200: /**
201: * Wrapper around the protected method Message.setMessageNumber() to
202: * make that method accessible to IMAPFolder.
203: */
204: @Override
205: protected void setMessageNumber(int msgnum) {
206:         super.setMessageNumber(msgnum);
207: }
208:
209: /**
210: * Return the UID for this message.
211: * Returns -1 if not known; use UIDFolder.getUID() in this case.
212: *
213: * @return        the UID
214: * @see        jakarta.mail.UIDFolder#getUID
215: */
216: protected long getUID() {
217:         return uid;
218: }
219:
220: protected void setUID(long uid) {
221:         this.uid = uid;
222: }
223:
224: /**
225: * Return the modification sequence number (MODSEQ) for this message.
226: * Returns -1 if not known.
227: *
228: * @return        the modification sequence number
229: * @exception        MessagingException for failures
230: * @see        "RFC 4551"
231: * @since        JavaMail 1.5.1
232: */
233: public synchronized long getModSeq() throws MessagingException {
234:         if (modseq != -1)
235:          return modseq;
236:
237:         synchronized (getMessageCacheLock()) { // Acquire Lock
238:          try {
239:                 IMAPProtocol p = getProtocol();
240:                 checkExpunged(); // insure that message is not expunged
241:                 MODSEQ ms = p.fetchMODSEQ(getSequenceNumber());
242:
243:                 if (ms != null)
244:                  modseq = ms.modseq;
245:          } catch (ConnectionException cex) {
246:                 throw new FolderClosedException(folder, cex.getMessage());
247:          } catch (ProtocolException pex) {
248:                 throw new MessagingException(pex.getMessage(), pex);
249:          }
250:         }
251:         return modseq;
252: }
253:
254: long _getModSeq() {
255:         return modseq;
256: }
257:
258: void setModSeq(long modseq) {
259:         this.modseq = modseq;
260: }
261:
262: // expose to MessageCache
263: @Override
264: protected void setExpunged(boolean set) {
265:         super.setExpunged(set);
266: }
267:
268: // Convenience routine
269: protected void checkExpunged() throws MessageRemovedException {
270:         if (expunged)
271:          throw new MessageRemovedException();
272: }
273:
274: /**
275: * Do a NOOP to force any untagged EXPUNGE responses
276: * and then check if this message is expunged.
277: *
278: * @exception        MessageRemovedException if the message has been removed
279: * @exception        FolderClosedException if the folder has been closed
280: */
281: protected void forceCheckExpunged()
282:                         throws MessageRemovedException, FolderClosedException {
283:         synchronized (getMessageCacheLock()) {
284:          try {
285:                 getProtocol().noop();
286:          } catch (ConnectionException cex) {
287:                 throw new FolderClosedException(folder, cex.getMessage());
288:          } catch (ProtocolException pex) {
289:                 // ignore it
290:          }
291:         }
292:         if (expunged)
293:          throw new MessageRemovedException();
294: }
295:
296: // Return the block size for FETCH requests
297: // MUST be overridden by IMAPNestedMessage
298: protected int getFetchBlockSize() {
299:         return ((IMAPStore)folder.getStore()).getFetchBlockSize();
300: }
301:
302: // Should we ignore the size in the BODYSTRUCTURE?
303: // MUST be overridden by IMAPNestedMessage
304: protected boolean ignoreBodyStructureSize() {
305:         return ((IMAPStore)folder.getStore()).ignoreBodyStructureSize();
306: }
307:
308: /**
309: * Get the "From" attribute.
310: */
311: @Override
312: public Address[] getFrom() throws MessagingException {
313:         checkExpunged();
314:         if (bodyLoaded)
315:          return super.getFrom();
316:         loadEnvelope();
317:         InternetAddress[] a = envelope.from;
318:         /*
319:          * Per RFC 2822, the From header is required, and thus the IMAP
320:          * spec also requires that it be present, but we know that in
321:          * practice it is often missing. Some servers fill in the
322:          * From field with the Sender field in this case, but at least
323:          * Exchange 2007 does not. Use the same fallback strategy used
324:          * by MimeMessage.
325:          */
326:         if (a == null || a.length == 0)
327:          a = envelope.sender;
328:         return aaclone(a);
329: }
330:
331: @Override
332: public void setFrom(Address address) throws MessagingException {
333:         throw new IllegalWriteException("IMAPMessage is read-only");
334: }
335:
336: @Override
337: public void addFrom(Address[] addresses) throws MessagingException {
338:         throw new IllegalWriteException("IMAPMessage is read-only");
339: }
340:
341: /**
342: * Get the "Sender" attribute.
343: */
344: @Override
345: public Address getSender() throws MessagingException {
346:         checkExpunged();
347:         if (bodyLoaded)
348:          return super.getSender();
349:         loadEnvelope();
350:         if (envelope.sender != null && envelope.sender.length > 0)
351:                 return (envelope.sender)[0];        // there can be only one sender
352:         else
353:                 return null;
354: }
355:         
356:
357: @Override
358: public void setSender(Address address) throws MessagingException {
359:         throw new IllegalWriteException("IMAPMessage is read-only");
360: }
361:
362: /**
363: * Get the desired Recipient type.
364: */
365: @Override
366: public Address[] getRecipients(Message.RecipientType type)
367:                                 throws MessagingException {
368:         checkExpunged();
369:         if (bodyLoaded)
370:          return super.getRecipients(type);
371:         loadEnvelope();
372:
373:         if (type == Message.RecipientType.TO)
374:          return aaclone(envelope.to);
375:         else if (type == Message.RecipientType.CC)
376:          return aaclone(envelope.cc);
377:         else if (type == Message.RecipientType.BCC)
378:          return aaclone(envelope.bcc);
379:         else
380:          return super.getRecipients(type);
381: }
382:
383: @Override
384: public void setRecipients(Message.RecipientType type, Address[] addresses)
385:                         throws MessagingException {
386:         throw new IllegalWriteException("IMAPMessage is read-only");
387: }
388:
389: @Override
390: public void addRecipients(Message.RecipientType type, Address[] addresses)
391:                         throws MessagingException {
392:         throw new IllegalWriteException("IMAPMessage is read-only");
393: }
394:
395: /**
396: * Get the ReplyTo addresses.
397: */
398: @Override
399: public Address[] getReplyTo() throws MessagingException {
400:         checkExpunged();
401:         if (bodyLoaded)
402:          return super.getReplyTo();
403:         loadEnvelope();
404:         /*
405:          * The IMAP spec requires that the Reply-To field never be
406:          * null, but at least Exchange 2007 fails to fill it in in
407:          * some cases. Use the same fallback strategy used by
408:          * MimeMessage.
409:          */
410:         if (envelope.replyTo == null || envelope.replyTo.length == 0)
411:          return getFrom();
412:         return aaclone(envelope.replyTo);
413: }
414:
415: @Override
416: public void setReplyTo(Address[] addresses) throws MessagingException {
417:         throw new IllegalWriteException("IMAPMessage is read-only");
418: }
419:
420: /**
421: * Get the decoded subject.
422: */
423: @Override
424: public String getSubject() throws MessagingException {
425:         checkExpunged();
426:         if (bodyLoaded)
427:          return super.getSubject();
428:
429:         if (subject != null) // already cached ?
430:          return subject;
431:
432:         loadEnvelope();
433:         if (envelope.subject == null) // no subject
434:          return null;
435:
436:         // Cache and return the decoded value.
437:         try {
438:          // The server *should* unfold the value, but just in case it
439:          // doesn't we unfold it here.
440:          subject =
441:                 MimeUtility.decodeText(MimeUtility.unfold(envelope.subject));
442:         } catch (UnsupportedEncodingException ex) {
443:          subject = envelope.subject;
444:         }
445:
446:         return subject;
447: }
448:
449: @Override
450: public void setSubject(String subject, String charset)
451:                 throws MessagingException {
452:         throw new IllegalWriteException("IMAPMessage is read-only");
453: }
454:
455: /**
456: * Get the SentDate.
457: */
458: @Override
459: public Date getSentDate() throws MessagingException {
460:         checkExpunged();
461:         if (bodyLoaded)
462:          return super.getSentDate();
463:         loadEnvelope();
464:         if (envelope.date == null)
465:          return null;
466:         else
467:          return new Date(envelope.date.getTime());
468: }
469:
470: @Override
471: public void setSentDate(Date d) throws MessagingException {
472:         throw new IllegalWriteException("IMAPMessage is read-only");
473: }
474:
475: /**
476: * Get the received date (INTERNALDATE).
477: */
478: @Override
479: public Date getReceivedDate() throws MessagingException {
480:         checkExpunged();
481:         if (receivedDate == null)
482:          loadEnvelope(); // have to go to the server for this
483:         if (receivedDate == null)
484:          return null;
485:         else
486:          return new Date(receivedDate.getTime());
487: }
488:
489: /**
490: * Get the message size. <p>
491: *
492: * Note that this returns RFC822.SIZE. That is, it's the
493: * size of the whole message, header and body included.
494: * Note also that if the size of the message is greater than
495: * Integer.MAX_VALUE (2GB), this method returns Integer.MAX_VALUE.
496: */
497: @Override
498: public int getSize() throws MessagingException {
499:         checkExpunged();
500:         // if bodyLoaded, size is already set
501:         if (size == -1)
502:          loadEnvelope();        // XXX - could just fetch the size
503:         if (size > Integer.MAX_VALUE)
504:          return Integer.MAX_VALUE;        // the best we can do...
505:         else
506:          return (int)size;
507: }
508:
509: /**
510: * Get the message size as a long. <p>
511: *
512: * Suitable for messages that might be larger than 2GB.
513: * @return        the message size as a long integer
514: * @exception        MessagingException for failures
515: * @since        JavaMail 1.6
516: */
517: public long getSizeLong() throws MessagingException {
518:         checkExpunged();
519:         // if bodyLoaded, size is already set
520:         if (size == -1)
521:          loadEnvelope();        // XXX - could just fetch the size
522:         return size;
523: }
524:
525: /**
526: * Get the total number of lines. <p>
527: *
528: * Returns the "body_fld_lines" field from the
529: * BODYSTRUCTURE. Note that this field is available
530: * only for text/plain and message/rfc822 types
531: */
532: @Override
533: public int getLineCount() throws MessagingException {
534:         checkExpunged();
535:         // XXX - superclass doesn't implement this
536:         loadBODYSTRUCTURE();
537:         return bs.lines;
538: }
539:
540: /**
541: * Get the content language.
542: */
543: @Override
544: public String[] getContentLanguage() throws MessagingException {
545:         checkExpunged();
546:         if (bodyLoaded)
547:          return super.getContentLanguage();
548:         loadBODYSTRUCTURE();
549:         if (bs.language != null)
550:          return bs.language.clone();
551:         else
552:          return null;
553: }
554:
555: @Override
556: public void setContentLanguage(String[] languages)
557:                                 throws MessagingException {
558:         throw new IllegalWriteException("IMAPMessage is read-only");
559: }
560:
561: /**
562: * Get the In-Reply-To header.
563: *
564: * @return        the In-Reply-To header
565: * @exception        MessagingException for failures
566: * @since        JavaMail 1.3.3
567: */
568: public String getInReplyTo() throws MessagingException {
569:         checkExpunged();
570:         if (bodyLoaded)
571:          return super.getHeader("In-Reply-To", " ");
572:         loadEnvelope();
573:         return envelope.inReplyTo;
574: }
575:
576: /**
577: * Get the Content-Type.
578: *
579: * Generate this header from the BODYSTRUCTURE. Append parameters
580: * as well.
581: */
582: @Override
583: public synchronized String getContentType() throws MessagingException {
584:         checkExpunged();
585:         if (bodyLoaded)
586:          return super.getContentType();
587:
588:         // If we haven't cached the type yet ..
589:         if (type == null) {
590:          loadBODYSTRUCTURE();
591:          // generate content-type from BODYSTRUCTURE
592:          ContentType ct = new ContentType(bs.type, bs.subtype, bs.cParams);
593:          type = ct.toString();
594:         }
595:         return type;
596: }
597:
598: /**
599: * Get the Content-Disposition.
600: */
601: @Override
602: public String getDisposition() throws MessagingException {
603:         checkExpunged();
604:         if (bodyLoaded)
605:          return super.getDisposition();
606:         loadBODYSTRUCTURE();
607:         return bs.disposition;
608: }
609:
610: @Override
611: public void setDisposition(String disposition) throws MessagingException {
612:         throw new IllegalWriteException("IMAPMessage is read-only");
613: }
614:
615: /**
616: * Get the Content-Transfer-Encoding.
617: */
618: @Override
619: public String getEncoding() throws MessagingException {
620:         checkExpunged();
621:         if (bodyLoaded)
622:          return super.getEncoding();
623:         loadBODYSTRUCTURE();
624:         return bs.encoding;
625: }
626:
627: /**
628: * Get the Content-ID.
629: */
630: @Override
631: public String getContentID() throws MessagingException {
632:         checkExpunged();
633:         if (bodyLoaded)
634:          return super.getContentID();
635:         loadBODYSTRUCTURE();
636:         return bs.id;
637: }
638:
639: @Override
640: public void setContentID(String cid) throws MessagingException {
641:         throw new IllegalWriteException("IMAPMessage is read-only");
642: }
643:
644: /**
645: * Get the Content-MD5.
646: */
647: @Override
648: public String getContentMD5() throws MessagingException {
649:         checkExpunged();
650:         if (bodyLoaded)
651:          return super.getContentMD5();
652:         loadBODYSTRUCTURE();
653:         return bs.md5;
654: }
655:
656: @Override
657: public void setContentMD5(String md5) throws MessagingException {
658:         throw new IllegalWriteException("IMAPMessage is read-only");
659: }
660:
661: /**
662: * Get the decoded Content-Description.
663: */
664: @Override
665: public String getDescription() throws MessagingException {
666:         checkExpunged();
667:         if (bodyLoaded)
668:          return super.getDescription();
669:
670:         if (description != null) // cached value ?
671:          return description;
672:         
673:         loadBODYSTRUCTURE();
674:         if (bs.description == null)
675:          return null;
676:         
677:         try {
678:          description = MimeUtility.decodeText(bs.description);
679:         } catch (UnsupportedEncodingException ex) {
680:          description = bs.description;
681:         }
682:
683:         return description;
684: }
685:
686: @Override
687: public void setDescription(String description, String charset)
688:                         throws MessagingException {
689:         throw new IllegalWriteException("IMAPMessage is read-only");
690: }
691:
692: /**
693: * Get the Message-ID.
694: */
695: @Override
696: public String getMessageID() throws MessagingException {
697:         checkExpunged();
698:         if (bodyLoaded)
699:          return super.getMessageID();
700:         loadEnvelope();
701:         return envelope.messageId;
702: }
703:
704: /**
705: * Get the "filename" Disposition parameter. (Only available in
706: * IMAP4rev1). If thats not available, get the "name" ContentType
707: * parameter.
708: */
709: @Override
710: public String getFileName() throws MessagingException {
711:         checkExpunged();
712:         if (bodyLoaded)
713:          return super.getFileName();
714:
715:         String filename = null;
716:         loadBODYSTRUCTURE();
717:
718:         if (bs.dParams != null)
719:          filename = bs.dParams.get("filename");
720:         if (filename == null && bs.cParams != null)
721:          filename = bs.cParams.get("name");
722:         return filename;
723: }
724:
725: @Override
726: public void setFileName(String filename) throws MessagingException {
727:         throw new IllegalWriteException("IMAPMessage is read-only");
728: }
729:
730: /**
731: * Get all the bytes for this message. Overrides getContentStream()
732: * in MimeMessage. This method is ultimately used by the DataHandler
733: * to obtain the input stream for this message.
734: *
735: * @see jakarta.mail.internet.MimeMessage#getContentStream
736: */
737: @Override
738: protected InputStream getContentStream() throws MessagingException {
739:         if (bodyLoaded)
740:          return super.getContentStream();
741:         InputStream is = null;
742:         boolean pk = getPeek();        // get before acquiring message cache lock
743:
744: // Acquire MessageCacheLock, to freeze seqnum.
745: synchronized(getMessageCacheLock()) {
746:          try {
747:                 IMAPProtocol p = getProtocol();
748:
749:                 // This message could be expunged when we were waiting
750:                 // to acquire the lock ...
751:                 checkExpunged();
752:
753:                 if (p.isREV1() && (getFetchBlockSize() != -1)) // IMAP4rev1
754:                  return new IMAPInputStream(this, toSection("TEXT"),
755:                                  bs != null && !ignoreBodyStructureSize() ?
756:                                         bs.size : -1, pk);
757:
758:                 if (p.isREV1()) {
759:                  BODY b;
760:                  if (pk)
761:                         b = p.peekBody(getSequenceNumber(), toSection("TEXT"));
762:                  else
763:                         b = p.fetchBody(getSequenceNumber(), toSection("TEXT"));
764:                  if (b != null)
765:                         is = b.getByteArrayInputStream();
766:                 } else {
767:                  RFC822DATA rd = p.fetchRFC822(getSequenceNumber(), "TEXT");
768:                  if (rd != null)
769:                         is = rd.getByteArrayInputStream();
770:                 }
771:          } catch (ConnectionException cex) {
772:                 throw new FolderClosedException(folder, cex.getMessage());
773:          } catch (ProtocolException pex) {
774:                 forceCheckExpunged();
775:                 throw new MessagingException(pex.getMessage(), pex);
776:          }
777:         }
778:
779:         if (is == null) {
780:          forceCheckExpunged();        // may throw MessageRemovedException
781:          // nope, the server doesn't think it's expunged.
782:          // can't tell the difference between the server returning NIL
783:          // and some other error that caused null to be returned above,
784:          // so we'll just assume it was empty content.
785:          is = new ByteArrayInputStream(new byte[0]);
786:         }
787:         return is;
788: }
789:
790: /**
791: * Get the DataHandler object for this message.
792: */
793: @Override
794: public synchronized DataHandler getDataHandler()
795:                 throws MessagingException {
796:         checkExpunged();
797:
798:         if (dh == null && !bodyLoaded) {
799:          loadBODYSTRUCTURE();
800:          if (type == null) { // type not yet computed
801:                 // generate content-type from BODYSTRUCTURE
802:                 ContentType ct = new ContentType(bs.type, bs.subtype,
803:                                                  bs.cParams);
804:                 type = ct.toString();
805:          }
806:
807:          /* Special-case Multipart and Nested content. All other
808:          * cases are handled by the superclass.
809:          */
810:          if (bs.isMulti())
811:                 dh = new DataHandler(
812:                         new IMAPMultipartDataSource(this, bs.bodies,
813:                                                  sectionId, this)
814:                  );
815:          else if (bs.isNested() && isREV1() && bs.envelope != null)
816:                 /* Nested messages are handled specially only for
817:                  * IMAP4rev1. IMAP4 doesn't provide enough support to
818:                  * FETCH the components of nested messages
819:                  */
820:                 dh = new DataHandler(
821:                          new IMAPNestedMessage(this,
822:                                 bs.bodies[0],
823:                                 bs.envelope,
824:                                 sectionId == null ? "1" : sectionId + ".1"),
825:                          type
826:                  );
827:         }
828:
829:         return super.getDataHandler();
830: }
831:
832: @Override
833: public void setDataHandler(DataHandler content)
834:                         throws MessagingException {
835:         throw new IllegalWriteException("IMAPMessage is read-only");
836: }
837:
838: /**
839: * Return the MIME format stream corresponding to this message.
840: *
841: * @return        the MIME format stream
842: * @since        JavaMail 1.4.5
843: */
844: @Override
845: public InputStream getMimeStream() throws MessagingException {
846:         // XXX - need an "if (bodyLoaded)" version
847:         InputStream is = null;
848:         boolean pk = getPeek();        // get before acquiring message cache lock
849:
850: // Acquire MessageCacheLock, to freeze seqnum.
851: synchronized(getMessageCacheLock()) {
852:          try {
853:                 IMAPProtocol p = getProtocol();
854:
855:                 checkExpunged(); // insure this message is not expunged
856:
857:                 if (p.isREV1() && (getFetchBlockSize() != -1)) // IMAP4rev1
858:                  return new IMAPInputStream(this, sectionId, -1, pk);
859:
860:                 if (p.isREV1()) {
861:                  BODY b;
862:                  if (pk)
863:                         b = p.peekBody(getSequenceNumber(), sectionId);
864:                  else
865:                         b = p.fetchBody(getSequenceNumber(), sectionId);
866:                  if (b != null)
867:                         is = b.getByteArrayInputStream();
868:                 } else {
869:                  RFC822DATA rd = p.fetchRFC822(getSequenceNumber(), null);
870:                  if (rd != null)
871:                         is = rd.getByteArrayInputStream();
872:                 }
873:          } catch (ConnectionException cex) {
874:                 throw new FolderClosedException(folder, cex.getMessage());
875:          } catch (ProtocolException pex) {
876:                 forceCheckExpunged();
877:                 throw new MessagingException(pex.getMessage(), pex);
878:          }
879:         }
880:
881:         if (is == null) {
882:          forceCheckExpunged();        // may throw MessageRemovedException
883:          // nope, the server doesn't think it's expunged.
884:          // can't tell the difference between the server returning NIL
885:          // and some other error that caused null to be returned above,
886:          // so we'll just assume it was empty content.
887:          is = new ByteArrayInputStream(new byte[0]);
888:         }
889:         return is;
890: }
891:
892: /**
893: * Write out the bytes into the given OutputStream.
894: */
895: @Override
896: public void writeTo(OutputStream os)
897:                                 throws IOException, MessagingException {
898:         if (bodyLoaded) {
899:          super.writeTo(os);
900:          return;
901:         }
902:         InputStream is = getMimeStream();
903:         try {
904:          // write out the bytes
905:          byte[] bytes = new byte[16*1024];
906:          int count;
907:          while ((count = is.read(bytes)) != -1)
908:                 os.write(bytes, 0, count);
909:         } finally {
910:          is.close();
911:         }
912: }
913:
914: /**
915: * Get the named header.
916: */
917: @Override
918: public String[] getHeader(String name) throws MessagingException {
919:         checkExpunged();
920:
921:         if (isHeaderLoaded(name)) // already loaded ?
922:          return headers.getHeader(name);
923:
924:         // Load this particular header
925:         InputStream is = null;
926:
927: // Acquire MessageCacheLock, to freeze seqnum.
928: synchronized(getMessageCacheLock()) {
929:          try {
930:                 IMAPProtocol p = getProtocol();
931:
932:                 // This message could be expunged when we were waiting
933:                 // to acquire the lock ...
934:                 checkExpunged();
935:
936:                 if (p.isREV1()) {
937:                  BODY b = p.peekBody(getSequenceNumber(),
938:                                 toSection("HEADER.FIELDS (" + name + ")")
939:                          );
940:                  if (b != null)
941:                         is = b.getByteArrayInputStream();
942:                 } else {
943:                  RFC822DATA rd = p.fetchRFC822(getSequenceNumber(),
944:                                         "HEADER.LINES (" + name + ")");
945:                  if (rd != null)
946:                         is = rd.getByteArrayInputStream();
947:                 }
948:          } catch (ConnectionException cex) {
949:                 throw new FolderClosedException(folder, cex.getMessage());
950:          } catch (ProtocolException pex) {
951:                 forceCheckExpunged();
952:                 throw new MessagingException(pex.getMessage(), pex);
953:          }
954:         }
955:
956:         // if we get this far without "is" being set, something has gone
957:         // wrong; prevent a later NullPointerException and return null here
958:         if (is == null)
959:          return null;
960:
961:         if (headers == null)
962:          headers = new InternetHeaders();
963:         headers.load(is); // load this header into the Headers object.
964:         setHeaderLoaded(name); // Mark this header as loaded
965:
966:         return headers.getHeader(name);
967: }
968:
969: /**
970: * Get the named header.
971: */
972: @Override
973: public String getHeader(String name, String delimiter)
974:                         throws MessagingException {
975:         checkExpunged();
976:
977:         // force the header to be loaded by invoking getHeader(name)
978:         if (getHeader(name) == null)
979:          return null;
980:         return headers.getHeader(name, delimiter);
981: }
982:
983: @Override
984: public void setHeader(String name, String value)
985:                         throws MessagingException {
986:         throw new IllegalWriteException("IMAPMessage is read-only");
987: }
988:
989: @Override
990: public void addHeader(String name, String value)
991:                         throws MessagingException {
992:         throw new IllegalWriteException("IMAPMessage is read-only");
993: }
994:         
995: @Override
996: public void removeHeader(String name)
997:                         throws MessagingException {
998:         throw new IllegalWriteException("IMAPMessage is read-only");
999: }
1000:
1001: /**
1002: * Get all headers.
1003: */
1004: @Override
1005: public Enumeration<Header> getAllHeaders() throws MessagingException {
1006:         checkExpunged();
1007:         loadHeaders();
1008:         return super.getAllHeaders();
1009: }
1010:
1011: /**
1012: * Get matching headers.
1013: */
1014: @Override
1015: public Enumeration<Header> getMatchingHeaders(String[] names)
1016:                         throws MessagingException {
1017:         checkExpunged();
1018:         loadHeaders();
1019:         return super.getMatchingHeaders(names);
1020: }
1021:
1022: /**
1023: * Get non-matching headers.
1024: */
1025: @Override
1026: public Enumeration<Header> getNonMatchingHeaders(String[] names)
1027:                         throws MessagingException {
1028:         checkExpunged();
1029:         loadHeaders();
1030:         return super.getNonMatchingHeaders(names);
1031: }
1032:
1033: @Override
1034: public void addHeaderLine(String line) throws MessagingException {
1035:         throw new IllegalWriteException("IMAPMessage is read-only");
1036: }
1037:
1038: /**
1039: * Get all header-lines.
1040: */
1041: @Override
1042: public Enumeration<String> getAllHeaderLines() throws MessagingException {
1043:         checkExpunged();
1044:         loadHeaders();
1045:         return super.getAllHeaderLines();
1046: }
1047:
1048: /**
1049: * Get all matching header-lines.
1050: */
1051: @Override
1052: public Enumeration<String> getMatchingHeaderLines(String[] names)
1053:                         throws MessagingException {
1054:         checkExpunged();
1055:         loadHeaders();
1056:         return super.getMatchingHeaderLines(names);
1057: }
1058:
1059: /**
1060: * Get all non-matching headerlines.
1061: */
1062: @Override
1063: public Enumeration<String> getNonMatchingHeaderLines(String[] names)
1064:                         throws MessagingException {
1065:         checkExpunged();
1066:         loadHeaders();
1067:         return super.getNonMatchingHeaderLines(names);
1068: }
1069:
1070: /**
1071: * Get the Flags for this message.
1072: */
1073: @Override
1074: public synchronized Flags getFlags() throws MessagingException {
1075:         checkExpunged();
1076:         loadFlags();
1077:         return super.getFlags();
1078: }
1079:
1080: /**
1081: * Test if the given Flags are set in this message.
1082: */
1083: @Override
1084: public synchronized boolean isSet(Flags.Flag flag)
1085:                                 throws MessagingException {
1086:         checkExpunged();
1087:         loadFlags();
1088:         return super.isSet(flag);
1089: }
1090:
1091: /**
1092: * Set/Unset the given flags in this message.
1093: */
1094: @Override
1095: public synchronized void setFlags(Flags flag, boolean set)
1096:                         throws MessagingException {
1097: // Acquire MessageCacheLock, to freeze seqnum.
1098: synchronized(getMessageCacheLock()) {
1099:          try {
1100:                 IMAPProtocol p = getProtocol();
1101:                 checkExpunged(); // Insure that this message is not expunged
1102:                 p.storeFlags(getSequenceNumber(), flag, set);
1103:          } catch (ConnectionException cex) {
1104:                 throw new FolderClosedException(folder, cex.getMessage());
1105:          } catch (ProtocolException pex) {
1106:                 throw new MessagingException(pex.getMessage(), pex);
1107:          }
1108:         }
1109: }
1110:
1111: /**
1112: * Set whether or not to use the PEEK variant of FETCH when
1113: * fetching message content. This overrides the default
1114: * value from the "mail.imap.peek" property.
1115: *
1116: * @param        peek        the peek flag
1117: * @since        JavaMail 1.3.3
1118: */
1119: public synchronized void setPeek(boolean peek) {
1120:         this.peek = Boolean.valueOf(peek);
1121: }
1122:
1123: /**
1124: * Get whether or not to use the PEEK variant of FETCH when
1125: * fetching message content.
1126: *
1127: * @return        the peek flag
1128: * @since        JavaMail 1.3.3
1129: */
1130: public synchronized boolean getPeek() {
1131:         if (peek == null)
1132:          return ((IMAPStore)folder.getStore()).getPeek();
1133:         else
1134:          return peek.booleanValue();
1135: }
1136:
1137: /**
1138: * Invalidate cached header and envelope information for this
1139: * message. Subsequent accesses of this information will
1140: * cause it to be fetched from the server.
1141: *
1142: * @since        JavaMail 1.3.3
1143: */
1144: public synchronized void invalidateHeaders() {
1145:         headersLoaded = false;
1146:         loadedHeaders.clear();
1147:         headers = null;
1148:         envelope = null;
1149:         bs = null;
1150:         receivedDate = null;
1151:         size = -1;
1152:         type = null;
1153:         subject = null;
1154:         description = null;
1155:         flags = null;
1156:         content = null;
1157:         contentStream = null;
1158:         bodyLoaded = false;
1159: }
1160:
1161: /**
1162: * This class implements the test to be done on each
1163: * message in the folder. The test is to check whether the
1164: * message has already cached all the items requested in the
1165: * FetchProfile. If any item is missing, the test succeeds and
1166: * breaks out.
1167: */
1168: public static class FetchProfileCondition implements Utility.Condition {
1169:         private boolean needEnvelope = false;
1170:         private boolean needFlags = false;
1171:         private boolean needBodyStructure = false;
1172:         private boolean needUID = false;
1173:         private boolean needHeaders = false;
1174:         private boolean needSize = false;
1175:         private boolean needMessage = false;
1176:         private boolean needRDate = false;
1177:         private String[] hdrs = null;
1178:         private Set<FetchItem> need = new HashSet<>();
1179:
1180:         /**
1181:          * Create a FetchProfileCondition to determine if we need to fetch
1182:          * any of the information specified in the FetchProfile.
1183:          *
1184:          * @param        fp        the FetchProfile
1185:          * @param        fitems        the FETCH items
1186:          */
1187:         @SuppressWarnings("deprecation")        // for FetchProfile.Item.SIZE
1188:         public FetchProfileCondition(FetchProfile fp, FetchItem[] fitems) {
1189:•         if (fp.contains(FetchProfile.Item.ENVELOPE))
1190:                 needEnvelope = true;
1191:•         if (fp.contains(FetchProfile.Item.FLAGS))
1192:                 needFlags = true;
1193:•         if (fp.contains(FetchProfile.Item.CONTENT_INFO))
1194:                 needBodyStructure = true;
1195:•         if (fp.contains(FetchProfile.Item.SIZE))
1196:                 needSize = true;
1197:•         if (fp.contains(UIDFolder.FetchProfileItem.UID))
1198:                 needUID = true;
1199:•         if (fp.contains(IMAPFolder.FetchProfileItem.HEADERS))
1200:                 needHeaders = true;
1201:•         if (fp.contains(IMAPFolder.FetchProfileItem.SIZE))
1202:                 needSize = true;
1203:•         if (fp.contains(IMAPFolder.FetchProfileItem.MESSAGE))
1204:                 needMessage = true;
1205:•         if (fp.contains(IMAPFolder.FetchProfileItem.INTERNALDATE))
1206:                 needRDate = true;
1207:          hdrs = fp.getHeaderNames();
1208:•         for (int i = 0; i < fitems.length; i++) {
1209:•                if (fp.contains(fitems[i].getFetchProfileItem()))
1210:                  need.add(fitems[i]);
1211:          }
1212:         }
1213:
1214:         /**
1215:          * Return true if we NEED to fetch the requested information
1216:          * for the specified message.
1217:          */
1218:         @Override
1219:         public boolean test(IMAPMessage m) {
1220:•         if (needEnvelope && m._getEnvelope() == null && !m.bodyLoaded)
1221:                 return true; // no envelope
1222:•         if (needFlags && m._getFlags() == null)
1223:                 return true; // no flags
1224:•         if (needBodyStructure && m._getBodyStructure() == null &&
1225:•                                                                !m.bodyLoaded)
1226:                 return true; // no BODYSTRUCTURE
1227:•         if (needUID && m.getUID() == -1)        // no UID
1228:                 return true;
1229:•         if (needHeaders && !m.areHeadersLoaded()) // no headers
1230:                 return true;
1231:•         if (needSize && m.size == -1 && !m.bodyLoaded) // no size
1232:                 return true;
1233:•         if (needMessage && !m.bodyLoaded)                // no message body
1234:                 return true;
1235:•         if (needRDate && m.receivedDate == null)        // no received date
1236:                 return true;
1237:
1238:          // Is the desired header present ?
1239:•         for (int i = 0; i < hdrs.length; i++) {
1240:•                if (!m.isHeaderLoaded(hdrs[i]))
1241:                  return true; // Nope, return
1242:          }
1243:          Iterator<FetchItem> it = need.iterator();
1244:•         while (it.hasNext()) {
1245:                 FetchItem fitem = it.next();
1246:•                if (m.items == null || m.items.get(fitem.getName()) == null)
1247:                  return true;
1248:          }
1249:
1250:          return false;
1251:         }
1252: }
1253:
1254: /**
1255: * Apply the data in the FETCH item to this message.
1256: *
1257: * ASSERT: Must hold the messageCacheLock.
1258: *
1259: * @param        item        the fetch item
1260: * @param        hdrs        the headers we're asking for
1261: * @param        allHeaders load all headers?
1262: * @return                did we handle this fetch item?
1263: * @exception        MessagingException for failures
1264: * @since JavaMail 1.4.6
1265: */
1266: protected boolean handleFetchItem(Item item,
1267:                                 String[] hdrs, boolean allHeaders)
1268:                                 throws MessagingException {
1269:         // Check for the FLAGS item
1270:         if (item instanceof Flags)
1271:          flags = (Flags)item;
1272:         // Check for ENVELOPE items
1273:         else if (item instanceof ENVELOPE)
1274:          envelope = (ENVELOPE)item;
1275:         else if (item instanceof INTERNALDATE)
1276:          receivedDate = ((INTERNALDATE)item).getDate();
1277:         else if (item instanceof RFC822SIZE)
1278:          size = ((RFC822SIZE)item).size;
1279:         else if (item instanceof MODSEQ)
1280:          modseq = ((MODSEQ)item).modseq;
1281:
1282:         // Check for the BODYSTRUCTURE item
1283:         else if (item instanceof BODYSTRUCTURE)
1284:          bs = (BODYSTRUCTURE)item;
1285:         // Check for the UID item
1286:         else if (item instanceof UID) {
1287:          UID u = (UID)item;
1288:          uid = u.uid; // set uid
1289:          // add entry into uid table
1290:          if (((IMAPFolder)folder).uidTable == null)
1291:                 ((IMAPFolder) folder).uidTable
1292:                         = new Hashtable<>();
1293:          ((IMAPFolder)folder).uidTable.put(Long.valueOf(u.uid), this);
1294:         }
1295:
1296:         // Check for header items
1297:         else if (item instanceof RFC822DATA ||
1298:                  item instanceof BODY) {
1299:          InputStream headerStream;
1300:          boolean isHeader;
1301:          if (item instanceof RFC822DATA) { // IMAP4
1302:                 headerStream =
1303:                  ((RFC822DATA)item).getByteArrayInputStream();
1304:                 isHeader = ((RFC822DATA)item).isHeader();
1305:          } else {        // IMAP4rev1
1306:                 headerStream =
1307:                  ((BODY)item).getByteArrayInputStream();
1308:                 isHeader = ((BODY)item).isHeader();
1309:          }
1310:
1311:          if (!isHeader) {
1312:                 // load the entire message by using the superclass
1313:                 // MimeMessage.parse method
1314:                 // first, save the size of the message
1315:                 try {
1316:                  size = headerStream.available();
1317:                 } catch (IOException ex) {
1318:                  // should never occur
1319:                 }
1320:                 parse(headerStream);
1321:                 bodyLoaded = true;
1322:                 setHeadersLoaded(true);
1323:          } else {
1324:                 // Load the obtained headers.
1325:                 InternetHeaders h = new InternetHeaders();
1326:                 // Some IMAP servers (e.g., gmx.net) return NIL
1327:                 // instead of a string just containing a CR/LF
1328:                 // when the header list is empty.
1329:                 if (headerStream != null)
1330:                  h.load(headerStream);
1331:                 if (headers == null || allHeaders)
1332:                  headers = h;
1333:                 else {
1334:                  /*
1335:                  * This is really painful. A second fetch
1336:                  * of the same headers (which might occur because
1337:                  * a new header was added to the set requested)
1338:                  * will return headers we already know about.
1339:                  * In this case, only load the headers we haven't
1340:                  * seen before to avoid adding duplicates of
1341:                  * headers we already have.
1342:                  *
1343:                  * XXX - There's a race condition here if another
1344:                  * thread is reading headers in the same message
1345:                  * object, because InternetHeaders is not thread
1346:                  * safe.
1347:                  */
1348:                  Enumeration<Header> e = h.getAllHeaders();
1349:                  while (e.hasMoreElements()) {
1350:                         Header he = e.nextElement();
1351:                         if (!isHeaderLoaded(he.getName()))
1352:                          headers.addHeader(
1353:                                         he.getName(), he.getValue());
1354:                  }
1355:                 }
1356:
1357:                 // if we asked for all headers, assume we got them
1358:                 if (allHeaders)
1359:                  setHeadersLoaded(true);
1360:                 else {
1361:                  // Mark all headers we asked for as 'loaded'
1362:                  for (int k = 0; k < hdrs.length; k++)
1363:                         setHeaderLoaded(hdrs[k]);
1364:                 }
1365:          }
1366:         } else
1367:          return false;        // not handled
1368:         return true;                // something above handled it
1369: }
1370:
1371: /**
1372: * Apply the data in the extension FETCH items to this message.
1373: * This method adds all the items to the items map.
1374: * Subclasses may override this method to call super and then
1375: * also copy the data to a more convenient form.
1376: *
1377: * ASSERT: Must hold the messageCacheLock.
1378: *
1379: * @param        extensionItems        the Map to add fetch items to
1380: * @since JavaMail 1.4.6
1381: */
1382: protected void handleExtensionFetchItems(
1383:          Map<String, Object> extensionItems) {
1384:         if (extensionItems == null || extensionItems.isEmpty())
1385:          return;
1386:         if (items == null)
1387:          items = new HashMap<>();
1388:         items.putAll(extensionItems);
1389: }
1390:
1391: /**
1392: * Fetch an individual item for the current message.
1393: * Note that handleExtensionFetchItems will have been called
1394: * to store this item in the message before this method
1395: * returns.
1396: *
1397: * @param        fitem        the FetchItem
1398: * @return                the data associated with the FetchItem
1399: * @exception        MessagingException for failures
1400: * @since JavaMail 1.4.6
1401: */
1402: protected Object fetchItem(FetchItem fitem)
1403:                                 throws MessagingException {
1404:
1405:         // Acquire MessageCacheLock, to freeze seqnum.
1406:         synchronized(getMessageCacheLock()) {
1407:          Object robj = null;
1408:
1409:          try {
1410:                 IMAPProtocol p = getProtocol();
1411:
1412:                 checkExpunged(); // Insure that this message is not expunged
1413:
1414:                 int seqnum = getSequenceNumber();
1415:                 Response[] r = p.fetch(seqnum, fitem.getName());
1416:
1417:                 for (int i = 0; i < r.length; i++) {
1418:                  // If this response is NOT a FetchResponse or if it does
1419:                  // not match our seqnum, skip.
1420:                  if (r[i] == null ||
1421:                         !(r[i] instanceof FetchResponse) ||
1422:                         ((FetchResponse)r[i]).getNumber() != seqnum)
1423:                         continue;
1424:
1425:                  FetchResponse f = (FetchResponse)r[i];
1426:                  handleExtensionFetchItems(f.getExtensionItems());
1427:                  if (items != null) {
1428:                         Object o = items.get(fitem.getName());
1429:                         if (o != null)
1430:                          robj = o;
1431:                  }
1432:                 }
1433:
1434:                 // ((IMAPFolder)folder).handleResponses(r);
1435:                 p.notifyResponseHandlers(r);
1436:                 p.handleResult(r[r.length - 1]);
1437:          } catch (ConnectionException cex) {
1438:                 throw new FolderClosedException(folder, cex.getMessage());
1439:          } catch (ProtocolException pex) {
1440:                 forceCheckExpunged();
1441:                 throw new MessagingException(pex.getMessage(), pex);
1442:          }
1443:          return robj;
1444:
1445:         } // Release MessageCacheLock
1446: }
1447:
1448: /**
1449: * Return the data associated with the FetchItem.
1450: * If the data hasn't been fetched, call the fetchItem
1451: * method to fetch it. Returns null if there is no
1452: * data for the FetchItem.
1453: *
1454: * @param        fitem        the FetchItem
1455: * @return                the data associated with the FetchItem
1456: * @exception        MessagingException for failures
1457: * @since JavaMail 1.4.6
1458: */
1459: public synchronized Object getItem(FetchItem fitem)
1460:                                 throws MessagingException {
1461:         Object item = items == null ? null : items.get(fitem.getName());
1462:         if (item == null)
1463:          item = fetchItem(fitem);
1464:         return item;
1465: }
1466:
1467: /*
1468: * Load the Envelope for this message.
1469: */
1470: private synchronized void loadEnvelope() throws MessagingException {
1471:         if (envelope != null) // already loaded
1472:          return;
1473:
1474:         Response[] r = null;
1475:
1476:         // Acquire MessageCacheLock, to freeze seqnum.
1477:         synchronized(getMessageCacheLock()) {
1478:          try {
1479:                 IMAPProtocol p = getProtocol();
1480:
1481:                 checkExpunged(); // Insure that this message is not expunged
1482:
1483:                 int seqnum = getSequenceNumber();
1484:                 r = p.fetch(seqnum, EnvelopeCmd);
1485:
1486:                 for (int i = 0; i < r.length; i++) {
1487:                  // If this response is NOT a FetchResponse or if it does
1488:                  // not match our seqnum, skip.
1489:                  if (r[i] == null ||
1490:                         !(r[i] instanceof FetchResponse) ||
1491:                         ((FetchResponse)r[i]).getNumber() != seqnum)
1492:                         continue;
1493:
1494:                  FetchResponse f = (FetchResponse)r[i];
1495:                 
1496:                  // Look for the Envelope items.
1497:                  int count = f.getItemCount();
1498:                  for (int j = 0; j < count; j++) {
1499:                         Item item = f.getItem(j);
1500:                         
1501:                         if (item instanceof ENVELOPE)
1502:                          envelope = (ENVELOPE)item;
1503:                         else if (item instanceof INTERNALDATE)
1504:                          receivedDate = ((INTERNALDATE)item).getDate();
1505:                         else if (item instanceof RFC822SIZE)
1506:                          size = ((RFC822SIZE)item).size;
1507:                  }
1508:                 }
1509:
1510:                 // ((IMAPFolder)folder).handleResponses(r);
1511:                 p.notifyResponseHandlers(r);
1512:                 p.handleResult(r[r.length - 1]);
1513:          } catch (ConnectionException cex) {
1514:                 throw new FolderClosedException(folder, cex.getMessage());
1515:          } catch (ProtocolException pex) {
1516:                 forceCheckExpunged();
1517:                 throw new MessagingException(pex.getMessage(), pex);
1518:          }
1519:
1520:         } // Release MessageCacheLock
1521:
1522:         if (envelope == null)
1523:          throw new MessagingException("Failed to load IMAP envelope");
1524: }
1525:
1526: /*
1527: * Load the BODYSTRUCTURE
1528: */
1529: private synchronized void loadBODYSTRUCTURE()
1530:                 throws MessagingException {
1531:         if (bs != null) // already loaded
1532:          return;
1533:
1534:         // Acquire MessageCacheLock, to freeze seqnum.
1535:         synchronized(getMessageCacheLock()) {
1536:          try {
1537:                 IMAPProtocol p = getProtocol();
1538:
1539:                 // This message could be expunged when we were waiting
1540:                 // to acquire the lock ...
1541:                 checkExpunged();
1542:
1543:                 bs = p.fetchBodyStructure(getSequenceNumber());
1544:          } catch (ConnectionException cex) {
1545:                 throw new FolderClosedException(folder, cex.getMessage());
1546:          } catch (ProtocolException pex) {
1547:                 forceCheckExpunged();
1548:                 throw new MessagingException(pex.getMessage(), pex);
1549:          }
1550:          if (bs == null) {
1551:                 // if the FETCH is successful, we should always get a
1552:                 // BODYSTRUCTURE, but some servers fail to return it
1553:                 // if the message has been expunged
1554:                 forceCheckExpunged();
1555:                 throw new MessagingException("Unable to load BODYSTRUCTURE");
1556:          }
1557:         }
1558: }
1559:
1560: /*
1561: * Load all headers.
1562: */
1563: private synchronized void loadHeaders() throws MessagingException {
1564:         if (headersLoaded)
1565:          return;
1566:
1567:         InputStream is = null;
1568:
1569:         // Acquire MessageCacheLock, to freeze seqnum.
1570:         synchronized (getMessageCacheLock()) {
1571:          try {
1572:                 IMAPProtocol p = getProtocol();
1573:
1574:                 // This message could be expunged when we were waiting
1575:                 // to acquire the lock ...
1576:                 checkExpunged();
1577:
1578:                 if (p.isREV1()) {
1579:                  BODY b = p.peekBody(getSequenceNumber(),
1580:                                          toSection("HEADER"));
1581:                  if (b != null)
1582:                         is = b.getByteArrayInputStream();
1583:                 } else {
1584:                  RFC822DATA rd = p.fetchRFC822(getSequenceNumber(),
1585:                                                  "HEADER");
1586:                  if (rd != null)
1587:                         is = rd.getByteArrayInputStream();
1588:                 }
1589:          } catch (ConnectionException cex) {
1590:                 throw new FolderClosedException(folder, cex.getMessage());
1591:          } catch (ProtocolException pex) {
1592:                 forceCheckExpunged();
1593:                 throw new MessagingException(pex.getMessage(), pex);
1594:          }
1595:         } // Release MessageCacheLock
1596:
1597:         if (is == null)
1598:          throw new MessagingException("Cannot load header");
1599:         headers = new InternetHeaders(is);
1600:         headersLoaded = true;
1601: }
1602:
1603: /*
1604: * Load this message's Flags
1605: */
1606: private synchronized void loadFlags() throws MessagingException {
1607:         if (flags != null)
1608:          return;
1609:         
1610:         // Acquire MessageCacheLock, to freeze seqnum.
1611:         synchronized(getMessageCacheLock()) {
1612:          try {
1613:                 IMAPProtocol p = getProtocol();
1614:
1615:                 // This message could be expunged when we were waiting
1616:                 // to acquire the lock ...
1617:                 checkExpunged();
1618:
1619:                 flags = p.fetchFlags(getSequenceNumber());
1620:                 // make sure flags is always set, even if server is broken
1621:                 if (flags == null)
1622:                  flags = new Flags();
1623:          } catch (ConnectionException cex) {
1624:                 throw new FolderClosedException(folder, cex.getMessage());
1625:          } catch (ProtocolException pex) {
1626:                 forceCheckExpunged();
1627:                 throw new MessagingException(pex.getMessage(), pex);
1628:          }
1629:         } // Release MessageCacheLock
1630: }
1631:
1632: /*
1633: * Are all headers loaded?
1634: */
1635: private boolean areHeadersLoaded() {
1636:         return headersLoaded;
1637: }
1638:
1639: /*
1640: * Set whether all headers are loaded.
1641: */
1642: private void setHeadersLoaded(boolean loaded) {
1643:         headersLoaded = loaded;
1644: }
1645:
1646: /*
1647: * Check if the given header was ever loaded from the server
1648: */
1649: private boolean isHeaderLoaded(String name) {
1650:         if (headersLoaded) // All headers for this message have been loaded
1651:          return true;
1652:         
1653:         return loadedHeaders.containsKey(name.toUpperCase(Locale.ENGLISH));
1654: }
1655:
1656: /*
1657: * Mark that the given headers have been loaded from the server.
1658: */
1659: private void setHeaderLoaded(String name) {
1660:         loadedHeaders.put(name.toUpperCase(Locale.ENGLISH), name);
1661: }
1662:
1663: /*
1664: * Convert the given FETCH item identifier to the approriate
1665: * section-string for this message.
1666: */
1667: private String toSection(String what) {
1668:         if (sectionId == null)
1669:          return what;
1670:         else
1671:          return sectionId + "." + what;
1672: }
1673:
1674: /*
1675: * Clone an array of InternetAddresses.
1676: */
1677: private InternetAddress[] aaclone(InternetAddress[] aa) {
1678:         if (aa == null)
1679:          return null;
1680:         else
1681:          return aa.clone();
1682: }
1683:
1684: private Flags _getFlags() {
1685:         return flags;
1686: }
1687:
1688: private ENVELOPE _getEnvelope() {
1689:         return envelope;
1690: }
1691:
1692: private BODYSTRUCTURE _getBodyStructure() {
1693:         return bs;
1694: }
1695:
1696: /***********************************************************
1697: * accessor routines to make available certain private/protected
1698: * fields to other classes in this package.
1699: ***********************************************************/
1700:
1701: /*
1702: * Called by IMAPFolder.
1703: * Must not be synchronized.
1704: */
1705: void _setFlags(Flags flags) {
1706:         this.flags = flags;
1707: }
1708:
1709: /*
1710: * Called by IMAPNestedMessage.
1711: */
1712: Session _getSession() {
1713:         return session;
1714: }
1715: }