All Downloads are FREE. Search and download functionalities are using the official Maven repository.

panda.net.pop3.POP3Client Maven / Gradle / Ivy

There is a newer version: 1.8.0
Show newest version
package panda.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 panda.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.
 * 

* 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 panda.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 panda.net.io.DotTerminatedMessageReader * @see panda.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. * @exception IOException If a network I/O error occurs in the process of sending the CAPA * command. ***/ 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 panda.net.SocketClient#connect connect } before attempting to login. A * login attempt is only valid if the client is in the * {@link panda.net.pop3.POP3#AUTHORIZATION_STATE AUTHORIZATION_STATE } . After logging in, the * client enters the {@link panda.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. * @exception 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 panda.net.pop3.POP3#getReplyString getReplyString } and parse out the * timestamp information yourself. *

* You must first connect to the server with {@link panda.net.SocketClient#connect connect } * before attempting to login. A login attempt is only valid if the client is in the * {@link panda.net.pop3.POP3#AUTHORIZATION_STATE AUTHORIZATION_STATE } . After logging in, the * client enters the {@link panda.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. * @exception IOException If a network I/O error occurs in the process of logging in. * @exception 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(getCharsetName())); // Java 1.6 can use 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 panda.net.pop3.POP3#disconnect disconnect }. A logout attempt is valid in any state. * If the client is in the {@link panda.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } , it * enters the {@link panda.net.pop3.POP3#UPDATE_STATE UPDATE_STATE } on a successful logout. * * @return True if the logout attempt was successful, false if not. * @exception 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 panda.net.pop3.POP3#TRANSACTION_STATE * TRANSACTION_STATE } . * * @return True if the noop attempt was successful, false if not. * @exception 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 panda.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. * @exception 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 panda.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } . * * @return True if the reset attempt was successful, false if not. * @exception 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 panda.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. * @exception 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 panda.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. * @exception 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 panda.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. * @exception 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 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 panda.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. * @exception 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 panda.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. * @exception 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 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 panda.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } *

* 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). * @exception 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 panda.net.pop3.POP3#TRANSACTION_STATE TRANSACTION_STATE } *

* 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 >= 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). * @exception 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); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy