001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: /*
038: * @(#)POP3Message.java 1.18 07/05/04
039: */
040:
041: package com.sun.mail.pop3;
042:
043: import java.io.*;
044: import java.util.Enumeration;
045: import javax.mail.*;
046: import javax.mail.internet.*;
047: import javax.mail.event.*;
048:
049: /**
050: * A POP3 Message. Just like a MimeMessage except that
051: * some things are not supported.
052: *
053: * @author Bill Shannon
054: */
055: public class POP3Message extends MimeMessage {
056:
057: /*
058: * Our locking strategy is to always lock the POP3Folder before the
059: * POP3Message so we have to be careful to drop our lock before calling
060: * back to the folder to close it and notify of connection lost events.
061: */
062:
063: // flag to indicate we haven't tried to fetch the UID yet
064: static final String UNKNOWN = "UNKNOWN";
065:
066: private POP3Folder folder; // overrides folder in MimeMessage
067: private int hdrSize = -1;
068: private int msgSize = -1;
069: String uid = UNKNOWN; // controlled by folder lock
070:
071: public POP3Message(Folder folder, int msgno)
072: throws MessagingException {
073: super (folder, msgno);
074: this .folder = (POP3Folder) folder;
075: }
076:
077: /**
078: * Set the specified flags on this message to the specified value.
079: *
080: * @param newFlags the flags to be set
081: * @param set the value to be set
082: */
083: public void setFlags(Flags newFlags, boolean set)
084: throws MessagingException {
085: Flags oldFlags = (Flags) flags.clone();
086: super .setFlags(newFlags, set);
087: if (!flags.equals(oldFlags))
088: folder.notifyMessageChangedListeners(
089: MessageChangedEvent.FLAGS_CHANGED, this );
090: }
091:
092: /**
093: * Return the size of the content of this message in bytes.
094: * Returns -1 if the size cannot be determined. <p>
095: *
096: * Note that this number may not be an exact measure of the
097: * content size and may or may not account for any transfer
098: * encoding of the content. <p>
099: *
100: * @return size of content in bytes
101: * @exception MessagingException
102: */
103: public int getSize() throws MessagingException {
104: try {
105: synchronized (this ) {
106: if (msgSize >= 0)
107: return msgSize;
108: if (msgSize < 0) {
109: /*
110: * Use LIST to determine the entire message
111: * size and subtract out the header size
112: * (which may involve loading the headers,
113: * which may load the content as a side effect).
114: * If the content is loaded as a side effect of
115: * loading the headers, get the size directly.
116: */
117: if (headers == null)
118: loadHeaders();
119: if (contentStream != null)
120: msgSize = contentStream.available();
121: else
122: msgSize = folder.getProtocol().list(msgnum)
123: - hdrSize;
124: }
125: return msgSize;
126: }
127: } catch (EOFException eex) {
128: folder.close(false);
129: throw new FolderClosedException(folder, eex.toString());
130: } catch (IOException ex) {
131: throw new MessagingException("error getting size", ex);
132: }
133: }
134:
135: /**
136: * Produce the raw bytes of the content. The data is fetched using
137: * the POP3 RETR command.
138: *
139: * @see #contentStream
140: */
141: protected InputStream getContentStream() throws MessagingException {
142: try {
143: synchronized (this ) {
144: if (contentStream == null) {
145: InputStream rawcontent = folder
146: .getProtocol()
147: .retr(msgnum,
148: msgSize > 0 ? msgSize + hdrSize : 0);
149: if (rawcontent == null) {
150: expunged = true;
151: throw new MessageRemovedException(); // XXX - what else?
152: }
153: if (headers == null
154: || ((POP3Store) (folder.getStore())).forgetTopHeaders) {
155: headers = new InternetHeaders(rawcontent);
156: hdrSize = (int) ((SharedInputStream) rawcontent)
157: .getPosition();
158: } else {
159: /*
160: * Already have the headers, have to skip the headers
161: * in the content array and return the body.
162: *
163: * XXX - It seems that some mail servers return slightly
164: * different headers in the RETR results than were returned
165: * in the TOP results, so we can't depend on remembering
166: * the size of the headers from the TOP command and just
167: * skipping that many bytes. Instead, we have to process
168: * the content, skipping over the header until we come to
169: * the empty line that separates the header from the body.
170: */
171: int offset = 0;
172: for (;;) {
173: int len = 0; // number of bytes in this line
174: int c1;
175: while ((c1 = rawcontent.read()) >= 0) {
176: if (c1 == '\n') // end of line
177: break;
178: else if (c1 == '\r') {
179: // got CR, is the next char LF?
180: if (rawcontent.available() > 0) {
181: rawcontent.mark(1);
182: if (rawcontent.read() != '\n')
183: rawcontent.reset();
184: }
185: break; // in any case, end of line
186: }
187:
188: // not CR, NL, or CRLF, count the byte
189: len++;
190: }
191: // here when end of line or out of data
192:
193: // if out of data, we're done
194: if (rawcontent.available() == 0)
195: break;
196:
197: // if it was an empty line, we're done
198: if (len == 0)
199: break;
200: }
201: hdrSize = (int) ((SharedInputStream) rawcontent)
202: .getPosition();
203: }
204: contentStream = ((SharedInputStream) rawcontent)
205: .newStream(hdrSize, -1);
206: rawcontent = null; // help GC
207: }
208: }
209: } catch (EOFException eex) {
210: folder.close(false);
211: throw new FolderClosedException(folder, eex.toString());
212: } catch (IOException ex) {
213: throw new MessagingException("error fetching POP3 content",
214: ex);
215: }
216: return super .getContentStream();
217: }
218:
219: /**
220: * Invalidate the cache of content for this message object, causing
221: * it to be fetched again from the server the next time it is needed.
222: * If <code>invalidateHeaders</code> is true, invalidate the headers
223: * as well.
224: *
225: * @param invalidateHeaders invalidate the headers as well?
226: */
227: public synchronized void invalidate(boolean invalidateHeaders) {
228: content = null;
229: contentStream = null;
230: msgSize = -1;
231: if (invalidateHeaders) {
232: headers = null;
233: hdrSize = -1;
234: }
235: }
236:
237: /**
238: * Fetch the header of the message and the first <code>n</code> lines
239: * of the raw content of the message. The headers and data are
240: * available in the returned InputStream.
241: *
242: * @param n number of lines of content to fetch
243: * @return InputStream containing the message headers and n content lines
244: */
245: public InputStream top(int n) throws MessagingException {
246: try {
247: synchronized (this ) {
248: return folder.getProtocol().top(msgnum, n);
249: }
250: } catch (EOFException eex) {
251: folder.close(false);
252: throw new FolderClosedException(folder, eex.toString());
253: } catch (IOException ex) {
254: throw new MessagingException("error getting size", ex);
255: }
256: }
257:
258: /**
259: * Get all the headers for this header_name. Note that certain
260: * headers may be encoded as per RFC 2047 if they contain
261: * non US-ASCII characters and these should be decoded. <p>
262: *
263: * @param name name of header
264: * @return array of headers
265: * @exception MessagingException
266: * @see javax.mail.internet.MimeUtility
267: */
268: public String[] getHeader(String name) throws MessagingException {
269: if (headers == null)
270: loadHeaders();
271: return headers.getHeader(name);
272: }
273:
274: /**
275: * Get all the headers for this header name, returned as a single
276: * String, with headers separated by the delimiter. If the
277: * delimiter is <code>null</code>, only the first header is
278: * returned.
279: *
280: * @param name the name of this header
281: * @param delimiter delimiter between returned headers
282: * @return the value fields for all headers with
283: * this name
284: * @exception MessagingException
285: */
286: public String getHeader(String name, String delimiter)
287: throws MessagingException {
288: if (headers == null)
289: loadHeaders();
290: return headers.getHeader(name, delimiter);
291: }
292:
293: /**
294: * Set the value for this header_name. Throws IllegalWriteException
295: * because POP3 messages are read-only.
296: *
297: * @param name header name
298: * @param value header value
299: * @see javax.mail.internet.MimeUtility
300: * @exception IllegalWriteException because the underlying
301: * implementation does not support modification
302: * @exception IllegalStateException if this message is
303: * obtained from a READ_ONLY folder.
304: */
305: public void setHeader(String name, String value)
306: throws MessagingException {
307: // XXX - should check for read-only folder?
308: throw new IllegalWriteException("POP3 messages are read-only");
309: }
310:
311: /**
312: * Add this value to the existing values for this header_name.
313: * Throws IllegalWriteException because POP3 messages are read-only.
314: *
315: * @param name header name
316: * @param value header value
317: * @see javax.mail.internet.MimeUtility
318: * @exception IllegalWriteException because the underlying
319: * implementation does not support modification
320: * @exception IllegalStateException if this message is
321: * obtained from a READ_ONLY folder.
322: */
323: public void addHeader(String name, String value)
324: throws MessagingException {
325: // XXX - should check for read-only folder?
326: throw new IllegalWriteException("POP3 messages are read-only");
327: }
328:
329: /**
330: * Remove all headers with this name.
331: * Throws IllegalWriteException because POP3 messages are read-only.
332: *
333: * @exception IllegalWriteException because the underlying
334: * implementation does not support modification
335: * @exception IllegalStateException if this message is
336: * obtained from a READ_ONLY folder.
337: */
338: public void removeHeader(String name) throws MessagingException {
339: // XXX - should check for read-only folder?
340: throw new IllegalWriteException("POP3 messages are read-only");
341: }
342:
343: /**
344: * Return all the headers from this Message as an enumeration
345: * of Header objects. <p>
346: *
347: * Note that certain headers may be encoded as per RFC 2047
348: * if they contain non US-ASCII characters and these should
349: * be decoded. <p>
350: *
351: * @return array of header objects
352: * @exception MessagingException
353: * @see javax.mail.internet.MimeUtility
354: */
355: public Enumeration getAllHeaders() throws MessagingException {
356: if (headers == null)
357: loadHeaders();
358: return headers.getAllHeaders();
359: }
360:
361: /**
362: * Return matching headers from this Message as an Enumeration of
363: * Header objects.
364: *
365: * @exception MessagingException
366: */
367: public Enumeration getMatchingHeaders(String[] names)
368: throws MessagingException {
369: if (headers == null)
370: loadHeaders();
371: return headers.getMatchingHeaders(names);
372: }
373:
374: /**
375: * Return non-matching headers from this Message as an
376: * Enumeration of Header objects.
377: *
378: * @exception MessagingException
379: */
380: public Enumeration getNonMatchingHeaders(String[] names)
381: throws MessagingException {
382: if (headers == null)
383: loadHeaders();
384: return headers.getNonMatchingHeaders(names);
385: }
386:
387: /**
388: * Add a raw RFC822 header-line.
389: * Throws IllegalWriteException because POP3 messages are read-only.
390: *
391: * @exception IllegalWriteException because the underlying
392: * implementation does not support modification
393: * @exception IllegalStateException if this message is
394: * obtained from a READ_ONLY folder.
395: */
396: public void addHeaderLine(String line) throws MessagingException {
397: // XXX - should check for read-only folder?
398: throw new IllegalWriteException("POP3 messages are read-only");
399: }
400:
401: /**
402: * Get all header lines as an Enumeration of Strings. A Header
403: * line is a raw RFC822 header-line, containing both the "name"
404: * and "value" field.
405: *
406: * @exception MessagingException
407: */
408: public Enumeration getAllHeaderLines() throws MessagingException {
409: if (headers == null)
410: loadHeaders();
411: return headers.getAllHeaderLines();
412: }
413:
414: /**
415: * Get matching header lines as an Enumeration of Strings.
416: * A Header line is a raw RFC822 header-line, containing both
417: * the "name" and "value" field.
418: *
419: * @exception MessagingException
420: */
421: public Enumeration getMatchingHeaderLines(String[] names)
422: throws MessagingException {
423: if (headers == null)
424: loadHeaders();
425: return headers.getMatchingHeaderLines(names);
426: }
427:
428: /**
429: * Get non-matching header lines as an Enumeration of Strings.
430: * A Header line is a raw RFC822 header-line, containing both
431: * the "name" and "value" field.
432: *
433: * @exception MessagingException
434: */
435: public Enumeration getNonMatchingHeaderLines(String[] names)
436: throws MessagingException {
437: if (headers == null)
438: loadHeaders();
439: return headers.getNonMatchingHeaderLines(names);
440: }
441:
442: /**
443: * POP3 message can't be changed. This method throws
444: * IllegalWriteException.
445: *
446: * @exception IllegalWriteException because the underlying
447: * implementation does not support modification
448: */
449: public void saveChanges() throws MessagingException {
450: // POP3 Messages are read-only
451: throw new IllegalWriteException("POP3 messages are read-only");
452: }
453:
454: /**
455: * Load the headers for this message into the InternetHeaders object.
456: * The headers are fetched using the POP3 TOP command.
457: */
458: private void loadHeaders() throws MessagingException {
459: try {
460: synchronized (this ) {
461: if (headers != null) // check again under lock
462: return;
463: InputStream hdrs = null;
464: if (((POP3Store) (folder.getStore())).disableTop
465: || (hdrs = folder.getProtocol().top(msgnum, 0)) == null) {
466: // possibly because the TOP command isn't supported,
467: // load headers as a side effect of loading the entire
468: // content.
469: InputStream cs = getContentStream();
470: cs.close();
471: } else {
472: hdrSize = hdrs.available();
473: headers = new InternetHeaders(hdrs);
474: }
475: }
476: } catch (EOFException eex) {
477: folder.close(false);
478: throw new FolderClosedException(folder, eex.toString());
479: } catch (IOException ex) {
480: throw new MessagingException("error loading POP3 headers",
481: ex);
482: }
483: }
484: }
|