blob: 042c1704160ddeeb674a28d783468598c44d5abd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.geronimo.javamail.store.pop3;
import java.util.Vector;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.MethodNotSupportedException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.URLName;
import javax.mail.event.ConnectionEvent;
import org.apache.geronimo.javamail.store.pop3.message.POP3Message;
import org.apache.geronimo.javamail.store.pop3.message.POP3MessageFactory;
import org.apache.geronimo.javamail.store.pop3.response.POP3ResponseFactory;
import org.apache.geronimo.javamail.store.pop3.response.POP3StatusResponse;
/**
* The POP3 implementation of the javax.mail.Folder Note that only INBOX is
* supported in POP3
* <p>
* <url>http://www.faqs.org/rfcs/rfc1939.html</url>
* </p>
*
* @see javax.mail.Folder
*
* @version $Rev$ $Date$
*/
public class POP3Folder extends Folder {
private boolean isFolderOpen = false;
private int mode;
private POP3Connection pop3Con;
private int msgCount;
private Session session;
/**
* Vector is synchronized so choose over the other Collection impls This is
* initialized on open A chache will save the expensive operation of
* retrieving the message again from the server.
*/
private Vector msgCache;
protected POP3Folder(Store store, URLName url) {
super(store);
}
protected POP3Folder(Store store, Session session, POP3Connection pop3Con) {
super(store);
this.pop3Con = pop3Con;
this.session = session;
}
public String getName() {
return "INBOX";
}
public String getFullName() {
return "INBOX";
}
/**
* Never return "this" as the parent folder. Somebody not familliar with
* POP3 may do something like while(getParent() != null) or something
* simmilar which will result in an infinte loop
*/
public Folder getParent() throws MessagingException {
throw new MethodNotSupportedException("INBOX is the root folder");
}
public boolean exists() throws MessagingException {
// INBOX always exists at the backend
return true;
}
public Folder[] list(String pattern) throws MessagingException {
throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
}
/**
* No sub folders, hence there is no notion of a seperator
*/
public char getSeparator() throws MessagingException {
throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
}
public int getType() throws MessagingException {
return HOLDS_MESSAGES;
}
public boolean create(int type) throws MessagingException {
throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
}
public boolean hasNewMessages() throws MessagingException {
throw new MethodNotSupportedException("POP3 doesn't support this operation");
}
public Folder getFolder(String name) throws MessagingException {
throw new MethodNotSupportedException("Only INBOX is supported in POP3, no sub folders");
}
public boolean delete(boolean recurse) throws MessagingException {
throw new MethodNotSupportedException("Only INBOX is supported in POP3 and INBOX cannot be deleted");
}
public boolean renameTo(Folder f) throws MessagingException {
throw new MethodNotSupportedException("Only INBOX is supported in POP3 and INBOX cannot be renamed");
}
/**
* @see javax.mail.Folder#open(int)
*/
public void open(int mode) throws MessagingException {
// Can only be performed on a closed folder
checkClosed();
try {
POP3StatusResponse res = (POP3StatusResponse) POP3ResponseFactory.getStatusResponse(pop3Con
.sendCommand(POP3CommandFactory.getCOMMAND_STAT()));
// I am not checking for the res == null condition as the
// try catch block will handle it.
this.mode = mode;
this.isFolderOpen = true;
this.msgCount = res.getNumMessages();
// JavaMail API has no method in Folder to expose the total
// size (no of bytes) of the mail drop;
// NB: We use the actual message number to access the messages from
// the cache, which is origin 1. Vectors are origin 0, so we add one additional
// element and burn the
msgCache = new Vector(msgCount + 1);
msgCache.setSize(msgCount + 1);
} catch (Exception e) {
throw new MessagingException("Unable to execute STAT command", e);
}
notifyConnectionListeners(ConnectionEvent.OPENED);
}
public void close(boolean expunge) throws MessagingException {
// Can only be performed on an open folder
checkOpen();
try {
if (mode == READ_WRITE) {
// find all messages marked deleted and issue DELE commands
POP3Message m;
// NB: the first element in the cache is not used.
for (int i = 1; i < msgCache.size(); i++) {
if ((m = (POP3Message) msgCache.elementAt(i)) != null) {
if (m.isSet(Flags.Flag.DELETED)) {
try {
pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_DELE(i + 1));
} catch (Exception e) {
throw new MessagingException("Exception deleting message no [" + (i + 1)
+ "] during close", e);
}
}
}
}
}
try {
pop3Con.sendCommand(POP3CommandFactory.getCOMMAND_QUIT());
} catch (Exception e) {
// doesn't really care about the response
}
// dosn't need a catch block here, but added incase something goes
// wrong
// so that the finnaly is garunteed to execute in such a case.
} finally {
try {
pop3Con.close();
} catch (Exception e) {
// doesn't really care about the response
// all we can do is to set the reference explicitly to null
pop3Con = null;
}
/*
* The message numbers depend on the mail drop if the connection is
* closed, then purge the cache
*/
msgCache = null;
isFolderOpen = false;
notifyConnectionListeners(ConnectionEvent.CLOSED);
}
}
public boolean isOpen() {
return isFolderOpen;
}
public Flags getPermanentFlags() {
// unfortunately doesn't have a throws clause for this method
// throw new MethodNotSupportedException("POP3 doesn't support permanent
// flags");
// Better than returning null, save the extra condition from a user to
// check for null
// and avoids a NullPointerException for the careless.
return new Flags();
}
public int getMessageCount() throws MessagingException {
return msgCount;
}
/**
* Checks wether the message is in cache, if not will create a new message
* object and return it.
*
* @see javax.mail.Folder#getMessage(int)
*/
public Message getMessage(int msgNum) throws MessagingException {
// Can only be performed on an Open folder
checkOpen();
if (msgNum < 1 || msgNum > getMessageCount()) {
throw new MessagingException("Invalid Message number");
}
Message msg = null;
try {
msg = (Message) msgCache.elementAt(msgNum);
} catch (RuntimeException e) {
session.getDebugOut().println("Message not in cache");
}
if (msg == null) {
msg = POP3MessageFactory.createMessage(this, session, pop3Con, msgNum);
msgCache.setElementAt(msg, msgNum);
}
return msg;
}
public void appendMessages(Message[] msgs) throws MessagingException {
throw new MethodNotSupportedException("Message appending is not supported in POP3");
}
public Message[] expunge() throws MessagingException {
throw new MethodNotSupportedException("Expunge is not supported in POP3");
}
public int getMode() throws IllegalStateException {
// Can only be performed on an Open folder
checkOpen();
return mode;
}
/**
* @see javax.mail.Folder#fetch(javax.mail.Message[],
* javax.mail.FetchProfile)
*
* The JavaMail API recommends that this method be overrident to provide a
* meaningfull implementation.
*/
public void fetch(Message[] msgs, FetchProfile fp) throws MessagingException {
// Can only be performed on an Open folder
checkOpen();
for (int i = 0; i < msgs.length; i++) {
Message msg = msgs[i];
if (msg == null) {
msg = POP3MessageFactory.createMessage(this, session, pop3Con, i);
}
if (fp.contains(FetchProfile.Item.ENVELOPE)) {
msg = POP3MessageFactory.createMessageWithEvelope((POP3Message) msg);
}
if (fp.contains(FetchProfile.Item.CONTENT_INFO)) {
msg = POP3MessageFactory.createMessageWithContentInfo((POP3Message) msg);
}
if (fp.contains(FetchProfile.Item.FLAGS)) {
msg = POP3MessageFactory.createMessageWithFlags((POP3Message) msg);
}
msgs[i] = msg;
}
}
/**
* Below is a list of covinience methods that avoid repeated checking for a
* value and throwing an exception
*/
/** Ensure the folder is open */
private void checkOpen() throws IllegalStateException {
if (!isFolderOpen) {
throw new IllegalStateException("Folder is not Open");
}
}
/** Ensure the folder is not open */
private void checkClosed() throws IllegalStateException {
if (isFolderOpen) {
throw new IllegalStateException("Folder is Open");
}
}
/**
* @see javax.mail.Folder#notifyMessageChangedListeners(int,
* javax.mail.Message)
*
* this method is protected and cannot be used outside of Folder, therefore
* had to explicitly expose it via a method in POP3Folder, so that
* POP3Message has access to it
*
* Bad design on the part of the Java Mail API.
*/
public void notifyMessageChangedListeners(int type, Message m) {
super.notifyMessageChangedListeners(type, m);
}
}