Java tutorial
/**************************************************************** * 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.james.pop3server; import org.apache.avalon.cornerstone.services.connection.ConnectionHandler; import org.apache.avalon.excalibur.pool.Poolable; import org.apache.avalon.framework.container.ContainerUtil; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.commons.collections.ListUtils; import org.apache.james.Constants; import org.apache.james.core.MailImpl; import org.apache.james.services.MailRepository; import org.apache.james.util.CRLFTerminatedReader; import org.apache.james.util.ExtraDotOutputStream; import org.apache.james.util.InternetPrintWriter; import org.apache.james.util.watchdog.BytesWrittenResetOutputStream; import org.apache.james.util.watchdog.Watchdog; import org.apache.james.util.watchdog.WatchdogTarget; import org.apache.mailet.Mail; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.StringTokenizer; /** * The handler class for POP3 connections. * */ public class POP3Handler extends AbstractLogEnabled implements ConnectionHandler, Poolable { // POP3 Server identification string used in POP3 headers private static final String softwaretype = "JAMES POP3 Server " + Constants.SOFTWARE_VERSION; // POP3 response prefixes private final static String OK_RESPONSE = "+OK"; // OK response. Requested content // will follow private final static String ERR_RESPONSE = "-ERR"; // Error response. Requested content // will not be provided. This prefix // is followed by a more detailed // error message // Authentication states for the POP3 interaction private final static int AUTHENTICATION_READY = 0; // Waiting for user id private final static int AUTHENTICATION_USERSET = 1; // User id provided, waiting for // password private final static int TRANSACTION = 2; // A valid user id/password combination // has been provided. In this state // the client can access the mailbox // of the specified user private static final Mail DELETED = new MailImpl(); // A placeholder for emails deleted // during the course of the POP3 // transaction. This Mail instance // is used to enable fast checks as // to whether an email has been // deleted from the inbox. /** * The per-service configuration data that applies to all handlers */ private POP3HandlerConfigurationData theConfigData; /** * The mail server's copy of the user's inbox */ private MailRepository userInbox; /** * The thread executing this handler */ private Thread handlerThread; /** * The TCP/IP socket over which the POP3 interaction * is occurring */ private Socket socket; /** * The reader associated with incoming characters. */ private CRLFTerminatedReader in; /** * The writer to which outgoing messages are written. */ private PrintWriter out; /** * The socket's output stream */ private OutputStream outs; /** * The current transaction state of the handler */ private int state; /** * The user id associated with the POP3 dialogue */ private String user; /** * A dynamic list representing the set of * emails in the user's inbox at any given time * during the POP3 transaction. */ private ArrayList userMailbox = new ArrayList(); private ArrayList backupUserMailbox; // A snapshot list representing the set of // emails in the user's inbox at the beginning // of the transaction /** * The watchdog being used by this handler to deal with idle timeouts. */ private Watchdog theWatchdog; /** * The watchdog target that idles out this handler. */ private WatchdogTarget theWatchdogTarget = new POP3WatchdogTarget(); /** * Set the configuration data for the handler. * * @param theData the configuration data */ void setConfigurationData(POP3HandlerConfigurationData theData) { theConfigData = theData; } /** * Set the Watchdog for use by this handler. * * @param theWatchdog the watchdog */ void setWatchdog(Watchdog theWatchdog) { this.theWatchdog = theWatchdog; } /** * Gets the Watchdog Target that should be used by Watchdogs managing * this connection. * * @return the WatchdogTarget */ WatchdogTarget getWatchdogTarget() { return theWatchdogTarget; } /** * Idle out this connection */ void idleClose() { if (getLogger() != null) { getLogger().error("POP3 Connection has idled out."); } try { if (socket != null) { socket.close(); } } catch (Exception e) { // ignored } finally { socket = null; } synchronized (this) { // Interrupt the thread to recover from internal hangs if (handlerThread != null) { handlerThread.interrupt(); handlerThread = null; } } } /** * @see org.apache.avalon.cornerstone.services.connection.ConnectionHandler#handleConnection(Socket) */ public void handleConnection(Socket connection) throws IOException { String remoteHost = ""; String remoteIP = ""; try { this.socket = connection; synchronized (this) { handlerThread = Thread.currentThread(); } // in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "ASCII"), 512); in = new CRLFTerminatedReader(new BufferedInputStream(socket.getInputStream(), 512), "ASCII"); remoteIP = socket.getInetAddress().getHostAddress(); remoteHost = socket.getInetAddress().getHostName(); } catch (Exception e) { if (getLogger().isErrorEnabled()) { StringBuffer exceptionBuffer = new StringBuffer(256).append("Cannot open connection from ") .append(remoteHost).append(" (").append(remoteIP).append("): ").append(e.getMessage()); getLogger().error(exceptionBuffer.toString(), e); } } if (getLogger().isInfoEnabled()) { StringBuffer logBuffer = new StringBuffer(128).append("Connection from ").append(remoteHost) .append(" (").append(remoteIP).append(") "); getLogger().info(logBuffer.toString()); } try { outs = new BufferedOutputStream(socket.getOutputStream(), 1024); out = new InternetPrintWriter(outs, true); state = AUTHENTICATION_READY; user = "unknown"; StringBuffer responseBuffer = new StringBuffer(256).append(OK_RESPONSE).append(" ") .append(theConfigData.getHelloName()).append(" POP3 server (").append(POP3Handler.softwaretype) .append(") ready "); out.println(responseBuffer.toString()); theWatchdog.start(); while (parseCommand(readCommandLine())) { theWatchdog.reset(); } theWatchdog.stop(); if (getLogger().isInfoEnabled()) { StringBuffer logBuffer = new StringBuffer(128).append("Connection for ").append(user) .append(" from ").append(remoteHost).append(" (").append(remoteIP).append(") closed."); getLogger().info(logBuffer.toString()); } } catch (Exception e) { out.println(ERR_RESPONSE + " Error closing connection."); out.flush(); StringBuffer exceptionBuffer = new StringBuffer(128).append("Exception during connection from ") .append(remoteHost).append(" (").append(remoteIP).append(") : ").append(e.getMessage()); getLogger().error(exceptionBuffer.toString(), e); } finally { resetHandler(); } } /** * Resets the handler data to a basic state. */ private void resetHandler() { if (theWatchdog != null) { ContainerUtil.dispose(theWatchdog); theWatchdog = null; } // Close and clear streams, sockets try { if (socket != null) { socket.close(); socket = null; } } catch (IOException ioe) { // Ignoring exception on close } finally { socket = null; } try { if (in != null) { in.close(); } } catch (Exception e) { // Ignored } finally { in = null; } try { if (out != null) { out.close(); } } catch (Exception e) { // Ignored } finally { out = null; } try { if (outs != null) { outs.close(); } } catch (Exception e) { // Ignored } finally { outs = null; } synchronized (this) { handlerThread = null; } // Clear user data user = null; userInbox = null; if (userMailbox != null) { userMailbox.clear(); userMailbox = null; } if (backupUserMailbox != null) { backupUserMailbox.clear(); backupUserMailbox = null; } // Clear config data theConfigData = null; } /** * Implements a "stat". If the handler is currently in * a transaction state, this amounts to a rollback of the * mailbox contents to the beginning of the transaction. * This method is also called when first entering the * transaction state to initialize the handler copies of the * user inbox. * */ private void stat() { userMailbox = new ArrayList(); userMailbox.add(DELETED); try { for (Iterator it = userInbox.list(); it.hasNext();) { String key = (String) it.next(); Mail mc = userInbox.retrieve(key); // Retrieve can return null if the mail is no longer in the store. // In this case we simply continue to the next key if (mc == null) { continue; } userMailbox.add(mc); } } catch (MessagingException e) { // In the event of an exception being thrown there may or may not be anything in userMailbox getLogger().error("Unable to STAT mail box ", e); } finally { backupUserMailbox = (ArrayList) userMailbox.clone(); } } /** * Reads a line of characters off the command line. * * @return the trimmed input line * @throws IOException if an exception is generated reading in the input characters */ final String readCommandLine() throws IOException { for (;;) try { String commandLine = in.readLine(); if (commandLine != null) { commandLine = commandLine.trim(); } return commandLine; } catch (CRLFTerminatedReader.TerminationException te) { writeLoggedFlushedResponse("-ERR Syntax error at character position " + te.position() + ". CR and LF must be CRLF paired. See RFC 1939 #3."); } } /** * This method parses POP3 commands read off the wire in handleConnection. * Actual processing of the command (possibly including additional back and * forth communication with the client) is delegated to one of a number of * command specific handler methods. The primary purpose of this method is * to parse the raw command string to determine exactly which handler should * be called. It returns true if expecting additional commands, false otherwise. * * @param rawCommand the raw command string passed in over the socket * * @return whether additional commands are expected. */ private boolean parseCommand(String rawCommand) { if (rawCommand == null) { return false; } boolean returnValue = true; String command = rawCommand; StringTokenizer commandLine = new StringTokenizer(command, " "); int arguments = commandLine.countTokens(); if (arguments == 0) { return true; } else if (arguments > 0) { command = commandLine.nextToken().toUpperCase(Locale.US); } if (getLogger().isDebugEnabled()) { // Don't display password in logger if (!command.equals("PASS")) { getLogger().debug("Command received: " + rawCommand); } else { getLogger().debug("Command received: PASS <password omitted>"); } } String argument = null; if (arguments > 1) { argument = commandLine.nextToken(); } String argument1 = null; if (arguments > 2) { argument1 = commandLine.nextToken(); } if (command.equals("USER")) { doUSER(command, argument, argument1); } else if (command.equals("PASS")) { doPASS(command, argument, argument1); } else if (command.equals("STAT")) { doSTAT(command, argument, argument1); } else if (command.equals("LIST")) { doLIST(command, argument, argument1); } else if (command.equals("UIDL")) { doUIDL(command, argument, argument1); } else if (command.equals("RSET")) { doRSET(command, argument, argument1); } else if (command.equals("DELE")) { doDELE(command, argument, argument1); } else if (command.equals("NOOP")) { doNOOP(command, argument, argument1); } else if (command.equals("RETR")) { doRETR(command, argument, argument1); } else if (command.equals("TOP")) { doTOP(command, argument, argument1); } else if (command.equals("QUIT")) { returnValue = false; doQUIT(command, argument, argument1); } else { doUnknownCmd(command, argument, argument1); } return returnValue; } /** * Handler method called upon receipt of a USER command. * Reads in the user id. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doUSER(String command, String argument, String argument1) { String responseString = null; if (state == AUTHENTICATION_READY && argument != null) { user = argument; state = AUTHENTICATION_USERSET; responseString = OK_RESPONSE; } else { responseString = ERR_RESPONSE; } writeLoggedFlushedResponse(responseString); } /** * Handler method called upon receipt of a PASS command. * Reads in and validates the password. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doPASS(String command, String argument, String argument1) { String responseString = null; if (state == AUTHENTICATION_USERSET && argument != null) { String passArg = argument; if (theConfigData.getUsersRepository().test(user, passArg)) { StringBuffer responseBuffer = new StringBuffer(64).append(OK_RESPONSE).append(" Welcome ") .append(user); responseString = responseBuffer.toString(); state = TRANSACTION; writeLoggedFlushedResponse(responseString); userInbox = theConfigData.getMailServer().getUserInbox(user); stat(); } else { responseString = ERR_RESPONSE + " Authentication failed."; state = AUTHENTICATION_READY; writeLoggedFlushedResponse(responseString); } } else { responseString = ERR_RESPONSE; writeLoggedFlushedResponse(responseString); } } /** * Handler method called upon receipt of a STAT command. * Returns the number of messages in the mailbox and its * aggregate size. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doSTAT(String command, String argument, String argument1) { String responseString = null; if (state == TRANSACTION) { long size = 0; int count = 0; try { for (Iterator i = userMailbox.iterator(); i.hasNext();) { Mail mc = (Mail) i.next(); if (mc != DELETED) { size += mc.getMessageSize(); count++; } } StringBuffer responseBuffer = new StringBuffer(32).append(OK_RESPONSE).append(" ").append(count) .append(" ").append(size); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } catch (MessagingException me) { responseString = ERR_RESPONSE; writeLoggedFlushedResponse(responseString); } } else { responseString = ERR_RESPONSE; writeLoggedFlushedResponse(responseString); } } /** * Handler method called upon receipt of a LIST command. * Returns the number of messages in the mailbox and its * aggregate size, or optionally, the number and size of * a single message. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doLIST(String command, String argument, String argument1) { String responseString = null; if (state == TRANSACTION) { if (argument == null) { long size = 0; int count = 0; try { for (Iterator i = userMailbox.iterator(); i.hasNext();) { Mail mc = (Mail) i.next(); if (mc != DELETED) { size += mc.getMessageSize(); count++; } } StringBuffer responseBuffer = new StringBuffer(32).append(OK_RESPONSE).append(" ").append(count) .append(" ").append(size); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); count = 0; for (Iterator i = userMailbox.iterator(); i.hasNext(); count++) { Mail mc = (Mail) i.next(); if (mc != DELETED) { responseBuffer = new StringBuffer(16).append(count).append(" ") .append(mc.getMessageSize()); out.println(responseBuffer.toString()); } } out.println("."); out.flush(); } catch (MessagingException me) { responseString = ERR_RESPONSE; writeLoggedFlushedResponse(responseString); } } else { int num = 0; try { num = Integer.parseInt(argument); Mail mc = (Mail) userMailbox.get(num); if (mc != DELETED) { StringBuffer responseBuffer = new StringBuffer(64).append(OK_RESPONSE).append(" ") .append(num).append(" ").append(mc.getMessageSize()); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } else { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" Message (") .append(num).append(") already deleted."); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } } catch (IndexOutOfBoundsException npe) { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" Message (") .append(num).append(") does not exist."); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } catch (NumberFormatException nfe) { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" ") .append(argument).append(" is not a valid number"); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } catch (MessagingException me) { responseString = ERR_RESPONSE; writeLoggedFlushedResponse(responseString); } } } else { responseString = ERR_RESPONSE; writeLoggedFlushedResponse(responseString); } } /** * Handler method called upon receipt of a UIDL command. * Returns a listing of message ids to the client. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doUIDL(String command, String argument, String argument1) { String responseString = null; if (state == TRANSACTION) { if (argument == null) { responseString = OK_RESPONSE + " unique-id listing follows"; writeLoggedFlushedResponse(responseString); int count = 0; for (Iterator i = userMailbox.iterator(); i.hasNext(); count++) { Mail mc = (Mail) i.next(); if (mc != DELETED) { StringBuffer responseBuffer = new StringBuffer(64).append(count).append(" ") .append(mc.getName()); out.println(responseBuffer.toString()); } } out.println("."); out.flush(); } else { int num = 0; try { num = Integer.parseInt(argument); Mail mc = (Mail) userMailbox.get(num); if (mc != DELETED) { StringBuffer responseBuffer = new StringBuffer(64).append(OK_RESPONSE).append(" ") .append(num).append(" ").append(mc.getName()); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } else { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" Message (") .append(num).append(") already deleted."); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } } catch (IndexOutOfBoundsException npe) { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" Message (") .append(num).append(") does not exist."); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } catch (NumberFormatException nfe) { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" ") .append(argument).append(" is not a valid number"); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } } } else { writeLoggedFlushedResponse(ERR_RESPONSE); } } /** * Handler method called upon receipt of a RSET command. * Calls stat() to reset the mailbox. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doRSET(String command, String argument, String argument1) { String responseString = null; if (state == TRANSACTION) { stat(); responseString = OK_RESPONSE; } else { responseString = ERR_RESPONSE; } writeLoggedFlushedResponse(responseString); } /** * Handler method called upon receipt of a DELE command. * This command deletes a particular mail message from the * mailbox. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doDELE(String command, String argument, String argument1) { String responseString = null; if (state == TRANSACTION) { int num = 0; try { num = Integer.parseInt(argument); } catch (Exception e) { responseString = ERR_RESPONSE + " Usage: DELE [mail number]"; writeLoggedFlushedResponse(responseString); return; } try { Mail mc = (Mail) userMailbox.get(num); if (mc == DELETED) { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" Message (") .append(num).append(") already deleted."); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } else { userMailbox.set(num, DELETED); writeLoggedFlushedResponse(OK_RESPONSE + " Message deleted"); } } catch (IndexOutOfBoundsException iob) { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" Message (") .append(num).append(") does not exist."); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } } else { responseString = ERR_RESPONSE; writeLoggedFlushedResponse(responseString); } } /** * Handler method called upon receipt of a NOOP command. * Like all good NOOPs, does nothing much. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doNOOP(String command, String argument, String argument1) { String responseString = null; if (state == TRANSACTION) { responseString = OK_RESPONSE; writeLoggedFlushedResponse(responseString); } else { responseString = ERR_RESPONSE; writeLoggedFlushedResponse(responseString); } } /** * Handler method called upon receipt of a RETR command. * This command retrieves a particular mail message from the * mailbox. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doRETR(String command, String argument, String argument1) { String responseString = null; if (state == TRANSACTION) { int num = 0; try { num = Integer.parseInt(argument.trim()); } catch (Exception e) { responseString = ERR_RESPONSE + " Usage: RETR [mail number]"; writeLoggedFlushedResponse(responseString); return; } try { Mail mc = (Mail) userMailbox.get(num); if (mc != DELETED) { responseString = OK_RESPONSE + " Message follows"; writeLoggedFlushedResponse(responseString); try { ExtraDotOutputStream edouts = new ExtraDotOutputStream(outs); OutputStream nouts = new BytesWrittenResetOutputStream(edouts, theWatchdog, theConfigData.getResetLength()); mc.getMessage().writeTo(nouts); nouts.flush(); edouts.checkCRLFTerminator(); edouts.flush(); } finally { out.println("."); out.flush(); } } else { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" Message (") .append(num).append(") already deleted."); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } } catch (IOException ioe) { responseString = ERR_RESPONSE + " Error while retrieving message."; writeLoggedFlushedResponse(responseString); } catch (MessagingException me) { responseString = ERR_RESPONSE + " Error while retrieving message."; writeLoggedFlushedResponse(responseString); } catch (IndexOutOfBoundsException iob) { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" Message (") .append(num).append(") does not exist."); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } } else { responseString = ERR_RESPONSE; writeLoggedFlushedResponse(responseString); } } /** * Handler method called upon receipt of a TOP command. * This command retrieves the top N lines of a specified * message in the mailbox. * * The expected command format is * TOP [mail message number] [number of lines to return] * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doTOP(String command, String argument, String argument1) { String responseString = null; if (state == TRANSACTION) { int num = 0; int lines = 0; try { num = Integer.parseInt(argument); lines = Integer.parseInt(argument1); } catch (NumberFormatException nfe) { responseString = ERR_RESPONSE + " Usage: TOP [mail number] [Line number]"; writeLoggedFlushedResponse(responseString); return; } try { Mail mc = (Mail) userMailbox.get(num); if (mc != DELETED) { responseString = OK_RESPONSE + " Message follows"; writeLoggedFlushedResponse(responseString); try { for (Enumeration e = mc.getMessage().getAllHeaderLines(); e.hasMoreElements();) { out.println(e.nextElement()); } out.println(); ExtraDotOutputStream edouts = new ExtraDotOutputStream(outs); OutputStream nouts = new BytesWrittenResetOutputStream(edouts, theWatchdog, theConfigData.getResetLength()); writeMessageContentTo(mc.getMessage(), nouts, lines); nouts.flush(); edouts.checkCRLFTerminator(); edouts.flush(); } finally { out.println("."); out.flush(); } } else { StringBuffer responseBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" Message (") .append(num).append(") already deleted."); responseString = responseBuffer.toString(); writeLoggedFlushedResponse(responseString); } } catch (IOException ioe) { responseString = ERR_RESPONSE + " Error while retrieving message."; writeLoggedFlushedResponse(responseString); } catch (MessagingException me) { responseString = ERR_RESPONSE + " Error while retrieving message."; writeLoggedFlushedResponse(responseString); } catch (IndexOutOfBoundsException iob) { StringBuffer exceptionBuffer = new StringBuffer(64).append(ERR_RESPONSE).append(" Message (") .append(num).append(") does not exist."); responseString = exceptionBuffer.toString(); writeLoggedFlushedResponse(responseString); } } else { responseString = ERR_RESPONSE; writeLoggedFlushedResponse(responseString); } } /** * Writes the content of the message, up to a total number of lines, out to * an OutputStream. * * @param out the OutputStream to which to write the content * @param lines the number of lines to write to the stream * * @throws MessagingException if the MimeMessage is not set for this MailImpl * @throws IOException if an error occurs while reading or writing from the stream */ public void writeMessageContentTo(MimeMessage message, OutputStream out, int lines) throws IOException, MessagingException { String line; BufferedReader br; if (message != null) { br = new BufferedReader(new InputStreamReader(message.getRawInputStream())); try { while (lines-- > 0) { if ((line = br.readLine()) == null) { break; } line += "\r\n"; out.write(line.getBytes()); } } finally { br.close(); } } else { throw new MessagingException("No message set for this MailImpl."); } } /** * Handler method called upon receipt of a QUIT command. * This method handles cleanup of the POP3Handler state. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doQUIT(String command, String argument, String argument1) { String responseString = null; if (state == AUTHENTICATION_READY || state == AUTHENTICATION_USERSET) { responseString = OK_RESPONSE + " Apache James POP3 Server signing off."; writeLoggedFlushedResponse(responseString); return; } List toBeRemoved = ListUtils.subtract(backupUserMailbox, userMailbox); try { userInbox.remove(toBeRemoved); // for (Iterator it = toBeRemoved.iterator(); it.hasNext(); ) { // Mail mc = (Mail) it.next(); // userInbox.remove(mc.getName()); //} responseString = OK_RESPONSE + " Apache James POP3 Server signing off."; writeLoggedFlushedResponse(responseString); } catch (Exception ex) { responseString = ERR_RESPONSE + " Some deleted messages were not removed"; writeLoggedFlushedResponse(responseString); getLogger().error("Some deleted messages were not removed: " + ex.getMessage()); } } /** * Handler method called upon receipt of an unrecognized command. * Returns an error response and logs the command. * * @param command the command parsed by the parseCommand method * @param argument the first argument parsed by the parseCommand method * @param argument1 the second argument parsed by the parseCommand method */ private void doUnknownCmd(String command, String argument, String argument1) { writeLoggedFlushedResponse(ERR_RESPONSE); } /** * This method logs at a "DEBUG" level the response string that * was sent to the POP3 client. The method is provided largely * as syntactic sugar to neaten up the code base. It is declared * private and final to encourage compiler inlining. * * @param responseString the response string sent to the client */ private final void logResponseString(String responseString) { if (getLogger().isDebugEnabled()) { getLogger().debug("Sent: " + responseString); } } /** * Write and flush a response string. The response is also logged. * Should be used for the last line of a multi-line response or * for a single line response. * * @param responseString the response string sent to the client */ final void writeLoggedFlushedResponse(String responseString) { out.println(responseString); out.flush(); logResponseString(responseString); } /** * Write a response string. The response is also logged. * Used for multi-line responses. * * @param responseString the response string sent to the client */ final void writeLoggedResponse(String responseString) { out.println(responseString); logResponseString(responseString); } /** * A private inner class which serves as an adaptor * between the WatchdogTarget interface and this * handler class. */ private class POP3WatchdogTarget implements WatchdogTarget { /** * @see org.apache.james.util.watchdog.WatchdogTarget#execute() */ public void execute() { POP3Handler.this.idleClose(); } } }