0N/A/*
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0N/A *
829N/A * Copyright (c) 1997-2016 Oracle and/or its affiliates. All rights reserved.
0N/A *
0N/A * The contents of this file are subject to the terms of either the GNU
0N/A * General Public License Version 2 only ("GPL") or the Common Development
0N/A * and Distribution License("CDDL") (collectively, the "License"). You
292N/A * may not use this file except in compliance with the License. You can
292N/A * obtain a copy of the License at
292N/A * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
292N/A * or packager/legal/LICENSE.txt. See the License for the specific
0N/A * language governing permissions and limitations under the License.
0N/A *
0N/A * When distributing the software, include this License Header Notice in each
292N/A * file and include the License file at packager/legal/LICENSE.txt.
292N/A *
292N/A * GPL Classpath Exception:
292N/A * Oracle designates this particular file as subject to the "Classpath"
292N/A * exception as provided by Oracle in the GPL Version 2 section of the License
292N/A * file that accompanied this code.
292N/A *
292N/A * Modifications:
292N/A * If applicable, add the following below the License Header, with the fields
292N/A * enclosed by brackets [] replaced by your own identifying information:
292N/A * "Portions Copyright [year] [name of copyright owner]"
0N/A *
0N/A * Contributor(s):
0N/A * If you wish your version of this file to be governed by only the CDDL or
0N/A * only the GPL Version 2, indicate your decision by adding "[Contributor]
0N/A * elects to include this software in this distribution under the [CDDL or GPL
0N/A * Version 2] license." If you don't indicate a single choice of license, a
0N/A * recipient has the option to distribute your version of this file under
0N/A * either the CDDL, the GPL Version 2 or to extend the choice of license to
0N/A * its licensees as provided above. However, if you add GPL Version 2 code
0N/A * and therefore, elected the GPL Version 2 license, then the option applies
0N/A * only if the new code is made subject to such option by the copyright
0N/A * holder.
0N/A */
0N/A
0N/Apackage com.sun.mail.pop3;
0N/A
0N/Aimport javax.mail.*;
0N/Aimport javax.mail.event.*;
0N/Aimport java.io.InputStream;
0N/Aimport java.io.IOException;
0N/Aimport java.io.EOFException;
0N/Aimport java.util.StringTokenizer;
470N/Aimport java.util.logging.Level;
0N/Aimport java.lang.reflect.Constructor;
0N/A
0N/Aimport com.sun.mail.util.LineInputStream;
470N/Aimport com.sun.mail.util.MailLogger;
788N/Aimport java.util.ArrayList;
788N/Aimport java.util.List;
0N/A
0N/A/**
0N/A * A POP3 Folder (can only be "INBOX").
0N/A *
0N/A * See the <a href="package-summary.html">com.sun.mail.pop3</a> package
0N/A * documentation for further information on the POP3 protocol provider. <p>
0N/A *
0N/A * @author Bill Shannon
0N/A * @author John Mani (ported to the javax.mail APIs)
0N/A */
0N/Apublic class POP3Folder extends Folder {
0N/A
0N/A private String name;
240N/A private POP3Store store;
255N/A private volatile Protocol port;
0N/A private int total;
0N/A private int size;
0N/A private boolean exists = false;
255N/A private volatile boolean opened = false;
788N/A private POP3Message[] message_cache;
0N/A private boolean doneUidl = false;
255N/A private volatile TempFile fileCache = null;
829N/A private boolean forceClose;
0N/A
470N/A MailLogger logger; // package private, for POP3Message
470N/A
686N/A protected POP3Folder(POP3Store store, String name) {
0N/A super(store);
0N/A this.name = name;
240N/A this.store = store;
0N/A if (name.equalsIgnoreCase("INBOX"))
0N/A exists = true;
470N/A logger = new MailLogger(this.getClass(),
470N/A "DEBUG POP3", store.getSession());
0N/A }
0N/A
0N/A public String getName() {
0N/A return name;
0N/A }
0N/A
0N/A public String getFullName() {
0N/A return name;
0N/A }
0N/A
0N/A public Folder getParent() {
240N/A return new DefaultFolder(store);
0N/A }
0N/A
0N/A /**
0N/A * Always true for the folder "INBOX", always false for
0N/A * any other name.
0N/A *
0N/A * @return true for INBOX, false otherwise
0N/A */
0N/A public boolean exists() {
0N/A return exists;
0N/A }
0N/A
0N/A /**
0N/A * Always throws <code>MessagingException</code> because no POP3 folders
0N/A * can contain subfolders.
0N/A *
0N/A * @exception MessagingException always
0N/A */
0N/A public Folder[] list(String pattern) throws MessagingException {
0N/A throw new MessagingException("not a directory");
0N/A }
0N/A
0N/A /**
0N/A * Always returns a NUL character because POP3 doesn't support a hierarchy.
0N/A *
0N/A * @return NUL
0N/A */
0N/A public char getSeparator() {
0N/A return '\0';
0N/A }
0N/A
0N/A /**
0N/A * Always returns Folder.HOLDS_MESSAGES.
0N/A *
0N/A * @return Folder.HOLDS_MESSAGES
0N/A */
0N/A public int getType() {
0N/A return HOLDS_MESSAGES;
0N/A }
0N/A
0N/A /**
0N/A * Always returns <code>false</code>; the POP3 protocol doesn't
0N/A * support creating folders.
0N/A *
0N/A * @return false
0N/A */
0N/A public boolean create(int type) throws MessagingException {
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Always returns <code>false</code>; the POP3 protocol provides
0N/A * no way to determine when a new message arrives.
0N/A *
0N/A * @return false
0N/A */
0N/A public boolean hasNewMessages() throws MessagingException {
0N/A return false; // no way to know
0N/A }
0N/A
0N/A /**
0N/A * Always throws <code>MessagingException</code> because no POP3 folders
0N/A * can contain subfolders.
0N/A *
0N/A * @exception MessagingException always
0N/A */
0N/A public Folder getFolder(String name) throws MessagingException {
0N/A throw new MessagingException("not a directory");
0N/A }
0N/A
0N/A /**
0N/A * Always throws <code>MethodNotSupportedException</code>
0N/A * because the POP3 protocol doesn't allow the INBOX to
0N/A * be deleted.
0N/A *
0N/A * @exception MethodNotSupportedException always
0N/A */
0N/A public boolean delete(boolean recurse) throws MessagingException {
0N/A throw new MethodNotSupportedException("delete");
0N/A }
0N/A
0N/A /**
0N/A * Always throws <code>MethodNotSupportedException</code>
0N/A * because the POP3 protocol doesn't support multiple folders.
0N/A *
0N/A * @exception MethodNotSupportedException always
0N/A */
0N/A public boolean renameTo(Folder f) throws MessagingException {
0N/A throw new MethodNotSupportedException("renameTo");
0N/A }
0N/A
0N/A /**
0N/A * Throws <code>FolderNotFoundException</code> unless this
0N/A * folder is named "INBOX".
0N/A *
0N/A * @exception FolderNotFoundException if not INBOX
610N/A * @exception AuthenticationFailedException authentication failures
0N/A * @exception MessagingException other open failures
0N/A */
0N/A public synchronized void open(int mode) throws MessagingException {
0N/A checkClosed();
0N/A if (!exists)
0N/A throw new FolderNotFoundException(this, "folder is not INBOX");
0N/A
0N/A try {
240N/A port = store.getPort(this);
0N/A Status s = port.stat();
0N/A total = s.total;
0N/A size = s.size;
0N/A this.mode = mode;
240N/A if (store.useFileCache) {
240N/A try {
240N/A fileCache = new TempFile(store.fileCacheDir);
240N/A } catch (IOException ex) {
470N/A logger.log(Level.FINE, "failed to create file cache", ex);
240N/A throw ex; // caught below
240N/A }
240N/A }
0N/A opened = true;
0N/A } catch (IOException ioex) {
0N/A try {
0N/A if (port != null)
0N/A port.quit();
0N/A } catch (IOException ioex2) {
0N/A // ignore
0N/A } finally {
0N/A port = null;
240N/A store.closePort(this);
0N/A }
0N/A throw new MessagingException("Open failed", ioex);
0N/A }
0N/A
788N/A // Create the message cache array of appropriate size
788N/A message_cache = new POP3Message[total];
0N/A doneUidl = false;
0N/A
0N/A notifyConnectionListeners(ConnectionEvent.OPENED);
0N/A }
0N/A
0N/A public synchronized void close(boolean expunge) throws MessagingException {
0N/A checkOpen();
0N/A
0N/A try {
0N/A /*
0N/A * Some POP3 servers will mark messages for deletion when
0N/A * they're read. To prevent such messages from being
0N/A * deleted before the client deletes them, you can set
0N/A * the mail.pop3.rsetbeforequit property to true. This
0N/A * causes us to issue a POP3 RSET command to clear all
0N/A * the "marked for deletion" flags. We can then explicitly
0N/A * delete messages as desired.
0N/A */
829N/A if (store.rsetBeforeQuit && !forceClose)
0N/A port.rset();
411N/A POP3Message m;
829N/A if (expunge && mode == READ_WRITE && !forceClose) {
0N/A // find all messages marked deleted and issue DELE commands
788N/A for (int i = 0; i < message_cache.length; i++) {
788N/A if ((m = message_cache[i]) != null) {
0N/A if (m.isSet(Flags.Flag.DELETED))
0N/A try {
0N/A port.dele(i + 1);
0N/A } catch (IOException ioex) {
0N/A throw new MessagingException(
0N/A "Exception deleting messages during close",
0N/A ioex);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
411N/A /*
411N/A * Flush and free all cached data for the messages.
411N/A */
788N/A for (int i = 0; i < message_cache.length; i++) {
788N/A if ((m = message_cache[i]) != null)
411N/A m.invalidate(true);
411N/A }
411N/A
829N/A if (forceClose)
829N/A port.close();
829N/A else
829N/A port.quit();
0N/A } catch (IOException ex) {
0N/A // do nothing
0N/A } finally {
0N/A port = null;
240N/A store.closePort(this);
0N/A message_cache = null;
0N/A opened = false;
0N/A notifyConnectionListeners(ConnectionEvent.CLOSED);
240N/A if (fileCache != null) {
240N/A fileCache.close();
240N/A fileCache = null;
240N/A }
0N/A }
0N/A }
0N/A
144N/A public synchronized boolean isOpen() {
0N/A if (!opened)
0N/A return false;
0N/A try {
144N/A if (!port.noop())
144N/A throw new IOException("NOOP failed");
144N/A } catch (IOException ioex) {
144N/A try {
144N/A close(false);
144N/A } catch (MessagingException mex) {
144N/A // ignore it
144N/A } finally {
144N/A return false;
144N/A }
144N/A }
144N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Always returns an empty <code>Flags</code> object because
0N/A * the POP3 protocol doesn't support any permanent flags.
0N/A *
0N/A * @return empty Flags object
0N/A */
0N/A public Flags getPermanentFlags() {
0N/A return new Flags(); // empty flags object
0N/A }
0N/A
0N/A /**
0N/A * Will not change while the folder is open because the POP3
0N/A * protocol doesn't support notification of new messages
0N/A * arriving in open folders.
0N/A */
0N/A public synchronized int getMessageCount() throws MessagingException {
0N/A if (!opened)
0N/A return -1;
0N/A checkReadable();
0N/A return total;
0N/A }
0N/A
0N/A public synchronized Message getMessage(int msgno)
0N/A throws MessagingException {
0N/A checkOpen();
0N/A
0N/A POP3Message m;
0N/A
0N/A // Assuming that msgno is <= total
788N/A if ((m = message_cache[msgno-1]) == null) {
0N/A m = createMessage(this, msgno);
788N/A message_cache[msgno-1] = m;
0N/A }
0N/A return m;
0N/A }
0N/A
0N/A protected POP3Message createMessage(Folder f, int msgno)
0N/A throws MessagingException {
0N/A POP3Message m = null;
787N/A Constructor<?> cons = store.messageConstructor;
0N/A if (cons != null) {
0N/A try {
583N/A Object[] o = { this, Integer.valueOf(msgno) };
0N/A m = (POP3Message)cons.newInstance(o);
0N/A } catch (Exception ex) {
0N/A // ignore
0N/A }
0N/A }
0N/A if (m == null)
0N/A m = new POP3Message(this, msgno);
0N/A return m;
0N/A }
0N/A
0N/A /**
0N/A * Always throws <code>MethodNotSupportedException</code>
0N/A * because the POP3 protocol doesn't support appending messages.
0N/A *
0N/A * @exception MethodNotSupportedException always
0N/A */
0N/A public void appendMessages(Message[] msgs) throws MessagingException {
0N/A throw new MethodNotSupportedException("Append not supported");
0N/A }
0N/A
0N/A /**
0N/A * Always throws <code>MethodNotSupportedException</code>
0N/A * because the POP3 protocol doesn't support expunging messages
0N/A * without closing the folder; call the {@link #close close} method
0N/A * with the <code>expunge</code> argument set to <code>true</code>
0N/A * instead.
0N/A *
0N/A * @exception MethodNotSupportedException always
0N/A */
0N/A public Message[] expunge() throws MessagingException {
0N/A throw new MethodNotSupportedException("Expunge not supported");
0N/A }
0N/A
0N/A /**
0N/A * Prefetch information about POP3 messages.
0N/A * If the FetchProfile contains <code>UIDFolder.FetchProfileItem.UID</code>,
0N/A * POP3 UIDs for all messages in the folder are fetched using the POP3
0N/A * UIDL command.
0N/A * If the FetchProfile contains <code>FetchProfile.Item.ENVELOPE</code>,
0N/A * the headers and size of all messages are fetched using the POP3 TOP
0N/A * and LIST commands.
0N/A */
0N/A public synchronized void fetch(Message[] msgs, FetchProfile fp)
0N/A throws MessagingException {
0N/A checkReadable();
240N/A if (!doneUidl && store.supportsUidl &&
138N/A fp.contains(UIDFolder.FetchProfileItem.UID)) {
0N/A /*
0N/A * Since the POP3 protocol only lets us fetch the UID
0N/A * for a single message or for all messages, we go ahead
0N/A * and fetch UIDs for all messages here, ignoring the msgs
0N/A * parameter. We could be more intelligent and base this
0N/A * decision on the number of messages fetched, or the
0N/A * percentage of the total number of messages fetched.
0N/A */
788N/A String[] uids = new String[message_cache.length];
0N/A try {
0N/A if (!port.uidl(uids))
0N/A return;
0N/A } catch (EOFException eex) {
0N/A close(false);
0N/A throw new FolderClosedException(this, eex.toString());
0N/A } catch (IOException ex) {
0N/A throw new MessagingException("error getting UIDL", ex);
0N/A }
0N/A for (int i = 0; i < uids.length; i++) {
0N/A if (uids[i] == null)
0N/A continue;
0N/A POP3Message m = (POP3Message)getMessage(i + 1);
0N/A m.uid = uids[i];
0N/A }
0N/A doneUidl = true; // only do this once
0N/A }
0N/A if (fp.contains(FetchProfile.Item.ENVELOPE)) {
0N/A for (int i = 0; i < msgs.length; i++) {
0N/A try {
0N/A POP3Message msg = (POP3Message)msgs[i];
0N/A // fetch headers
0N/A msg.getHeader("");
0N/A // fetch message size
0N/A msg.getSize();
0N/A } catch (MessageRemovedException mex) {
0N/A // should never happen, but ignore it if it does
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return the unique ID string for this message, or null if
0N/A * not available. Uses the POP3 UIDL command.
0N/A *
610N/A * @param msg the message
0N/A * @return unique ID string
610N/A * @exception MessagingException for failures
0N/A */
0N/A public synchronized String getUID(Message msg) throws MessagingException {
0N/A checkOpen();
583N/A if (!(msg instanceof POP3Message))
583N/A throw new MessagingException("message is not a POP3Message");
0N/A POP3Message m = (POP3Message)msg;
0N/A try {
240N/A if (!store.supportsUidl)
138N/A return null;
0N/A if (m.uid == POP3Message.UNKNOWN)
0N/A m.uid = port.uidl(m.getMessageNumber());
0N/A return m.uid;
0N/A } catch (EOFException eex) {
0N/A close(false);
0N/A throw new FolderClosedException(this, eex.toString());
0N/A } catch (IOException ex) {
0N/A throw new MessagingException("error getting UIDL", ex);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Return the size of this folder, as was returned by the POP3 STAT
0N/A * command when this folder was opened.
0N/A *
0N/A * @return folder size
0N/A * @exception IllegalStateException if the folder isn't open
610N/A * @exception MessagingException for other failures
0N/A */
0N/A public synchronized int getSize() throws MessagingException {
0N/A checkOpen();
0N/A return size;
0N/A }
0N/A
0N/A /**
0N/A * Return the sizes of all messages in this folder, as returned
0N/A * by the POP3 LIST command. Each entry in the array corresponds
0N/A * to a message; entry <i>i</i> corresponds to message number <i>i+1</i>.
0N/A *
0N/A * @return array of message sizes
0N/A * @exception IllegalStateException if the folder isn't open
610N/A * @exception MessagingException for other failures
0N/A * @since JavaMail 1.3.3
0N/A */
0N/A public synchronized int[] getSizes() throws MessagingException {
0N/A checkOpen();
0N/A int sizes[] = new int[total];
0N/A InputStream is = null;
0N/A LineInputStream lis = null;
0N/A try {
0N/A is = port.list();
0N/A lis = new LineInputStream(is);
0N/A String line;
0N/A while ((line = lis.readLine()) != null) {
0N/A try {
0N/A StringTokenizer st = new StringTokenizer(line);
0N/A int msgnum = Integer.parseInt(st.nextToken());
0N/A int size = Integer.parseInt(st.nextToken());
0N/A if (msgnum > 0 && msgnum <= total)
0N/A sizes[msgnum - 1] = size;
583N/A } catch (RuntimeException e) {
0N/A }
0N/A }
0N/A } catch (IOException ex) {
0N/A // ignore it?
0N/A } finally {
0N/A try {
0N/A if (lis != null)
0N/A lis.close();
0N/A } catch (IOException cex) { }
0N/A try {
0N/A if (is != null)
0N/A is.close();
0N/A } catch (IOException cex) { }
0N/A }
0N/A return sizes;
0N/A }
0N/A
0N/A /**
0N/A * Return the raw results of the POP3 LIST command with no arguments.
0N/A *
0N/A * @return InputStream containing results
0N/A * @exception IllegalStateException if the folder isn't open
610N/A * @exception IOException for I/O errors talking to the server
610N/A * @exception MessagingException for other errors
0N/A * @since JavaMail 1.3.3
0N/A */
0N/A public synchronized InputStream listCommand()
0N/A throws MessagingException, IOException {
0N/A checkOpen();
0N/A return port.list();
0N/A }
0N/A
0N/A /**
0N/A * Close the folder when we're finalized.
0N/A */
0N/A protected void finalize() throws Throwable {
829N/A forceClose = !store.finalizeCleanClose;
741N/A try {
741N/A if (opened)
741N/A close(false);
741N/A } finally {
741N/A super.finalize();
829N/A forceClose = false;
741N/A }
0N/A }
0N/A
0N/A /* Ensure the folder is open */
255N/A private void checkOpen() throws IllegalStateException {
0N/A if (!opened)
0N/A throw new IllegalStateException("Folder is not Open");
0N/A }
0N/A
0N/A /* Ensure the folder is not open */
255N/A private void checkClosed() throws IllegalStateException {
0N/A if (opened)
0N/A throw new IllegalStateException("Folder is Open");
0N/A }
0N/A
0N/A /* Ensure the folder is open & readable */
255N/A private void checkReadable() throws IllegalStateException {
0N/A if (!opened || (mode != READ_ONLY && mode != READ_WRITE))
0N/A throw new IllegalStateException("Folder is not Readable");
0N/A }
0N/A
0N/A /* Ensure the folder is open & writable */
255N/A /*
255N/A private void checkWritable() throws IllegalStateException {
0N/A if (!opened || mode != READ_WRITE)
0N/A throw new IllegalStateException("Folder is not Writable");
0N/A }
255N/A */
0N/A
0N/A /**
0N/A * Centralize access to the Protocol object by POP3Message
0N/A * objects so that they will fail appropriately when the folder
0N/A * is closed.
0N/A */
0N/A Protocol getProtocol() throws MessagingException {
255N/A Protocol p = port; // read it before close() can set it to null
0N/A checkOpen();
255N/A // close() might happen here
255N/A return p;
0N/A }
0N/A
0N/A /*
0N/A * Only here to make accessible to POP3Message.
0N/A */
0N/A protected void notifyMessageChangedListeners(int type, Message m) {
0N/A super.notifyMessageChangedListeners(type, m);
0N/A }
240N/A
240N/A /**
240N/A * Used by POP3Message.
240N/A */
331N/A TempFile getFileCache() {
240N/A return fileCache;
240N/A }
0N/A}