KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > james > pop3server > POP3Handler


1 /***********************************************************************
2  * Copyright (c) 2000-2004 The Apache Software Foundation. *
3  * All rights reserved. *
4  * ------------------------------------------------------------------- *
5  * Licensed under the Apache License, Version 2.0 (the "License"); you *
6  * may not use this file except in compliance with the License. You *
7  * may obtain a copy of the License at: *
8  * *
9  * http://www.apache.org/licenses/LICENSE-2.0 *
10  * *
11  * Unless required by applicable law or agreed to in writing, software *
12  * distributed under the License is distributed on an "AS IS" BASIS, *
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
14  * implied. See the License for the specific language governing *
15  * permissions and limitations under the License. *
16  ***********************************************************************/

17
18 package org.apache.james.pop3server;
19
20 import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
21 import org.apache.avalon.excalibur.collections.ListUtils;
22 import org.apache.avalon.excalibur.pool.Poolable;
23 import org.apache.avalon.framework.activity.Disposable;
24 import org.apache.avalon.framework.logger.AbstractLogEnabled;
25
26 import org.apache.james.Constants;
27 import org.apache.james.core.MailImpl;
28 import org.apache.james.services.MailRepository;
29 import org.apache.james.services.MailServer;
30 import org.apache.james.services.UsersRepository;
31 import org.apache.james.services.UsersStore;
32 import org.apache.james.util.CRLFTerminatedReader;
33 import org.apache.james.util.ExtraDotOutputStream;
34 import org.apache.james.util.InternetPrintWriter;
35 import org.apache.james.util.watchdog.BytesWrittenResetOutputStream;
36 import org.apache.james.util.watchdog.Watchdog;
37 import org.apache.james.util.watchdog.WatchdogTarget;
38 import org.apache.mailet.Mail;
39
40 import javax.mail.MessagingException JavaDoc;
41 import java.io.*;
42 import java.net.Socket JavaDoc;
43 import java.util.*;
44
45 /**
46  * The handler class for POP3 connections.
47  *
48  */

49 public class POP3Handler
50     extends AbstractLogEnabled
51     implements ConnectionHandler, Poolable {
52
53     // POP3 Server identification string used in POP3 headers
54
private static final String JavaDoc softwaretype = "JAMES POP3 Server "
55                                                         + Constants.SOFTWARE_VERSION;
56
57     // POP3 response prefixes
58
private final static String JavaDoc OK_RESPONSE = "+OK"; // OK response. Requested content
59
// will follow
60

61     private final static String JavaDoc ERR_RESPONSE = "-ERR"; // Error response. Requested content
62
// will not be provided. This prefix
63
// is followed by a more detailed
64
// error message
65

66     // Authentication states for the POP3 interaction
67

68     private final static int AUTHENTICATION_READY = 0; // Waiting for user id
69

70     private final static int AUTHENTICATION_USERSET = 1; // User id provided, waiting for
71
// password
72

73     private final static int TRANSACTION = 2; // A valid user id/password combination
74
// has been provided. In this state
75
// the client can access the mailbox
76
// of the specified user
77

78     private static final Mail DELETED = new MailImpl(); // A placeholder for emails deleted
79
// during the course of the POP3
80
// transaction. This Mail instance
81
// is used to enable fast checks as
82
// to whether an email has been
83
// deleted from the inbox.
84

85     /**
86      * The per-service configuration data that applies to all handlers
87      */

88     private POP3HandlerConfigurationData theConfigData;
89
90     /**
91      * The mail server's copy of the user's inbox
92      */

93     private MailRepository userInbox;
94
95     /**
96      * The thread executing this handler
97      */

98     private Thread JavaDoc handlerThread;
99
100     /**
101      * The TCP/IP socket over which the POP3 interaction
102      * is occurring
103      */

104     private Socket JavaDoc socket;
105
106     /**
107      * The reader associated with incoming characters.
108      */

109     private BufferedReader in;
110
111     /**
112      * The writer to which outgoing messages are written.
113      */

114     private PrintWriter out;
115
116     /**
117      * The socket's output stream
118      */

119     private OutputStream outs;
120
121     /**
122      * The current transaction state of the handler
123      */

124     private int state;
125
126     /**
127      * The user id associated with the POP3 dialogue
128      */

129     private String JavaDoc user;
130
131     /**
132      * A dynamic list representing the set of
133      * emails in the user's inbox at any given time
134      * during the POP3 transaction.
135      */

136     private ArrayList userMailbox = new ArrayList();
137
138     private ArrayList backupUserMailbox; // A snapshot list representing the set of
139
// emails in the user's inbox at the beginning
140
// of the transaction
141

142     /**
143      * The watchdog being used by this handler to deal with idle timeouts.
144      */

145     private Watchdog theWatchdog;
146
147     /**
148      * The watchdog target that idles out this handler.
149      */

150     private WatchdogTarget theWatchdogTarget = new POP3WatchdogTarget();
151
152     /**
153      * Set the configuration data for the handler.
154      *
155      * @param theData the configuration data
156      */

157     void setConfigurationData(POP3HandlerConfigurationData theData) {
158         theConfigData = theData;
159     }
160
161     /**
162      * Set the Watchdog for use by this handler.
163      *
164      * @param theWatchdog the watchdog
165      */

166     void setWatchdog(Watchdog theWatchdog) {
167         this.theWatchdog = theWatchdog;
168     }
169
170     /**
171      * Gets the Watchdog Target that should be used by Watchdogs managing
172      * this connection.
173      *
174      * @return the WatchdogTarget
175      */

176     WatchdogTarget getWatchdogTarget() {
177         return theWatchdogTarget;
178     }
179
180     /**
181      * Idle out this connection
182      */

183     void idleClose() {
184         if (getLogger() != null) {
185             getLogger().error("POP3 Connection has idled out.");
186         }
187         try {
188             if (socket != null) {
189                 socket.close();
190             }
191         } catch (Exception JavaDoc e) {
192             // ignored
193
} finally {
194             socket = null;
195         }
196
197         synchronized (this) {
198             // Interrupt the thread to recover from internal hangs
199
if (handlerThread != null) {
200                 handlerThread.interrupt();
201                 handlerThread = null;
202             }
203         }
204
205     }
206
207     /**
208      * @see org.apache.avalon.cornerstone.services.connection.ConnectionHandler#handleConnection(Socket)
209      */

210     public void handleConnection( Socket JavaDoc connection )
211             throws IOException {
212
213         String JavaDoc remoteHost = "";
214         String JavaDoc remoteIP = "";
215
216         try {
217             this.socket = connection;
218             synchronized (this) {
219                 handlerThread = Thread.currentThread();
220             }
221             // in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "ASCII"), 512);
222
in = new CRLFTerminatedReader(new BufferedInputStream(socket.getInputStream(), 512), "ASCII");
223             remoteIP = socket.getInetAddress().getHostAddress ();
224             remoteHost = socket.getInetAddress().getHostName ();
225         } catch (Exception JavaDoc e) {
226             if (getLogger().isErrorEnabled()) {
227                 StringBuffer JavaDoc exceptionBuffer =
228                     new StringBuffer JavaDoc(256)
229                             .append("Cannot open connection from ")
230                             .append(remoteHost)
231                             .append(" (")
232                             .append(remoteIP)
233                             .append("): ")
234                             .append(e.getMessage());
235                 getLogger().error( exceptionBuffer.toString(), e );
236             }
237         }
238
239         if (getLogger().isInfoEnabled()) {
240             StringBuffer JavaDoc logBuffer =
241                 new StringBuffer JavaDoc(128)
242                         .append("Connection from ")
243                         .append(remoteHost)
244                         .append(" (")
245                         .append(remoteIP)
246                         .append(") ");
247             getLogger().info(logBuffer.toString());
248         }
249
250         try {
251             outs = new BufferedOutputStream(socket.getOutputStream(), 1024);
252             out = new InternetPrintWriter(outs, true);
253             state = AUTHENTICATION_READY;
254             user = "unknown";
255             StringBuffer JavaDoc responseBuffer =
256                 new StringBuffer JavaDoc(256)
257                         .append(OK_RESPONSE)
258                         .append(" ")
259                         .append(theConfigData.getHelloName())
260                         .append(" POP3 server (")
261                         .append(POP3Handler.softwaretype)
262                         .append(") ready ");
263             out.println(responseBuffer.toString());
264
265             theWatchdog.start();
266             while (parseCommand(readCommandLine())) {
267                 theWatchdog.reset();
268             }
269             theWatchdog.stop();
270             if (getLogger().isInfoEnabled()) {
271                 StringBuffer JavaDoc logBuffer =
272                     new StringBuffer JavaDoc(128)
273                         .append("Connection for ")
274                         .append(user)
275                         .append(" from ")
276                         .append(remoteHost)
277                         .append(" (")
278                         .append(remoteIP)
279                         .append(") closed.");
280                 getLogger().info(logBuffer.toString());
281             }
282         } catch (Exception JavaDoc e) {
283             out.println(ERR_RESPONSE + " Error closing connection.");
284             out.flush();
285             StringBuffer JavaDoc exceptionBuffer =
286                 new StringBuffer JavaDoc(128)
287                         .append("Exception during connection from ")
288                         .append(remoteHost)
289                         .append(" (")
290                         .append(remoteIP)
291                         .append(") : ")
292                         .append(e.getMessage());
293             getLogger().error(exceptionBuffer.toString(), e );
294         } finally {
295             resetHandler();
296         }
297     }
298
299     /**
300      * Resets the handler data to a basic state.
301      */

302     private void resetHandler() {
303
304         if (theWatchdog != null) {
305             if (theWatchdog instanceof Disposable) {
306                 ((Disposable)theWatchdog).dispose();
307             }
308             theWatchdog = null;
309         }
310
311         // Close and clear streams, sockets
312

313         try {
314             if (socket != null) {
315                 socket.close();
316                 socket = null;
317             }
318         } catch (IOException ioe) {
319             // Ignoring exception on close
320
} finally {
321             socket = null;
322         }
323
324         try {
325             if (in != null) {
326                 in.close();
327             }
328         } catch (Exception JavaDoc e) {
329             // Ignored
330
} finally {
331             in = null;
332         }
333
334         try {
335             if (out != null) {
336                 out.close();
337             }
338         } catch (Exception JavaDoc e) {
339             // Ignored
340
} finally {
341             out = null;
342         }
343
344         try {
345            if (outs != null) {
346                outs.close();
347             }
348         } catch (Exception JavaDoc e) {
349             // Ignored
350
} finally {
351             outs = null;
352         }
353
354         synchronized (this) {
355             handlerThread = null;
356         }
357
358         // Clear user data
359
user = null;
360         userInbox = null;
361         if (userMailbox != null) {
362             userMailbox.clear();
363             userMailbox = null;
364         }
365
366         if (backupUserMailbox != null) {
367             backupUserMailbox.clear();
368             backupUserMailbox = null;
369         }
370
371         // Clear config data
372
theConfigData = null;
373     }
374
375     /**
376      * Implements a "stat". If the handler is currently in
377      * a transaction state, this amounts to a rollback of the
378      * mailbox contents to the beginning of the transaction.
379      * This method is also called when first entering the
380      * transaction state to initialize the handler copies of the
381      * user inbox.
382      *
383      */

384     private void stat() {
385         userMailbox = new ArrayList();
386         userMailbox.add(DELETED);
387         try {
388             for (Iterator it = userInbox.list(); it.hasNext(); ) {
389                 String JavaDoc key = (String JavaDoc) it.next();
390                 Mail mc = userInbox.retrieve(key);
391                 // Retrieve can return null if the mail is no longer in the store.
392
// In this case we simply continue to the next key
393
if (mc == null) {
394                     continue;
395                 }
396                 userMailbox.add(mc);
397             }
398         } catch(MessagingException JavaDoc e) {
399             // In the event of an exception being thrown there may or may not be anything in userMailbox
400
getLogger().error("Unable to STAT mail box ", e);
401         }
402         finally {
403             backupUserMailbox = (ArrayList) userMailbox.clone();
404         }
405     }
406
407     /**
408      * Reads a line of characters off the command line.
409      *
410      * @return the trimmed input line
411      * @throws IOException if an exception is generated reading in the input characters
412      */

413     final String JavaDoc readCommandLine() throws IOException {
414         for (;;) try {
415             String JavaDoc commandLine = in.readLine();
416             if (commandLine != null) {
417                 commandLine = commandLine.trim();
418             }
419             return commandLine;
420         } catch (CRLFTerminatedReader.TerminationException te) {
421             writeLoggedFlushedResponse("-ERR Syntax error at character position " + te.position() + ". CR and LF must be CRLF paired. See RFC 1939 #3.");
422         }
423     }
424
425     /**
426      * This method parses POP3 commands read off the wire in handleConnection.
427      * Actual processing of the command (possibly including additional back and
428      * forth communication with the client) is delegated to one of a number of
429      * command specific handler methods. The primary purpose of this method is
430      * to parse the raw command string to determine exactly which handler should
431      * be called. It returns true if expecting additional commands, false otherwise.
432      *
433      * @param rawCommand the raw command string passed in over the socket
434      *
435      * @return whether additional commands are expected.
436      */

437     private boolean parseCommand(String JavaDoc rawCommand) {
438         if (rawCommand == null) {
439             return false;
440         }
441         boolean returnValue = true;
442         String JavaDoc command = rawCommand;
443         StringTokenizer commandLine = new StringTokenizer(command, " ");
444         int arguments = commandLine.countTokens();
445         if (arguments == 0) {
446             return true;
447         } else if(arguments > 0) {
448             command = commandLine.nextToken().toUpperCase(Locale.US);
449         }
450         if (getLogger().isDebugEnabled()) {
451             // Don't display password in logger
452
if (!command.equals("PASS")) {
453                 getLogger().debug("Command received: " + rawCommand);
454             } else {
455                 getLogger().debug("Command received: PASS <password omitted>");
456             }
457         }
458         String JavaDoc argument = (String JavaDoc) null;
459         if(arguments > 1) {
460             argument = commandLine.nextToken();
461         }
462         String JavaDoc argument1 = (String JavaDoc) null;
463         if(arguments > 2) {
464             argument1 = commandLine.nextToken();
465         }
466
467         if (command.equals("USER")) {
468             doUSER(command,argument,argument1);
469         } else if (command.equals("PASS")) {
470             doPASS(command,argument,argument1);
471         } else if (command.equals("STAT")) {
472             doSTAT(command,argument,argument1);
473         } else if (command.equals("LIST")) {
474             doLIST(command,argument,argument1);
475         } else if (command.equals("UIDL")) {
476             doUIDL(command,argument,argument1);
477         } else if (command.equals("RSET")) {
478             doRSET(command,argument,argument1);
479         } else if (command.equals("DELE")) {
480             doDELE(command,argument,argument1);
481         } else if (command.equals("NOOP")) {
482             doNOOP(command,argument,argument1);
483         } else if (command.equals("RETR")) {
484             doRETR(command,argument,argument1);
485         } else if (command.equals("TOP")) {
486             doTOP(command,argument,argument1);
487         } else if (command.equals("QUIT")) {
488             returnValue = false;
489             doQUIT(command,argument,argument1);
490         } else {
491             doUnknownCmd(command,argument,argument1);
492         }
493         return returnValue;
494     }
495
496     /**
497      * Handler method called upon receipt of a USER command.
498      * Reads in the user id.
499      *
500      * @param command the command parsed by the parseCommand method
501      * @param argument the first argument parsed by the parseCommand method
502      * @param argument1 the second argument parsed by the parseCommand method
503      */

504     private void doUSER(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
505         String JavaDoc responseString = null;
506         if (state == AUTHENTICATION_READY && argument != null) {
507             user = argument;
508             state = AUTHENTICATION_USERSET;
509             responseString = OK_RESPONSE;
510         } else {
511             responseString = ERR_RESPONSE;
512         }
513         writeLoggedFlushedResponse(responseString);
514     }
515
516     /**
517      * Handler method called upon receipt of a PASS command.
518      * Reads in and validates the password.
519      *
520      * @param command the command parsed by the parseCommand method
521      * @param argument the first argument parsed by the parseCommand method
522      * @param argument1 the second argument parsed by the parseCommand method
523      */

524     private void doPASS(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
525         String JavaDoc responseString = null;
526         if (state == AUTHENTICATION_USERSET && argument != null) {
527             String JavaDoc passArg = argument;
528             if (theConfigData.getUsersRepository().test(user, passArg)) {
529                 StringBuffer JavaDoc responseBuffer =
530                     new StringBuffer JavaDoc(64)
531                             .append(OK_RESPONSE)
532                             .append(" Welcome ")
533                             .append(user);
534                 responseString = responseBuffer.toString();
535                 state = TRANSACTION;
536                 writeLoggedFlushedResponse(responseString);
537                 userInbox = theConfigData.getMailServer().getUserInbox(user);
538                 stat();
539             } else {
540                 responseString = ERR_RESPONSE + " Authentication failed.";
541                 state = AUTHENTICATION_READY;
542                 writeLoggedFlushedResponse(responseString);
543             }
544         } else {
545             responseString = ERR_RESPONSE;
546             writeLoggedFlushedResponse(responseString);
547         }
548     }
549
550     /**
551      * Handler method called upon receipt of a STAT command.
552      * Returns the number of messages in the mailbox and its
553      * aggregate size.
554      *
555      * @param command the command parsed by the parseCommand method
556      * @param argument the first argument parsed by the parseCommand method
557      * @param argument1 the second argument parsed by the parseCommand method
558      */

559     private void doSTAT(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
560         String JavaDoc responseString = null;
561         if (state == TRANSACTION) {
562             long size = 0;
563             int count = 0;
564             try {
565                 for (Iterator i = userMailbox.iterator(); i.hasNext(); ) {
566                     MailImpl mc = (MailImpl) i.next();
567                     if (mc != DELETED) {
568                         size += mc.getMessageSize();
569                         count++;
570                     }
571                 }
572                 StringBuffer JavaDoc responseBuffer =
573                     new StringBuffer JavaDoc(32)
574                             .append(OK_RESPONSE)
575                             .append(" ")
576                             .append(count)
577                             .append(" ")
578                             .append(size);
579                 responseString = responseBuffer.toString();
580                 writeLoggedFlushedResponse(responseString);
581             } catch (MessagingException JavaDoc me) {
582                 responseString = ERR_RESPONSE;
583                 writeLoggedFlushedResponse(responseString);
584             }
585         } else {
586             responseString = ERR_RESPONSE;
587             writeLoggedFlushedResponse(responseString);
588         }
589     }
590
591     /**
592      * Handler method called upon receipt of a LIST command.
593      * Returns the number of messages in the mailbox and its
594      * aggregate size, or optionally, the number and size of
595      * a single message.
596      *
597      * @param command the command parsed by the parseCommand method
598      * @param argument the first argument parsed by the parseCommand method
599      * @param argument1 the second argument parsed by the parseCommand method
600      */

601     private void doLIST(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
602         String JavaDoc responseString = null;
603         if (state == TRANSACTION) {
604             if (argument == null) {
605                 long size = 0;
606                 int count = 0;
607                 try {
608                     for (Iterator i = userMailbox.iterator(); i.hasNext(); ) {
609                         MailImpl mc = (MailImpl) i.next();
610                         if (mc != DELETED) {
611                             size += mc.getMessageSize();
612                             count++;
613                         }
614                     }
615                     StringBuffer JavaDoc responseBuffer =
616                         new StringBuffer JavaDoc(32)
617                                 .append(OK_RESPONSE)
618                                 .append(" ")
619                                 .append(count)
620                                 .append(" ")
621                                 .append(size);
622                     responseString = responseBuffer.toString();
623                     writeLoggedFlushedResponse(responseString);
624                     count = 0;
625                     for (Iterator i = userMailbox.iterator(); i.hasNext(); count++) {
626                         MailImpl mc = (MailImpl) i.next();
627
628                         if (mc != DELETED) {
629                             responseBuffer =
630                                 new StringBuffer JavaDoc(16)
631                                         .append(count)
632                                         .append(" ")
633                                         .append(mc.getMessageSize());
634                             out.println(responseBuffer.toString());
635                         }
636                     }
637                     out.println(".");
638                     out.flush();
639                 } catch (MessagingException JavaDoc me) {
640                     responseString = ERR_RESPONSE;
641                     writeLoggedFlushedResponse(responseString);
642                 }
643             } else {
644                 int num = 0;
645                 try {
646                     num = Integer.parseInt(argument);
647                     MailImpl mc = (MailImpl) userMailbox.get(num);
648                     if (mc != DELETED) {
649                         StringBuffer JavaDoc responseBuffer =
650                             new StringBuffer JavaDoc(64)
651                                     .append(OK_RESPONSE)
652                                     .append(" ")
653                                     .append(num)
654                                     .append(" ")
655                                     .append(mc.getMessageSize());
656                         responseString = responseBuffer.toString();
657                         writeLoggedFlushedResponse(responseString);
658                     } else {
659                         StringBuffer JavaDoc responseBuffer =
660                             new StringBuffer JavaDoc(64)
661                                     .append(ERR_RESPONSE)
662                                     .append(" Message (")
663                                     .append(num)
664                                     .append(") already deleted.");
665                         responseString = responseBuffer.toString();
666                         writeLoggedFlushedResponse(responseString);
667                     }
668                 } catch (IndexOutOfBoundsException JavaDoc npe) {
669                     StringBuffer JavaDoc responseBuffer =
670                         new StringBuffer JavaDoc(64)
671                                 .append(ERR_RESPONSE)
672                                 .append(" Message (")
673                                 .append(num)
674                                 .append(") does not exist.");
675                     responseString = responseBuffer.toString();
676                     writeLoggedFlushedResponse(responseString);
677                 } catch (NumberFormatException JavaDoc nfe) {
678                     StringBuffer JavaDoc responseBuffer =
679                         new StringBuffer JavaDoc(64)
680                                 .append(ERR_RESPONSE)
681                                 .append(" ")
682                                 .append(argument)
683                                 .append(" is not a valid number");
684                     responseString = responseBuffer.toString();
685                     writeLoggedFlushedResponse(responseString);
686                 } catch (MessagingException JavaDoc me) {
687                     responseString = ERR_RESPONSE;
688                     writeLoggedFlushedResponse(responseString);
689                }
690             }
691         } else {
692             responseString = ERR_RESPONSE;
693             writeLoggedFlushedResponse(responseString);
694         }
695     }
696
697     /**
698      * Handler method called upon receipt of a UIDL command.
699      * Returns a listing of message ids to the client.
700      *
701      * @param command the command parsed by the parseCommand method
702      * @param argument the first argument parsed by the parseCommand method
703      * @param argument1 the second argument parsed by the parseCommand method
704      */

705     private void doUIDL(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
706         String JavaDoc responseString = null;
707         if (state == TRANSACTION) {
708             if (argument == null) {
709                 responseString = OK_RESPONSE + " unique-id listing follows";
710                 writeLoggedFlushedResponse(responseString);
711                 int count = 0;
712                 for (Iterator i = userMailbox.iterator(); i.hasNext(); count++) {
713                     MailImpl mc = (MailImpl) i.next();
714                     if (mc != DELETED) {
715                         StringBuffer JavaDoc responseBuffer =
716                             new StringBuffer JavaDoc(64)
717                                     .append(count)
718                                     .append(" ")
719                                     .append(mc.getName());
720                         out.println(responseBuffer.toString());
721                     }
722                 }
723                 out.println(".");
724                 out.flush();
725             } else {
726                 int num = 0;
727                 try {
728                     num = Integer.parseInt(argument);
729                     MailImpl mc = (MailImpl) userMailbox.get(num);
730                     if (mc != DELETED) {
731                         StringBuffer JavaDoc responseBuffer =
732                             new StringBuffer JavaDoc(64)
733                                     .append(OK_RESPONSE)
734                                     .append(" ")
735                                     .append(num)
736                                     .append(" ")
737                                     .append(mc.getName());
738                         responseString = responseBuffer.toString();
739                         writeLoggedFlushedResponse(responseString);
740                     } else {
741                         StringBuffer JavaDoc responseBuffer =
742                             new StringBuffer JavaDoc(64)
743                                     .append(ERR_RESPONSE)
744                                     .append(" Message (")
745                                     .append(num)
746                                     .append(") already deleted.");
747                         responseString = responseBuffer.toString();
748                         writeLoggedFlushedResponse(responseString);
749                     }
750                 } catch (IndexOutOfBoundsException JavaDoc npe) {
751                     StringBuffer JavaDoc responseBuffer =
752                         new StringBuffer JavaDoc(64)
753                                 .append(ERR_RESPONSE)
754                                 .append(" Message (")
755                                 .append(num)
756                                 .append(") does not exist.");
757                     responseString = responseBuffer.toString();
758                     writeLoggedFlushedResponse(responseString);
759                 } catch (NumberFormatException JavaDoc nfe) {
760                     StringBuffer JavaDoc responseBuffer =
761                         new StringBuffer JavaDoc(64)
762                                 .append(ERR_RESPONSE)
763                                 .append(" ")
764                                 .append(argument)
765                                 .append(" is not a valid number");
766                     responseString = responseBuffer.toString();
767                     writeLoggedFlushedResponse(responseString);
768                 }
769             }
770         } else {
771             writeLoggedFlushedResponse(ERR_RESPONSE);
772         }
773     }
774
775     /**
776      * Handler method called upon receipt of a RSET command.
777      * Calls stat() to reset the mailbox.
778      *
779      * @param command the command parsed by the parseCommand method
780      * @param argument the first argument parsed by the parseCommand method
781      * @param argument1 the second argument parsed by the parseCommand method
782      */

783     private void doRSET(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
784         String JavaDoc responseString = null;
785         if (state == TRANSACTION) {
786             stat();
787             responseString = OK_RESPONSE;
788         } else {
789             responseString = ERR_RESPONSE;
790         }
791         writeLoggedFlushedResponse(responseString);
792     }
793
794     /**
795      * Handler method called upon receipt of a DELE command.
796      * This command deletes a particular mail message from the
797      * mailbox.
798      *
799      * @param command the command parsed by the parseCommand method
800      * @param argument the first argument parsed by the parseCommand method
801      * @param argument1 the second argument parsed by the parseCommand method
802      */

803     private void doDELE(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
804         String JavaDoc responseString = null;
805         if (state == TRANSACTION) {
806             int num = 0;
807             try {
808                 num = Integer.parseInt(argument);
809             } catch (Exception JavaDoc e) {
810                 responseString = ERR_RESPONSE + " Usage: DELE [mail number]";
811                 writeLoggedFlushedResponse(responseString);
812                 return;
813             }
814             try {
815                 MailImpl mc = (MailImpl) userMailbox.get(num);
816                 if (mc == DELETED) {
817                     StringBuffer JavaDoc responseBuffer =
818                         new StringBuffer JavaDoc(64)
819                                 .append(ERR_RESPONSE)
820                                 .append(" Message (")
821                                 .append(num)
822                                 .append(") already deleted.");
823                     responseString = responseBuffer.toString();
824                     writeLoggedFlushedResponse(responseString);
825                 } else {
826                     userMailbox.set(num, DELETED);
827                     writeLoggedFlushedResponse(OK_RESPONSE + " Message deleted");
828                 }
829             } catch (IndexOutOfBoundsException JavaDoc iob) {
830                 StringBuffer JavaDoc responseBuffer =
831                     new StringBuffer JavaDoc(64)
832                             .append(ERR_RESPONSE)
833                             .append(" Message (")
834                             .append(num)
835                             .append(") does not exist.");
836                 responseString = responseBuffer.toString();
837                 writeLoggedFlushedResponse(responseString);
838             }
839         } else {
840             responseString = ERR_RESPONSE;
841             writeLoggedFlushedResponse(responseString);
842         }
843     }
844
845     /**
846      * Handler method called upon receipt of a NOOP command.
847      * Like all good NOOPs, does nothing much.
848      *
849      * @param command the command parsed by the parseCommand method
850      * @param argument the first argument parsed by the parseCommand method
851      * @param argument1 the second argument parsed by the parseCommand method
852      */

853     private void doNOOP(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
854         String JavaDoc responseString = null;
855         if (state == TRANSACTION) {
856             responseString = OK_RESPONSE;
857             writeLoggedFlushedResponse(responseString);
858         } else {
859             responseString = ERR_RESPONSE;
860             writeLoggedFlushedResponse(responseString);
861         }
862     }
863
864     /**
865      * Handler method called upon receipt of a RETR command.
866      * This command retrieves a particular mail message from the
867      * mailbox.
868      *
869      * @param command the command parsed by the parseCommand method
870      * @param argument the first argument parsed by the parseCommand method
871      * @param argument1 the second argument parsed by the parseCommand method
872      */

873     private void doRETR(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
874         String JavaDoc responseString = null;
875         if (state == TRANSACTION) {
876             int num = 0;
877             try {
878                 num = Integer.parseInt(argument.trim());
879             } catch (Exception JavaDoc e) {
880                 responseString = ERR_RESPONSE + " Usage: RETR [mail number]";
881                 writeLoggedFlushedResponse(responseString);
882                 return;
883             }
884             try {
885                 MailImpl mc = (MailImpl) userMailbox.get(num);
886                 if (mc != DELETED) {
887                     responseString = OK_RESPONSE + " Message follows";
888                     writeLoggedFlushedResponse(responseString);
889                     OutputStream nouts =
890                             new ExtraDotOutputStream(outs);
891                     nouts = new BytesWrittenResetOutputStream(nouts,
892                                                               theWatchdog,
893                                                               theConfigData.getResetLength());
894                     mc.writeMessageTo(nouts);
895                     nouts.flush();
896                     // TODO: Is this an extra CRLF?
897
out.println();
898                     out.println(".");
899                     out.flush();
900                 } else {
901                     StringBuffer JavaDoc responseBuffer =
902                         new StringBuffer JavaDoc(64)
903                                 .append(ERR_RESPONSE)
904                                 .append(" Message (")
905                                 .append(num)
906                                 .append(") already deleted.");
907                     responseString = responseBuffer.toString();
908                     writeLoggedFlushedResponse(responseString);
909                 }
910             } catch (IOException ioe) {
911                 responseString = ERR_RESPONSE + " Error while retrieving message.";
912                 writeLoggedFlushedResponse(responseString);
913             } catch (MessagingException JavaDoc me) {
914                 responseString = ERR_RESPONSE + " Error while retrieving message.";
915                 writeLoggedFlushedResponse(responseString);
916             } catch (IndexOutOfBoundsException JavaDoc iob) {
917                 StringBuffer JavaDoc responseBuffer =
918                     new StringBuffer JavaDoc(64)
919                             .append(ERR_RESPONSE)
920                             .append(" Message (")
921                             .append(num)
922                             .append(") does not exist.");
923                 responseString = responseBuffer.toString();
924                 writeLoggedFlushedResponse(responseString);
925             }
926         } else {
927             responseString = ERR_RESPONSE;
928             writeLoggedFlushedResponse(responseString);
929         }
930     }
931
932     /**
933      * Handler method called upon receipt of a TOP command.
934      * This command retrieves the top N lines of a specified
935      * message in the mailbox.
936      *
937      * The expected command format is
938      * TOP [mail message number] [number of lines to return]
939      *
940      * @param command the command parsed by the parseCommand method
941      * @param argument the first argument parsed by the parseCommand method
942      * @param argument1 the second argument parsed by the parseCommand method
943      */

944     private void doTOP(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
945         String JavaDoc responseString = null;
946         if (state == TRANSACTION) {
947             int num = 0;
948             int lines = 0;
949             try {
950                 num = Integer.parseInt(argument);
951                 lines = Integer.parseInt(argument1);
952             } catch (NumberFormatException JavaDoc nfe) {
953                 responseString = ERR_RESPONSE + " Usage: TOP [mail number] [Line number]";
954                 writeLoggedFlushedResponse(responseString);
955                 return;
956             }
957             try {
958                 MailImpl mc = (MailImpl) userMailbox.get(num);
959                 if (mc != DELETED) {
960                     responseString = OK_RESPONSE + " Message follows";
961                     writeLoggedFlushedResponse(responseString);
962                     for (Enumeration e = mc.getMessage().getAllHeaderLines(); e.hasMoreElements(); ) {
963                         out.println(e.nextElement());
964                     }
965                     out.println();
966                     OutputStream nouts =
967                             new ExtraDotOutputStream(outs);
968                     nouts = new BytesWrittenResetOutputStream(nouts,
969                                                               theWatchdog,
970                                                               theConfigData.getResetLength());
971                     mc.writeContentTo(nouts, lines);
972                     nouts.flush();
973                     out.println(".");
974                     out.flush();
975                 } else {
976                     StringBuffer JavaDoc responseBuffer =
977                         new StringBuffer JavaDoc(64)
978                                 .append(ERR_RESPONSE)
979                                 .append(" Message (")
980                                 .append(num)
981                                 .append(") already deleted.");
982                     responseString = responseBuffer.toString();
983                     writeLoggedFlushedResponse(responseString);
984                 }
985             } catch (IOException ioe) {
986                 responseString = ERR_RESPONSE + " Error while retrieving message.";
987                 writeLoggedFlushedResponse(responseString);
988             } catch (MessagingException JavaDoc me) {
989                 responseString = ERR_RESPONSE + " Error while retrieving message.";
990                 writeLoggedFlushedResponse(responseString);
991             } catch (IndexOutOfBoundsException JavaDoc iob) {
992                 StringBuffer JavaDoc exceptionBuffer =
993                     new StringBuffer JavaDoc(64)
994                             .append(ERR_RESPONSE)
995                             .append(" Message (")
996                             .append(num)
997                             .append(") does not exist.");
998                 responseString = exceptionBuffer.toString();
999                 writeLoggedFlushedResponse(responseString);
1000            }
1001        } else {
1002            responseString = ERR_RESPONSE;
1003            writeLoggedFlushedResponse(responseString);
1004        }
1005    }
1006
1007    /**
1008     * Handler method called upon receipt of a QUIT command.
1009     * This method handles cleanup of the POP3Handler state.
1010     *
1011     * @param command the command parsed by the parseCommand method
1012     * @param argument the first argument parsed by the parseCommand method
1013     * @param argument1 the second argument parsed by the parseCommand method
1014     */

1015    private void doQUIT(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
1016        String JavaDoc responseString = null;
1017        if (state == AUTHENTICATION_READY || state == AUTHENTICATION_USERSET) {
1018            responseString = OK_RESPONSE + " Apache James POP3 Server signing off.";
1019            writeLoggedFlushedResponse(responseString);
1020            return;
1021        }
1022        List toBeRemoved = ListUtils.subtract(backupUserMailbox, userMailbox);
1023        try {
1024            userInbox.remove(toBeRemoved);
1025            // for (Iterator it = toBeRemoved.iterator(); it.hasNext(); ) {
1026
// MailImpl mc = (MailImpl) it.next();
1027
// userInbox.remove(mc.getName());
1028
//}
1029
responseString = OK_RESPONSE + " Apache James POP3 Server signing off.";
1030            writeLoggedFlushedResponse(responseString);
1031        } catch (Exception JavaDoc ex) {
1032            responseString = ERR_RESPONSE + " Some deleted messages were not removed";
1033            writeLoggedFlushedResponse(responseString);
1034            getLogger().error("Some deleted messages were not removed: " + ex.getMessage());
1035        }
1036    }
1037
1038    /**
1039     * Handler method called upon receipt of an unrecognized command.
1040     * Returns an error response and logs the command.
1041     *
1042     * @param command the command parsed by the parseCommand method
1043     * @param argument the first argument parsed by the parseCommand method
1044     * @param argument1 the second argument parsed by the parseCommand method
1045     */

1046    private void doUnknownCmd(String JavaDoc command,String JavaDoc argument,String JavaDoc argument1) {
1047        writeLoggedFlushedResponse(ERR_RESPONSE);
1048    }
1049
1050    /**
1051     * This method logs at a "DEBUG" level the response string that
1052     * was sent to the POP3 client. The method is provided largely
1053     * as syntactic sugar to neaten up the code base. It is declared
1054     * private and final to encourage compiler inlining.
1055     *
1056     * @param responseString the response string sent to the client
1057     */

1058    private final void logResponseString(String JavaDoc responseString) {
1059        if (getLogger().isDebugEnabled()) {
1060            getLogger().debug("Sent: " + responseString);
1061        }
1062    }
1063
1064    /**
1065     * Write and flush a response string. The response is also logged.
1066     * Should be used for the last line of a multi-line response or
1067     * for a single line response.
1068     *
1069     * @param responseString the response string sent to the client
1070     */

1071    final void writeLoggedFlushedResponse(String JavaDoc responseString) {
1072        out.println(responseString);
1073        out.flush();
1074        logResponseString(responseString);
1075    }
1076
1077    /**
1078     * Write a response string. The response is also logged.
1079     * Used for multi-line responses.
1080     *
1081     * @param responseString the response string sent to the client
1082     */

1083    final void writeLoggedResponse(String JavaDoc responseString) {
1084        out.println(responseString);
1085        logResponseString(responseString);
1086    }
1087
1088    /**
1089     * A private inner class which serves as an adaptor
1090     * between the WatchdogTarget interface and this
1091     * handler class.
1092     */

1093    private class POP3WatchdogTarget
1094        implements WatchdogTarget {
1095
1096        /**
1097         * @see org.apache.james.util.watchdog.WatchdogTarget#execute()
1098         */

1099        public void execute() {
1100            POP3Handler.this.idleClose();
1101        }
1102
1103    }
1104
1105}
1106
1107
Popular Tags