blob: 16339bceb55ccf3c598d99c62be72f7b9502fdab [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.commons.net.pop3;
import java.io.IOException;
import java.io.Reader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ListIterator;
import java.util.StringTokenizer;
import org.apache.commons.net.io.DotTerminatedMessageReader;
/***
* The POP3Client class implements the client side of the Internet POP3
* Protocol defined in RFC 1939. All commands are supported, including
* the APOP command which requires MD5 encryption. See RFC 1939 for
* more details on the POP3 protocol.
* <p>
* Rather than list it separately for each method, we mention here that
* every method communicating with the server and throwing an IOException
* can also throw a
* {@link org.apache.commons.net.MalformedServerReplyException}
* , which is a subclass
* of IOException. A MalformedServerReplyException will be thrown when
* the reply received from the server deviates enough from the protocol
* specification that it cannot be interpreted in a useful manner despite
* attempts to be as lenient as possible.
*
*
* @see POP3MessageInfo
* @see org.apache.commons.net.io.DotTerminatedMessageReader
* @see org.apache.commons.net.MalformedServerReplyException
***/
public class POP3Client extends POP3
{
private static POP3MessageInfo __parseStatus(String line)
{
int num, size;
StringTokenizer tokenizer;
tokenizer = new StringTokenizer(line);
if (!tokenizer.hasMoreElements()) {
return null;
}
num = size = 0;
try
{
num = Integer.parseInt(tokenizer.nextToken());
if (!tokenizer.hasMoreElements()) {
return null;
}
size = Integer.parseInt(tokenizer.nextToken());
}
catch (NumberFormatException e)
{
return null;
}
return new POP3MessageInfo(num, size);
}
private static POP3MessageInfo __parseUID(String line)
{
int num;
StringTokenizer tokenizer;
tokenizer = new StringTokenizer(line);
if (!tokenizer.hasMoreElements()) {
return null;
}
num = 0;
try
{
num = Integer.parseInt(tokenizer.nextToken());
if (!tokenizer.hasMoreElements()) {
return null;
}
line = tokenizer.nextToken();
}
catch (NumberFormatException e)
{
return null;
}
return new POP3MessageInfo(num, line);
}
/***
* Send a CAPA command to the POP3 server.
* @return True if the command was successful, false if not.
* @throws IOException If a network I/O error occurs in the process of
* sending the CAPA command.
* @since 3.1 (was previously in ExtendedPOP3Client)
***/
public boolean capa() throws IOException
{
if (sendCommand(POP3Command.CAPA) == POP3Reply.OK) {
getAdditionalReply();
return true;
}
return false;
}
/***
* Login to the POP3 server with the given username and password. You
* must first connect to the server with
* {@link org.apache.commons.net.SocketClient#connect connect }
* before attempting to login. A login attempt is only valid if
* the client is in the
* {@link org.apache.commons.net.pop3.POP3#AUTHORIZATION_STATE AUTHORIZATION_STATE }
* . After logging in, the client enters the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* .
*
* @param username The account name being logged in to.
* @param password The plain text password of the account.
* @return True if the login attempt was successful, false if not.
* @throws IOException If a network I/O error occurs in the process of
* logging in.
***/
public boolean login(String username, String password) throws IOException
{
if (getState() != AUTHORIZATION_STATE) {
return false;
}
if (sendCommand(POP3Command.USER, username) != POP3Reply.OK) {
return false;
}
if (sendCommand(POP3Command.PASS, password) != POP3Reply.OK) {
return false;
}
setState(TRANSACTION_STATE);
return true;
}
/***
* Login to the POP3 server with the given username and authentication
* information. Use this method when connecting to a server requiring
* authentication using the APOP command. Because the timestamp
* produced in the greeting banner varies from server to server, it is
* not possible to consistently extract the information. Therefore,
* after connecting to the server, you must call
* {@link org.apache.commons.net.pop3.POP3#getReplyString getReplyString }
* and parse out the timestamp information yourself.
* <p>
* You must first connect to the server with
* {@link org.apache.commons.net.SocketClient#connect connect }
* before attempting to login. A login attempt is only valid if
* the client is in the
* {@link org.apache.commons.net.pop3.POP3#AUTHORIZATION_STATE AUTHORIZATION_STATE }
* . After logging in, the client enters the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* . After connecting, you must parse out the
* server specific information to use as a timestamp, and pass that
* information to this method. The secret is a shared secret known
* to you and the server. See RFC 1939 for more details regarding
* the APOP command.
*
* @param username The account name being logged in to.
* @param timestamp The timestamp string to combine with the secret.
* @param secret The shared secret which produces the MD5 digest when
* combined with the timestamp.
* @return True if the login attempt was successful, false if not.
* @throws IOException If a network I/O error occurs in the process of
* logging in.
* @throws NoSuchAlgorithmException If the MD5 encryption algorithm
* cannot be instantiated by the Java runtime system.
***/
public boolean login(String username, String timestamp, String secret)
throws IOException, NoSuchAlgorithmException
{
int i;
byte[] digest;
StringBuilder buffer, digestBuffer;
MessageDigest md5;
if (getState() != AUTHORIZATION_STATE) {
return false;
}
md5 = MessageDigest.getInstance("MD5");
timestamp += secret;
digest = md5.digest(timestamp.getBytes(getCharset()));
digestBuffer = new StringBuilder(128);
for (i = 0; i < digest.length; i++) {
int digit = digest[i] & 0xff;
if (digit <= 15) { // Add leading zero if necessary (NET-351)
digestBuffer.append("0");
}
digestBuffer.append(Integer.toHexString(digit));
}
buffer = new StringBuilder(256);
buffer.append(username);
buffer.append(' ');
buffer.append(digestBuffer.toString());
if (sendCommand(POP3Command.APOP, buffer.toString()) != POP3Reply.OK) {
return false;
}
setState(TRANSACTION_STATE);
return true;
}
/***
* Logout of the POP3 server. To fully disconnect from the server
* you must call
* {@link org.apache.commons.net.pop3.POP3#disconnect disconnect }.
* A logout attempt is valid in any state. If
* the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* , it enters the
* {@link org.apache.commons.net.pop3.POP3#UPDATE_STATE UPDATE_STATE }
* on a successful logout.
*
* @return True if the logout attempt was successful, false if not.
* @throws IOException If a network I/O error occurs in the process
* of logging out.
***/
public boolean logout() throws IOException
{
if (getState() == TRANSACTION_STATE) {
setState(UPDATE_STATE);
}
sendCommand(POP3Command.QUIT);
return (_replyCode == POP3Reply.OK);
}
/***
* Send a NOOP command to the POP3 server. This is useful for keeping
* a connection alive since most POP3 servers will timeout after 10
* minutes of inactivity. A noop attempt will only succeed if
* the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* .
*
* @return True if the noop attempt was successful, false if not.
* @throws IOException If a network I/O error occurs in the process of
* sending the NOOP command.
***/
public boolean noop() throws IOException
{
if (getState() == TRANSACTION_STATE) {
return (sendCommand(POP3Command.NOOP) == POP3Reply.OK);
}
return false;
}
/***
* Delete a message from the POP3 server. The message is only marked
* for deletion by the server. If you decide to unmark the message, you
* must issuse a {@link #reset reset } command. Messages marked
* for deletion are only deleted by the server on
* {@link #logout logout }.
* A delete attempt can only succeed if the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* .
*
* @param messageId The message number to delete.
* @return True if the deletion attempt was successful, false if not.
* @throws IOException If a network I/O error occurs in the process of
* sending the delete command.
***/
public boolean deleteMessage(int messageId) throws IOException
{
if (getState() == TRANSACTION_STATE) {
return (sendCommand(POP3Command.DELE, Integer.toString(messageId))
== POP3Reply.OK);
}
return false;
}
/***
* Reset the POP3 session. This is useful for undoing any message
* deletions that may have been performed. A reset attempt can only
* succeed if the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* .
*
* @return True if the reset attempt was successful, false if not.
* @throws IOException If a network I/O error occurs in the process of
* sending the reset command.
***/
public boolean reset() throws IOException
{
if (getState() == TRANSACTION_STATE) {
return (sendCommand(POP3Command.RSET) == POP3Reply.OK);
}
return false;
}
/***
* Get the mailbox status. A status attempt can only
* succeed if the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* . Returns a POP3MessageInfo instance
* containing the number of messages in the mailbox and the total
* size of the messages in bytes. Returns null if the status the
* attempt fails.
*
* @return A POP3MessageInfo instance containing the number of
* messages in the mailbox and the total size of the messages
* in bytes. Returns null if the status the attempt fails.
* @throws IOException If a network I/O error occurs in the process of
* sending the status command.
***/
public POP3MessageInfo status() throws IOException
{
if (getState() != TRANSACTION_STATE) {
return null;
}
if (sendCommand(POP3Command.STAT) != POP3Reply.OK) {
return null;
}
return __parseStatus(_lastReplyLine.substring(3));
}
/***
* List an individual message. A list attempt can only
* succeed if the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* . Returns a POP3MessageInfo instance
* containing the number of the listed message and the
* size of the message in bytes. Returns null if the list
* attempt fails (e.g., if the specified message number does
* not exist).
*
* @param messageId The number of the message list.
* @return A POP3MessageInfo instance containing the number of the
* listed message and the size of the message in bytes. Returns
* null if the list attempt fails.
* @throws IOException If a network I/O error occurs in the process of
* sending the list command.
***/
public POP3MessageInfo listMessage(int messageId) throws IOException
{
if (getState() != TRANSACTION_STATE) {
return null;
}
if (sendCommand(POP3Command.LIST, Integer.toString(messageId))
!= POP3Reply.OK) {
return null;
}
return __parseStatus(_lastReplyLine.substring(3));
}
/***
* List all messages. A list attempt can only
* succeed if the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* . Returns an array of POP3MessageInfo instances,
* each containing the number of a message and its size in bytes.
* If there are no messages, this method returns a zero length array.
* If the list attempt fails, it returns null.
*
* @return An array of POP3MessageInfo instances representing all messages
* in the order they appear in the mailbox,
* each containing the number of a message and its size in bytes.
* If there are no messages, this method returns a zero length array.
* If the list attempt fails, it returns null.
* @throws IOException If a network I/O error occurs in the process of
* sending the list command.
***/
public POP3MessageInfo[] listMessages() throws IOException
{
if (getState() != TRANSACTION_STATE) {
return null;
}
if (sendCommand(POP3Command.LIST) != POP3Reply.OK) {
return null;
}
getAdditionalReply();
// This could be a zero length array if no messages present
POP3MessageInfo[] messages = new POP3MessageInfo[_replyLines.size() - 2]; // skip first and last lines
ListIterator<String> en = _replyLines.listIterator(1); // Skip first line
// Fetch lines.
for (int line = 0; line < messages.length; line++) {
messages[line] = __parseStatus(en.next());
}
return messages;
}
/***
* List the unique identifier for a message. A list attempt can only
* succeed if the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* . Returns a POP3MessageInfo instance
* containing the number of the listed message and the
* unique identifier for that message. Returns null if the list
* attempt fails (e.g., if the specified message number does
* not exist).
*
* @param messageId The number of the message list.
* @return A POP3MessageInfo instance containing the number of the
* listed message and the unique identifier for that message.
* Returns null if the list attempt fails.
* @throws IOException If a network I/O error occurs in the process of
* sending the list unique identifier command.
***/
public POP3MessageInfo listUniqueIdentifier(int messageId)
throws IOException
{
if (getState() != TRANSACTION_STATE) {
return null;
}
if (sendCommand(POP3Command.UIDL, Integer.toString(messageId))
!= POP3Reply.OK) {
return null;
}
return __parseUID(_lastReplyLine.substring(3));
}
/***
* List the unique identifiers for all messages. A list attempt can only
* succeed if the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* . Returns an array of POP3MessageInfo instances,
* each containing the number of a message and its unique identifier.
* If there are no messages, this method returns a zero length array.
* If the list attempt fails, it returns null.
*
* @return An array of POP3MessageInfo instances representing all messages
* in the order they appear in the mailbox,
* each containing the number of a message and its unique identifier
* If there are no messages, this method returns a zero length array.
* If the list attempt fails, it returns null.
* @throws IOException If a network I/O error occurs in the process of
* sending the list unique identifier command.
***/
public POP3MessageInfo[] listUniqueIdentifiers() throws IOException
{
if (getState() != TRANSACTION_STATE) {
return null;
}
if (sendCommand(POP3Command.UIDL) != POP3Reply.OK) {
return null;
}
getAdditionalReply();
// This could be a zero length array if no messages present
POP3MessageInfo[] messages = new POP3MessageInfo[_replyLines.size() - 2]; // skip first and last lines
ListIterator<String> en = _replyLines.listIterator(1); // skip first line
// Fetch lines.
for (int line = 0; line < messages.length; line++) {
messages[line] = __parseUID(en.next());
}
return messages;
}
/**
* Retrieve a message from the POP3 server. A retrieve message attempt
* can only succeed if the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* <p>
* You must not issue any commands to the POP3 server (i.e., call any
* other methods) until you finish reading the message from the
* returned BufferedReader instance.
* The POP3 protocol uses the same stream for issuing commands as it does
* for returning results. Therefore the returned BufferedReader actually reads
* directly from the POP3 connection. After the end of message has been
* reached, new commands can be executed and their replies read. If
* you do not follow these requirements, your program will not work
* properly.
*
* @param messageId The number of the message to fetch.
* @return A DotTerminatedMessageReader instance
* from which the entire message can be read.
* This can safely be cast to a {@link java.io.BufferedReader BufferedReader} in order to
* use the {@link java.io.BufferedReader#readLine() BufferedReader#readLine()} method.
* Returns null if the retrieval attempt fails (e.g., if the specified
* message number does not exist).
* @throws IOException If a network I/O error occurs in the process of
* sending the retrieve message command.
*/
public Reader retrieveMessage(int messageId) throws IOException
{
if (getState() != TRANSACTION_STATE) {
return null;
}
if (sendCommand(POP3Command.RETR, Integer.toString(messageId)) != POP3Reply.OK) {
return null;
}
return new DotTerminatedMessageReader(_reader);
}
/**
* Retrieve only the specified top number of lines of a message from the
* POP3 server. A retrieve top lines attempt
* can only succeed if the client is in the
* {@link org.apache.commons.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE }
* <p>
* You must not issue any commands to the POP3 server (i.e., call any
* other methods) until you finish reading the message from the returned
* BufferedReader instance.
* The POP3 protocol uses the same stream for issuing commands as it does
* for returning results. Therefore the returned BufferedReader actually reads
* directly from the POP3 connection. After the end of message has been
* reached, new commands can be executed and their replies read. If
* you do not follow these requirements, your program will not work
* properly.
*
* @param messageId The number of the message to fetch.
* @param numLines The top number of lines to fetch. This must be &gt;= 0.
* @return A DotTerminatedMessageReader instance
* from which the specified top number of lines of the message can be
* read.
* This can safely be cast to a {@link java.io.BufferedReader BufferedReader} in order to
* use the {@link java.io.BufferedReader#readLine() BufferedReader#readLine()} method.
* Returns null if the retrieval attempt fails (e.g., if the specified
* message number does not exist).
* @throws IOException If a network I/O error occurs in the process of
* sending the top command.
*/
public Reader retrieveMessageTop(int messageId, int numLines)
throws IOException
{
if (numLines < 0 || getState() != TRANSACTION_STATE) {
return null;
}
if (sendCommand(POP3Command.TOP, Integer.toString(messageId) + " " +
Integer.toString(numLines)) != POP3Reply.OK) {
return null;
}
return new DotTerminatedMessageReader(_reader);
}
}