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: * @(#)POP3Folder.java 1.34 07/05/04
039: */
040:
041: package com.sun.mail.pop3;
042:
043: import javax.mail.*;
044: import javax.mail.internet.*;
045: import javax.mail.event.*;
046: import java.io.InputStream;
047: import java.io.BufferedInputStream;
048: import java.io.ByteArrayInputStream;
049: import java.io.IOException;
050: import java.io.EOFException;
051: import java.util.Vector;
052: import java.util.StringTokenizer;
053: import java.lang.reflect.Constructor;
054:
055: import com.sun.mail.util.LineInputStream;
056:
057: /**
058: * A POP3 Folder (can only be "INBOX").
059: *
060: * See the <a href="package-summary.html">com.sun.mail.pop3</a> package
061: * documentation for further information on the POP3 protocol provider. <p>
062: *
063: * @author Bill Shannon
064: * @author John Mani (ported to the javax.mail APIs)
065: */
066: public class POP3Folder extends Folder {
067:
068: private String name;
069: private Protocol port;
070: private int total;
071: private int size;
072: private boolean exists = false;
073: private boolean opened = false;
074: private Vector message_cache;
075: private boolean doneUidl = false;
076:
077: POP3Folder(POP3Store store, String name) {
078: super (store);
079: this .name = name;
080: if (name.equalsIgnoreCase("INBOX"))
081: exists = true;
082: }
083:
084: public String getName() {
085: return name;
086: }
087:
088: public String getFullName() {
089: return name;
090: }
091:
092: public Folder getParent() {
093: return new DefaultFolder((POP3Store) store);
094: }
095:
096: /**
097: * Always true for the folder "INBOX", always false for
098: * any other name.
099: *
100: * @return true for INBOX, false otherwise
101: */
102: public boolean exists() {
103: return exists;
104: }
105:
106: /**
107: * Always throws <code>MessagingException</code> because no POP3 folders
108: * can contain subfolders.
109: *
110: * @exception MessagingException always
111: */
112: public Folder[] list(String pattern) throws MessagingException {
113: throw new MessagingException("not a directory");
114: }
115:
116: /**
117: * Always returns a NUL character because POP3 doesn't support a hierarchy.
118: *
119: * @return NUL
120: */
121: public char getSeparator() {
122: return '\0';
123: }
124:
125: /**
126: * Always returns Folder.HOLDS_MESSAGES.
127: *
128: * @return Folder.HOLDS_MESSAGES
129: */
130: public int getType() {
131: return HOLDS_MESSAGES;
132: }
133:
134: /**
135: * Always returns <code>false</code>; the POP3 protocol doesn't
136: * support creating folders.
137: *
138: * @return false
139: */
140: public boolean create(int type) throws MessagingException {
141: return false;
142: }
143:
144: /**
145: * Always returns <code>false</code>; the POP3 protocol provides
146: * no way to determine when a new message arrives.
147: *
148: * @return false
149: */
150: public boolean hasNewMessages() throws MessagingException {
151: return false; // no way to know
152: }
153:
154: /**
155: * Always throws <code>MessagingException</code> because no POP3 folders
156: * can contain subfolders.
157: *
158: * @exception MessagingException always
159: */
160: public Folder getFolder(String name) throws MessagingException {
161: throw new MessagingException("not a directory");
162: }
163:
164: /**
165: * Always throws <code>MethodNotSupportedException</code>
166: * because the POP3 protocol doesn't allow the INBOX to
167: * be deleted.
168: *
169: * @exception MethodNotSupportedException always
170: */
171: public boolean delete(boolean recurse) throws MessagingException {
172: throw new MethodNotSupportedException("delete");
173: }
174:
175: /**
176: * Always throws <code>MethodNotSupportedException</code>
177: * because the POP3 protocol doesn't support multiple folders.
178: *
179: * @exception MethodNotSupportedException always
180: */
181: public boolean renameTo(Folder f) throws MessagingException {
182: throw new MethodNotSupportedException("renameTo");
183: }
184:
185: /**
186: * Throws <code>FolderNotFoundException</code> unless this
187: * folder is named "INBOX".
188: *
189: * @exception FolderNotFoundException if not INBOX
190: * @exception AuthenticationException authentication failures
191: * @exception MessagingException other open failures
192: */
193: public synchronized void open(int mode) throws MessagingException {
194: checkClosed();
195: if (!exists)
196: throw new FolderNotFoundException(this ,
197: "folder is not INBOX");
198:
199: try {
200: port = ((POP3Store) store).getPort(this );
201: Status s = port.stat();
202: total = s.total;
203: size = s.size;
204: this .mode = mode;
205: opened = true;
206: } catch (IOException ioex) {
207: try {
208: if (port != null)
209: port.quit();
210: } catch (IOException ioex2) {
211: // ignore
212: } finally {
213: port = null;
214: ((POP3Store) store).closePort(this );
215: }
216: throw new MessagingException("Open failed", ioex);
217: }
218:
219: // Create the message cache vector of appropriate size
220: message_cache = new Vector(total);
221: message_cache.setSize(total);
222: doneUidl = false;
223:
224: notifyConnectionListeners(ConnectionEvent.OPENED);
225: }
226:
227: public synchronized void close(boolean expunge)
228: throws MessagingException {
229: checkOpen();
230:
231: try {
232: /*
233: * Some POP3 servers will mark messages for deletion when
234: * they're read. To prevent such messages from being
235: * deleted before the client deletes them, you can set
236: * the mail.pop3.rsetbeforequit property to true. This
237: * causes us to issue a POP3 RSET command to clear all
238: * the "marked for deletion" flags. We can then explicitly
239: * delete messages as desired.
240: */
241: if (((POP3Store) store).rsetBeforeQuit)
242: port.rset();
243: if (expunge && mode == READ_WRITE) {
244: // find all messages marked deleted and issue DELE commands
245: POP3Message m;
246: for (int i = 0; i < message_cache.size(); i++) {
247: if ((m = (POP3Message) message_cache.elementAt(i)) != null) {
248: if (m.isSet(Flags.Flag.DELETED))
249: try {
250: port.dele(i + 1);
251: } catch (IOException ioex) {
252: throw new MessagingException(
253: "Exception deleting messages during close",
254: ioex);
255: }
256: }
257: }
258: }
259:
260: port.quit();
261: } catch (IOException ex) {
262: // do nothing
263: } finally {
264: port = null;
265: ((POP3Store) store).closePort(this );
266: message_cache = null;
267: opened = false;
268: notifyConnectionListeners(ConnectionEvent.CLOSED);
269: }
270: }
271:
272: public boolean isOpen() {
273: if (!opened)
274: return false;
275: if (store.isConnected())
276: return true;
277: try {
278: close(false);
279: } catch (MessagingException ex) {
280: }
281: return false;
282: }
283:
284: /**
285: * Always returns an empty <code>Flags</code> object because
286: * the POP3 protocol doesn't support any permanent flags.
287: *
288: * @return empty Flags object
289: */
290: public Flags getPermanentFlags() {
291: return new Flags(); // empty flags object
292: }
293:
294: /**
295: * Will not change while the folder is open because the POP3
296: * protocol doesn't support notification of new messages
297: * arriving in open folders.
298: */
299: public synchronized int getMessageCount() throws MessagingException {
300: if (!opened)
301: return -1;
302: checkReadable();
303: return total;
304: }
305:
306: public synchronized Message getMessage(int msgno)
307: throws MessagingException {
308: checkOpen();
309:
310: POP3Message m;
311:
312: // Assuming that msgno is <= total
313: if ((m = (POP3Message) message_cache.elementAt(msgno - 1)) == null) {
314: m = createMessage(this , msgno);
315: message_cache.setElementAt(m, msgno - 1);
316: }
317: return m;
318: }
319:
320: protected POP3Message createMessage(Folder f, int msgno)
321: throws MessagingException {
322: POP3Message m = null;
323: Constructor cons = ((POP3Store) store).messageConstructor;
324: if (cons != null) {
325: try {
326: Object[] o = { this , new Integer(msgno) };
327: m = (POP3Message) cons.newInstance(o);
328: } catch (Exception ex) {
329: // ignore
330: }
331: }
332: if (m == null)
333: m = new POP3Message(this , msgno);
334: return m;
335: }
336:
337: /**
338: * Always throws <code>MethodNotSupportedException</code>
339: * because the POP3 protocol doesn't support appending messages.
340: *
341: * @exception MethodNotSupportedException always
342: */
343: public void appendMessages(Message[] msgs)
344: throws MessagingException {
345: throw new MethodNotSupportedException("Append not supported");
346: }
347:
348: /**
349: * Always throws <code>MethodNotSupportedException</code>
350: * because the POP3 protocol doesn't support expunging messages
351: * without closing the folder; call the {@link #close close} method
352: * with the <code>expunge</code> argument set to <code>true</code>
353: * instead.
354: *
355: * @exception MethodNotSupportedException always
356: */
357: public Message[] expunge() throws MessagingException {
358: throw new MethodNotSupportedException("Expunge not supported");
359: }
360:
361: /**
362: * Prefetch information about POP3 messages.
363: * If the FetchProfile contains <code>UIDFolder.FetchProfileItem.UID</code>,
364: * POP3 UIDs for all messages in the folder are fetched using the POP3
365: * UIDL command.
366: * If the FetchProfile contains <code>FetchProfile.Item.ENVELOPE</code>,
367: * the headers and size of all messages are fetched using the POP3 TOP
368: * and LIST commands.
369: */
370: public synchronized void fetch(Message[] msgs, FetchProfile fp)
371: throws MessagingException {
372: checkReadable();
373: if (!doneUidl && fp.contains(UIDFolder.FetchProfileItem.UID)) {
374: /*
375: * Since the POP3 protocol only lets us fetch the UID
376: * for a single message or for all messages, we go ahead
377: * and fetch UIDs for all messages here, ignoring the msgs
378: * parameter. We could be more intelligent and base this
379: * decision on the number of messages fetched, or the
380: * percentage of the total number of messages fetched.
381: */
382: String[] uids = new String[message_cache.size()];
383: try {
384: if (!port.uidl(uids))
385: return;
386: } catch (EOFException eex) {
387: close(false);
388: throw new FolderClosedException(this , eex.toString());
389: } catch (IOException ex) {
390: throw new MessagingException("error getting UIDL", ex);
391: }
392: for (int i = 0; i < uids.length; i++) {
393: if (uids[i] == null)
394: continue;
395: POP3Message m = (POP3Message) getMessage(i + 1);
396: m.uid = uids[i];
397: }
398: doneUidl = true; // only do this once
399: }
400: if (fp.contains(FetchProfile.Item.ENVELOPE)) {
401: for (int i = 0; i < msgs.length; i++) {
402: try {
403: POP3Message msg = (POP3Message) msgs[i];
404: // fetch headers
405: msg.getHeader("");
406: // fetch message size
407: msg.getSize();
408: } catch (MessageRemovedException mex) {
409: // should never happen, but ignore it if it does
410: }
411: }
412: }
413: }
414:
415: /**
416: * Return the unique ID string for this message, or null if
417: * not available. Uses the POP3 UIDL command.
418: *
419: * @return unique ID string
420: * @exception MessagingException
421: */
422: public synchronized String getUID(Message msg)
423: throws MessagingException {
424: checkOpen();
425: POP3Message m = (POP3Message) msg;
426: try {
427: if (m.uid == POP3Message.UNKNOWN)
428: m.uid = port.uidl(m.getMessageNumber());
429: return m.uid;
430: } catch (EOFException eex) {
431: close(false);
432: throw new FolderClosedException(this , eex.toString());
433: } catch (IOException ex) {
434: throw new MessagingException("error getting UIDL", ex);
435: }
436: }
437:
438: /**
439: * Return the size of this folder, as was returned by the POP3 STAT
440: * command when this folder was opened.
441: *
442: * @return folder size
443: * @exception IllegalStateException if the folder isn't open
444: */
445: public synchronized int getSize() throws MessagingException {
446: checkOpen();
447: return size;
448: }
449:
450: /**
451: * Return the sizes of all messages in this folder, as returned
452: * by the POP3 LIST command. Each entry in the array corresponds
453: * to a message; entry <i>i</i> corresponds to message number <i>i+1</i>.
454: *
455: * @return array of message sizes
456: * @exception IllegalStateException if the folder isn't open
457: * @since JavaMail 1.3.3
458: */
459: public synchronized int[] getSizes() throws MessagingException {
460: checkOpen();
461: int sizes[] = new int[total];
462: InputStream is = null;
463: LineInputStream lis = null;
464: try {
465: is = port.list();
466: lis = new LineInputStream(is);
467: String line;
468: while ((line = lis.readLine()) != null) {
469: try {
470: StringTokenizer st = new StringTokenizer(line);
471: int msgnum = Integer.parseInt(st.nextToken());
472: int size = Integer.parseInt(st.nextToken());
473: if (msgnum > 0 && msgnum <= total)
474: sizes[msgnum - 1] = size;
475: } catch (Exception e) {
476: }
477: }
478: } catch (IOException ex) {
479: // ignore it?
480: } finally {
481: try {
482: if (lis != null)
483: lis.close();
484: } catch (IOException cex) {
485: }
486: try {
487: if (is != null)
488: is.close();
489: } catch (IOException cex) {
490: }
491: }
492: return sizes;
493: }
494:
495: /**
496: * Return the raw results of the POP3 LIST command with no arguments.
497: *
498: * @return InputStream containing results
499: * @exception IllegalStateException if the folder isn't open
500: * @since JavaMail 1.3.3
501: */
502: public synchronized InputStream listCommand()
503: throws MessagingException, IOException {
504: checkOpen();
505: return port.list();
506: }
507:
508: /**
509: * Close the folder when we're finalized.
510: */
511: protected void finalize() throws Throwable {
512: super .finalize();
513: close(false);
514: }
515:
516: /* Ensure the folder is open */
517: void checkOpen() throws IllegalStateException {
518: if (!opened)
519: throw new IllegalStateException("Folder is not Open");
520: }
521:
522: /* Ensure the folder is not open */
523: void checkClosed() throws IllegalStateException {
524: if (opened)
525: throw new IllegalStateException("Folder is Open");
526: }
527:
528: /* Ensure the folder is open & readable */
529: void checkReadable() throws IllegalStateException {
530: if (!opened || (mode != READ_ONLY && mode != READ_WRITE))
531: throw new IllegalStateException("Folder is not Readable");
532: }
533:
534: /* Ensure the folder is open & writable */
535: void checkWritable() throws IllegalStateException {
536: if (!opened || mode != READ_WRITE)
537: throw new IllegalStateException("Folder is not Writable");
538: }
539:
540: /**
541: * Centralize access to the Protocol object by POP3Message
542: * objects so that they will fail appropriately when the folder
543: * is closed.
544: */
545: Protocol getProtocol() throws MessagingException {
546: checkOpen();
547: return port;
548: }
549:
550: /*
551: * Only here to make accessible to POP3Message.
552: */
553: protected void notifyMessageChangedListeners(int type, Message m) {
554: super.notifyMessageChangedListeners(type, m);
555: }
556: }
|