View Javadoc

1   /*****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  
21  package org.apache.james.remotemanager;
22  
23  import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
24  import org.apache.avalon.excalibur.pool.Poolable;
25  import org.apache.avalon.framework.container.ContainerUtil;
26  import org.apache.avalon.framework.logger.AbstractLogEnabled;
27  import org.apache.james.Constants;
28  import org.apache.james.services.JamesUser;
29  import org.apache.james.services.User;
30  import org.apache.james.services.UsersRepository;
31  import org.apache.james.util.watchdog.Watchdog;
32  import org.apache.james.util.watchdog.WatchdogTarget;
33  import org.apache.mailet.MailAddress;
34  
35  import javax.mail.internet.ParseException;
36  import java.io.BufferedReader;
37  import java.io.BufferedWriter;
38  import java.io.IOException;
39  import java.io.InputStreamReader;
40  import java.io.OutputStreamWriter;
41  import java.io.PrintWriter;
42  import java.net.Socket;
43  import java.util.Iterator;
44  import java.util.Locale;
45  
46  /***
47   * Provides a really rude network interface to administer James.
48   * Allow to add accounts.
49   * TODO: -improve protocol
50   *       -much more...
51   *
52   * @version $Revision: 440727 $
53   *
54   */
55  public class RemoteManagerHandler
56      extends AbstractLogEnabled
57      implements ConnectionHandler, Poolable {
58  
59      /***
60       * The text string for the MEMSTAT command
61       */
62      private static final String COMMAND_MEMSTAT = "MEMSTAT";
63  
64      /***
65       * The text string for the ADDUSER command
66       */
67      private static final String COMMAND_ADDUSER = "ADDUSER";
68  
69      /***
70       * The text string for the SETPASSWORD command
71       */
72      private static final String COMMAND_SETPASSWORD = "SETPASSWORD";
73  
74      /***
75       * The text string for the DELUSER command
76       */
77      private static final String COMMAND_DELUSER = "DELUSER";
78  
79      /***
80       * The text string for the LISTUSERS command
81       */
82      private static final String COMMAND_LISTUSERS = "LISTUSERS";
83  
84      /***
85       * The text string for the COUNTUSERS command
86       */
87      private static final String COMMAND_COUNTUSERS = "COUNTUSERS";
88  
89      /***
90       * The text string for the VERIFY command
91       */
92      private static final String COMMAND_VERIFY = "VERIFY";
93  
94      /***
95       * The text string for the HELP command
96       */
97      private static final String COMMAND_HELP = "HELP";
98  
99      /***
100      * The text string for the SETFORWARDING command
101      */
102     private static final String COMMAND_SETFORWARDING = "SETFORWARDING";
103 
104     /***
105      * The text string for the SHOWFORWARDING command
106      */
107     private static final String COMMAND_SHOWFORWARDING = "SHOWFORWARDING";
108 
109     /***
110      * The text string for the UNSETFORWARDING command
111      */
112     private static final String COMMAND_UNSETFORWARDING = "UNSETFORWARDING";
113 
114     /***
115      * The text string for the SETALIAS command
116      */
117     private static final String COMMAND_SETALIAS = "SETALIAS";
118 
119     /***
120      * The text string for the SHOWALIAS command
121      */
122     private static final String COMMAND_SHOWALIAS = "SHOWALIAS";
123 
124     /***
125      * The text string for the UNSETALIAS command
126      */
127     private static final String COMMAND_UNSETALIAS = "UNSETALIAS";
128 
129     /***
130      * The text string for the USER command
131      */
132     private static final String COMMAND_USER = "USER";
133 
134     /***
135      * The text string for the QUIT command
136      */
137     private static final String COMMAND_QUIT = "QUIT";
138 
139     /***
140      * The text string for the SHUTDOWN command
141      */
142     private static final String COMMAND_SHUTDOWN = "SHUTDOWN";
143 
144     /***
145      * The per-service configuration data that applies to all handlers
146      */
147     private RemoteManagerHandlerConfigurationData theConfigData;
148 
149     /***
150      * The current UsersRepository being managed/viewed/modified
151      */
152     private UsersRepository users;
153 
154     /***
155      * The reader associated with incoming commands.
156      */
157     private BufferedReader in;
158 
159     /***
160      * The writer to which outgoing messages are written.
161      */
162     private PrintWriter out;
163 
164     /***
165      * The thread executing this handler
166      */
167     private Thread handlerThread;
168 
169     /***
170      * The TCP/IP socket over which the RemoteManager interaction
171      * is occurring
172      */
173     private Socket socket;
174 
175     /***
176      * The watchdog being used by this handler to deal with idle timeouts.
177      */
178     private Watchdog theWatchdog;
179 
180     /***
181      * The watchdog target that idles out this handler.
182      */
183     private WatchdogTarget theWatchdogTarget = new RemoteManagerWatchdogTarget();
184 
185     /***
186      * Set the configuration data for the handler.
187      *
188      * @param theData the configuration data
189      */
190     void setConfigurationData(RemoteManagerHandlerConfigurationData theData) {
191         theConfigData = theData;
192 
193         // Reset the users repository to the default.
194         users = theConfigData.getUsersRepository();
195     }
196 
197     /***
198      * Set the Watchdog for use by this handler.
199      *
200      * @param theWatchdog the watchdog
201      */
202     void setWatchdog(Watchdog theWatchdog) {
203         this.theWatchdog = theWatchdog;
204     }
205 
206     /***
207      * Gets the Watchdog Target that should be used by Watchdogs managing
208      * this connection.
209      *
210      * @return the WatchdogTarget
211      */
212     WatchdogTarget getWatchdogTarget() {
213         return theWatchdogTarget;
214     }
215 
216     /***
217      * Idle out this connection
218      */
219     void idleClose() {
220         if (getLogger() != null) {
221             getLogger().error("Remote Manager Connection has idled out.");
222         }
223         try {
224             if (socket != null) {
225                 socket.close();
226             }
227         } catch (Exception e) {
228             // ignored
229         } finally {
230             socket = null;
231         }
232 
233         synchronized (this) {
234             // Interrupt the thread to recover from internal hangs
235             if (handlerThread != null) {
236                 handlerThread.interrupt();
237                 handlerThread = null;
238             }
239         }
240     }
241 
242     /***
243      * @see org.apache.avalon.cornerstone.services.connection.ConnectionHandler#handleConnection(Socket)
244      */
245     public void handleConnection( final Socket connection )
246         throws IOException {
247 
248         socket = connection;
249         String remoteIP = socket.getInetAddress().getHostAddress();
250         String remoteHost = socket.getInetAddress().getHostName();
251 
252         synchronized (this) {
253             handlerThread = Thread.currentThread();
254         }
255 
256         try {
257             in = new BufferedReader(new InputStreamReader(socket.getInputStream(), "ASCII"), 512);
258             out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()), 512), false);
259             if (getLogger().isInfoEnabled()) {
260                 StringBuffer infoBuffer =
261                     new StringBuffer(128)
262                             .append("Access from ")
263                             .append(remoteHost)
264                             .append("(")
265                             .append(remoteIP)
266                             .append(")");
267                 getLogger().info( infoBuffer.toString() );
268             }
269             writeLoggedResponse("JAMES Remote Administration Tool " + Constants.SOFTWARE_VERSION );
270             writeLoggedResponse("Please enter your login and password");
271             String login = null;
272             String password = null;
273             do {
274                 if (login != null) {
275                     final String message = "Login failed for " + login;
276                     writeLoggedFlushedResponse(message);
277                 }
278                 writeLoggedFlushedResponse("Login id:");
279                 login = in.readLine().trim();
280                 writeLoggedFlushedResponse("Password:");
281                 password = in.readLine().trim();
282             } while (!password.equals(theConfigData.getAdministrativeAccountData().get(login)) || password.length() == 0);
283 
284             StringBuffer messageBuffer =
285                 new StringBuffer(64)
286                         .append("Welcome ")
287                         .append(login)
288                         .append(". HELP for a list of commands");
289             out.println( messageBuffer.toString() );
290             out.flush();
291             if (getLogger().isInfoEnabled()) {
292                 StringBuffer infoBuffer =
293                     new StringBuffer(128)
294                             .append("Login for ")
295                             .append(login)
296                             .append(" successful");
297                 getLogger().info(infoBuffer.toString());
298             }
299 
300             try {
301                 out.print(theConfigData.getPrompt());
302                 out.flush();
303                 theWatchdog.start();
304                 while (parseCommand(in.readLine())) {
305                     theWatchdog.reset();
306                     out.print(theConfigData.getPrompt());
307                     out.flush();
308                 }
309                 theWatchdog.stop();
310             } catch (IOException ioe) {
311                 //We can cleanly ignore this as it's probably a socket timeout
312             } catch (Throwable thr) {
313                 System.out.println("Exception: " + thr.getMessage());
314                 getLogger().error("Encountered exception in handling the remote manager connection.", thr);
315             }
316             StringBuffer infoBuffer =
317                 new StringBuffer(64)
318                         .append("Logout for ")
319                         .append(login)
320                         .append(".");
321             getLogger().info(infoBuffer.toString());
322 
323         } catch ( final IOException e ) {
324             out.println("Error. Closing connection");
325             out.flush();
326             if (getLogger().isErrorEnabled()) {
327                 StringBuffer exceptionBuffer =
328                     new StringBuffer(128)
329                             .append("Exception during connection from ")
330                             .append(remoteHost)
331                             .append(" (")
332                             .append(remoteIP)
333                             .append("): ")
334                             .append(e.getMessage());
335                 getLogger().error(exceptionBuffer.toString());
336             }
337         } finally {
338             resetHandler();
339         }
340     }
341 
342     /***
343      * Resets the handler data to a basic state.
344      */
345     private void resetHandler() {
346 
347         // Clear the Watchdog
348         if (theWatchdog != null) {
349             ContainerUtil.dispose(theWatchdog);
350             theWatchdog = null;
351         }
352 
353         in = null;
354         out = null;
355         try {
356             if (socket != null) {
357                 socket.close();
358             }
359         } catch (IOException e) {
360             if (getLogger().isErrorEnabled()) {
361                 getLogger().error("Exception closing socket: "
362                                   + e.getMessage());
363             }
364         } finally {
365             socket = null;
366         }
367 
368         synchronized (this) {
369             handlerThread = null;
370         }
371 
372         // Reset user repository
373         users = theConfigData.getUsersRepository();
374 
375         // Clear config data
376         theConfigData = null;
377     }
378 
379     /***
380      * <p>This method parses and processes RemoteManager commands read off the
381      * wire in handleConnection.  It returns true if expecting additional
382      * commands, false otherwise.</p>
383      *
384      * @param command the raw command string passed in over the socket
385      *
386      * @return whether additional commands are expected.
387      */
388     private boolean parseCommand( String rawCommand ) {
389         if (rawCommand == null) {
390             return false;
391         }
392         String command = rawCommand.trim();
393         String argument = null;
394         int breakIndex = command.indexOf(" ");
395         if (breakIndex > 0) {
396             argument = command.substring(breakIndex + 1);
397             command = command.substring(0, breakIndex);
398         }
399         command = command.toUpperCase(Locale.US);
400         if (command.equals(COMMAND_ADDUSER)) {
401             doADDUSER(argument);
402         } else if (command.equals(COMMAND_SETPASSWORD)) {
403             return doSETPASSWORD(argument);
404         } else if (command.equals(COMMAND_DELUSER)) {
405             return doDELUSER(argument);
406         } else if (command.equals(COMMAND_LISTUSERS)) {
407             return doLISTUSERS(argument);
408         } else if (command.equals(COMMAND_COUNTUSERS)) {
409             return doCOUNTUSERS(argument);
410         } else if (command.equals(COMMAND_VERIFY)) {
411             return doVERIFY(argument);
412         } else if (command.equals(COMMAND_HELP)) {
413             return doHELP(argument);
414         } else if (command.equals(COMMAND_SETALIAS)) {
415             return doSETALIAS(argument);
416         } else if (command.equals(COMMAND_SETFORWARDING)) {
417             return doSETFORWARDING(argument);
418         } else if (command.equals(COMMAND_SHOWALIAS)) {
419             return doSHOWALIAS(argument);
420         } else if (command.equals(COMMAND_SHOWFORWARDING)) {
421             return doSHOWFORWARDING(argument);
422         } else if (command.equals(COMMAND_UNSETALIAS)) {
423             return doUNSETALIAS(argument);
424         } else if (command.equals(COMMAND_UNSETFORWARDING)) {
425             return doUNSETFORWARDING(argument);
426         } else if (command.equals(COMMAND_USER)) {
427             return doUSER(argument);
428         } else if (command.equals(COMMAND_MEMSTAT)) {
429             return doMEMSTAT(argument);
430         } else if (command.equals(COMMAND_QUIT)) {
431             return doQUIT(argument);
432         } else if (command.equals(COMMAND_SHUTDOWN)) {
433             return doSHUTDOWN(argument);
434         } else {
435             return doUnknownCommand(rawCommand);
436         }
437         return true;
438     }
439 
440     /***
441      * Handler method called upon receipt of an MEMSTAT command.
442      * Returns whether further commands should be read off the wire.
443      *
444      * @param argument the argument passed in with the command
445      */
446     private boolean doMEMSTAT(String argument) {
447         writeLoggedFlushedResponse("Current memory statistics:");
448         writeLoggedFlushedResponse("\tFree Memory: " + Runtime.getRuntime().freeMemory());
449         writeLoggedFlushedResponse("\tTotal Memory: " + Runtime.getRuntime().totalMemory());
450         writeLoggedFlushedResponse("\tMax Memory: " + Runtime.getRuntime().maxMemory());
451 
452         if ("-gc".equalsIgnoreCase(argument)) {
453             System.gc();
454             writeLoggedFlushedResponse("And after System.gc():");
455             writeLoggedFlushedResponse("\tFree Memory: " + Runtime.getRuntime().freeMemory());
456             writeLoggedFlushedResponse("\tTotal Memory: " + Runtime.getRuntime().totalMemory());
457             writeLoggedFlushedResponse("\tMax Memory: " + Runtime.getRuntime().maxMemory());
458         }
459 
460         return true;
461     }
462 
463     /***
464      * Handler method called upon receipt of an ADDUSER command.
465      * Returns whether further commands should be read off the wire.
466      *
467      * @param argument the argument passed in with the command
468      */
469     private boolean doADDUSER(String argument) {
470         int breakIndex = -1;
471         if ((argument == null) ||
472             (argument.equals("")) ||
473             ((breakIndex = argument.indexOf(" ")) < 0)) {
474             writeLoggedFlushedResponse("Usage: adduser [username] [password]");
475             return true;
476         }
477         String username = argument.substring(0,breakIndex);
478         String passwd = argument.substring(breakIndex + 1);
479         if (username.equals("") || passwd.equals("")) {
480             writeLoggedFlushedResponse("Usage: adduser [username] [password]");
481             return true;
482         }
483 
484         boolean success = false;
485         if (users.contains(username)) {
486             StringBuffer responseBuffer =
487                 new StringBuffer(64)
488                         .append("User ")
489                         .append(username)
490                         .append(" already exists");
491             String response = responseBuffer.toString();
492             writeLoggedResponse(response);
493         } else {
494             success = users.addUser(username, passwd);
495         }
496         if ( success ) {
497             StringBuffer responseBuffer =
498                 new StringBuffer(64)
499                         .append("User ")
500                         .append(username)
501                         .append(" added");
502             String response = responseBuffer.toString();
503             out.println(response);
504             getLogger().info(response);
505         } else {
506             out.println("Error adding user " + username);
507             getLogger().error("Error adding user " + username);
508         }
509         out.flush();
510         return true;
511     }
512 
513     /***
514      * Handler method called upon receipt of an SETPASSWORD command.
515      * Returns whether further commands should be read off the wire.
516      *
517      * @param argument the argument passed in with the command
518      */
519     private boolean doSETPASSWORD(String argument) {
520 
521         int breakIndex = -1;
522         if ((argument == null) ||
523             (argument.equals("")) ||
524             ((breakIndex = argument.indexOf(" ")) < 0)) {
525             writeLoggedFlushedResponse("Usage: setpassword [username] [password]");
526             return true;
527         }
528         String username = argument.substring(0,breakIndex);
529         String passwd = argument.substring(breakIndex + 1);
530 
531         if (username.equals("") || passwd.equals("")) {
532             writeLoggedFlushedResponse("Usage: adduser [username] [password]");
533             return true;
534         }
535         User user = users.getUserByName(username);
536         if (user == null) {
537             writeLoggedFlushedResponse("No such user " + username);
538             return true;
539         }
540         boolean success = user.setPassword(passwd);
541         if (success) {
542             users.updateUser(user);
543             StringBuffer responseBuffer =
544                 new StringBuffer(64)
545                         .append("Password for ")
546                         .append(username)
547                         .append(" reset");
548             String response = responseBuffer.toString();
549             out.println(response);
550             getLogger().info(response);
551         } else {
552             out.println("Error resetting password");
553             getLogger().error("Error resetting password");
554         }
555         out.flush();
556         return true;
557     }
558 
559     /***
560      * Handler method called upon receipt of an DELUSER command.
561      * Returns whether further commands should be read off the wire.
562      *
563      * @param argument the argument passed in with the command
564      */
565     private boolean doDELUSER(String argument) {
566         String user = argument;
567         if ((user == null) || (user.equals(""))) {
568             writeLoggedFlushedResponse("Usage: deluser [username]");
569             return true;
570         }
571         if (users.contains(user)) {
572             try {
573                 users.removeUser(user);
574                 StringBuffer responseBuffer =
575                                              new StringBuffer(64)
576                                              .append("User ")
577                                              .append(user)
578                                              .append(" deleted");
579                 String response = responseBuffer.toString();
580                 out.println(response);
581                 getLogger().info(response);
582             } catch (Exception e) {
583                 StringBuffer exceptionBuffer =
584                                               new StringBuffer(128)
585                                               .append("Error deleting user ")
586                                               .append(user)
587                                               .append(" : ")
588                                               .append(e.getMessage());
589                 String exception = exceptionBuffer.toString();
590                 out.println(exception);
591                 getLogger().error(exception);
592             }
593         } else {
594             StringBuffer responseBuffer =
595                                          new StringBuffer(64)
596                                          .append("User ")
597                                          .append(user)
598                                          .append(" doesn't exist");
599             String response = responseBuffer.toString();
600             out.println(response);
601         }
602         out.flush();
603         return true;
604     }
605 
606     /***
607      * Handler method called upon receipt of an LISTUSERS command.
608      * Returns whether further commands should be read off the wire.
609      *
610      * @param argument the argument passed in with the command
611      */
612     private boolean doLISTUSERS(String argument) {
613         writeLoggedResponse("Existing accounts " + users.countUsers());
614         for (Iterator it = users.list(); it.hasNext();) {
615            writeLoggedResponse("user: " + (String) it.next());
616         }
617         out.flush();
618         return true;
619     }
620 
621     /***
622      * Handler method called upon receipt of an COUNTUSERS command.
623      * Returns whether further commands should be read off the wire.
624      *
625      * @param argument the argument passed in with the command
626      */
627     private boolean doCOUNTUSERS(String argument) {
628         writeLoggedFlushedResponse("Existing accounts " + users.countUsers());
629         return true;
630     }
631 
632     /***
633      * Handler method called upon receipt of an VERIFY command.
634      * Returns whether further commands should be read off the wire.
635      *
636      * @param argument the argument passed in with the command
637      */
638     private boolean doVERIFY(String argument) {
639         String user = argument;
640         if (user == null || user.equals("")) {
641             writeLoggedFlushedResponse("Usage: verify [username]");
642             return true;
643         }
644         if (users.contains(user)) {
645             StringBuffer responseBuffer =
646                 new StringBuffer(64)
647                         .append("User ")
648                         .append(user)
649                         .append(" exists");
650             String response = responseBuffer.toString();
651             writeLoggedResponse(response);
652         } else {
653             StringBuffer responseBuffer =
654                 new StringBuffer(64)
655                         .append("User ")
656                         .append(user)
657                         .append(" does not exist");
658             String response = responseBuffer.toString();
659             writeLoggedResponse(response);
660         }
661         out.flush();
662         return true;
663     }
664 
665     /***
666      * Handler method called upon receipt of an HELP command.
667      * Returns whether further commands should be read off the wire.
668      *
669      * @param argument the argument passed in with the command
670      */
671     private boolean doHELP(String argument) {
672         out.println("Currently implemented commands:");
673         out.println("help                                    display this help");
674         out.println("listusers                               display existing accounts");
675         out.println("countusers                              display the number of existing accounts");
676         out.println("adduser [username] [password]           add a new user");
677         out.println("verify [username]                       verify if specified user exist");
678         out.println("deluser [username]                      delete existing user");
679         out.println("setpassword [username] [password]       sets a user's password");
680         out.println("setalias [user] [alias]                 locally forwards all email for 'user' to 'alias'");
681         out.println("showalias [username]                    shows a user's current email alias");
682         out.println("unsetalias [user]                       unsets an alias for 'user'");
683         out.println("setforwarding [username] [emailaddress] forwards a user's email to another email address");
684         out.println("showforwarding [username]               shows a user's current email forwarding");
685         out.println("unsetforwarding [username]              removes a forward");
686         out.println("user [repositoryname]                   change to another user repository");
687         out.println("shutdown                                kills the current JVM (convenient when James is run as a daemon)");
688         out.println("quit                                    close connection");
689         out.flush();
690         return true;
691     }
692 
693     /***
694      * Handler method called upon receipt of an SETALIAS command.
695      * Returns whether further commands should be read off the wire.
696      *
697      * @param argument the argument passed in with the command
698      */
699     private boolean doSETALIAS(String argument) {
700         int breakIndex = -1;
701         if ((argument == null) ||
702             (argument.equals("")) ||
703             ((breakIndex = argument.indexOf(" ")) < 0)) {
704             writeLoggedFlushedResponse("Usage: setalias [username] [emailaddress]");
705             return true;
706         }
707         String username = argument.substring(0,breakIndex);
708         String alias = argument.substring(breakIndex + 1);
709         if (username.equals("") || alias.equals("")) {
710             writeLoggedFlushedResponse("Usage: setalias [username] [alias]");
711             return true;
712         }
713         
714         User baseuser = users.getUserByName(username);
715         if (baseuser == null) {
716             writeLoggedFlushedResponse("No such user " + username);
717             return true;
718         }
719         if (! (baseuser instanceof JamesUser ) ) {
720             writeLoggedFlushedResponse("Can't set alias for this user type.");
721             return true;
722         }
723         
724         JamesUser user = (JamesUser) baseuser;
725         JamesUser aliasUser = (JamesUser) users.getUserByName(alias);
726         if (aliasUser == null) {
727             writeLoggedFlushedResponse("Alias unknown to server - create that user first.");
728             return true;
729         }
730 
731         boolean success = user.setAlias(alias);
732         if (success) {
733             user.setAliasing(true);
734             users.updateUser(user);
735             StringBuffer responseBuffer =
736                 new StringBuffer(64)
737                         .append("Alias for ")
738                         .append(username)
739                         .append(" set to:")
740                         .append(alias);
741             String response = responseBuffer.toString();
742             out.println(response);
743             getLogger().info(response);
744         } else {
745             out.println("Error setting alias");
746             getLogger().error("Error setting alias");
747         }
748         out.flush();
749         return true;
750     }
751 
752     /***
753      * Handler method called upon receipt of an SETFORWARDING command.
754      * Returns whether further commands should be read off the wire.
755      *
756      * @param argument the argument passed in with the command
757      */
758     private boolean doSETFORWARDING(String argument) {
759         int breakIndex = -1;
760         if ((argument == null) ||
761             (argument.equals("")) ||
762             ((breakIndex = argument.indexOf(" ")) < 0)) {
763             writeLoggedFlushedResponse("Usage: setforwarding [username] [emailaddress]");
764             return true;
765         }
766         String username = argument.substring(0,breakIndex);
767         String forward = argument.substring(breakIndex + 1);
768         if (username.equals("") || forward.equals("")) {
769            writeLoggedFlushedResponse("Usage: setforwarding [username] [emailaddress]");
770            return true;
771         }
772         // Verify user exists
773         User baseuser = users.getUserByName(username);
774         if (baseuser == null) {
775             writeLoggedFlushedResponse("No such user " + username);
776             return true;
777         } else if (! (baseuser instanceof JamesUser ) ) {
778             writeLoggedFlushedResponse("Can't set forwarding for this user type.");
779             return true;
780         }
781         JamesUser user = (JamesUser)baseuser;
782         // Verify acceptable email address
783         MailAddress forwardAddr;
784         try {
785              forwardAddr = new MailAddress(forward);
786         } catch(ParseException pe) {
787             writeLoggedResponse("Parse exception with that email address: " + pe.getMessage());
788             writeLoggedFlushedResponse("Forwarding address not added for " + username);
789             return true;
790         }
791 
792         boolean success = user.setForwardingDestination(forwardAddr);
793         if (success) {
794             user.setForwarding(true);
795             users.updateUser(user);
796             StringBuffer responseBuffer =
797                 new StringBuffer(64)
798                         .append("Forwarding destination for ")
799                         .append(username)
800                         .append(" set to:")
801                         .append(forwardAddr.toString());
802             String response = responseBuffer.toString();
803             out.println(response);
804             getLogger().info(response);
805         } else {
806             out.println("Error setting forwarding");
807             getLogger().error("Error setting forwarding");
808         }
809         out.flush();
810         return true;
811     }
812 
813     /***
814      * Handler method called upon receipt of an SHOWALIAS command.
815      * Returns whether further commands should be read off the wire.
816      *
817      * @param argument the user name
818      */
819     private boolean doSHOWALIAS(String username) {
820         if ( username == null || username.equals("") ) {
821             writeLoggedFlushedResponse("Usage: showalias [username]");
822             return true;
823         }
824 
825 
826         User baseuser = users.getUserByName(username);
827         if (baseuser == null) {
828             writeLoggedFlushedResponse("No such user " + username);
829             return true;
830         } else if (! (baseuser instanceof JamesUser ) ) {
831             writeLoggedFlushedResponse("Can't show aliases for this user type.");
832             return true;
833         }
834 
835         JamesUser user = (JamesUser)baseuser;
836         if ( user == null ) {
837             writeLoggedFlushedResponse("No such user " + username);
838             return true;
839         }
840 
841         if ( !user.getAliasing() ) {
842             writeLoggedFlushedResponse("User " + username + " does not currently have an alias");
843             return true;
844         }
845 
846         String alias = user.getAlias();
847 
848         if ( alias == null || alias.equals("") ) {    //  defensive programming -- neither should occur
849             String errmsg = "For user " + username + ", the system indicates that aliasing is set but no alias was found";
850             out.println(errmsg);
851             getLogger().error(errmsg);
852             return true;
853         }
854 
855         writeLoggedFlushedResponse("Current alias for " + username + " is: " + alias);
856         return true;
857     }
858 
859     /***
860      * Handler method called upon receipt of an SHOWFORWARDING command.
861      * Returns whether further commands should be read off the wire.
862      *
863      * @param argument the user name
864      */
865     private boolean doSHOWFORWARDING(String username) {
866         if ( username == null || username.equals("") ) {
867             writeLoggedFlushedResponse("Usage: showforwarding [username]");
868             return true;
869         }
870 
871         // Verify user exists
872         User baseuser = users.getUserByName(username);
873         if (baseuser == null) {
874             writeLoggedFlushedResponse("No such user " + username);
875             return true;
876         } else if (! (baseuser instanceof JamesUser ) ) {
877             writeLoggedFlushedResponse("Can't set forwarding for this user type.");
878             return true;
879         }
880         JamesUser user = (JamesUser)baseuser;
881         if ( user == null ) {
882             writeLoggedFlushedResponse("No such user " + username);
883             return true;
884         }
885 
886         if ( !user.getForwarding() ) {
887             writeLoggedFlushedResponse("User " + username + " is not currently being forwarded");
888             return true;
889         }
890 
891         MailAddress fwdAddr = user.getForwardingDestination();
892 
893         if ( fwdAddr == null ) {    //  defensive programming -- should not occur
894             String errmsg = "For user " + username + ", the system indicates that forwarding is set but no forwarding destination was found";
895             out.println(errmsg);
896             getLogger().error(errmsg);
897             return true;
898         }
899 
900         writeLoggedFlushedResponse("Current forwarding destination for " + username + " is: " + fwdAddr);
901         return true;
902     }
903 
904     /***
905      * Handler method called upon receipt of an UNSETALIAS command.
906      * Returns whether further commands should be read off the wire.
907      *
908      * @param argument the argument passed in with the command
909      */
910     private boolean doUNSETALIAS(String argument) {
911         if ((argument == null) || (argument.equals(""))) {
912             writeLoggedFlushedResponse("Usage: unsetalias [username]");
913             return true;
914         }
915         String username = argument;
916         JamesUser user = (JamesUser) users.getUserByName(username);
917         if (user == null) {
918             writeLoggedResponse("No such user " + username);
919         } else if (user.getAliasing()){
920             user.setAliasing(false);
921             users.updateUser(user);
922             StringBuffer responseBuffer =
923                 new StringBuffer(64)
924                         .append("Alias for ")
925                         .append(username)
926                         .append(" unset");
927             String response = responseBuffer.toString();
928             out.println(response);
929             getLogger().info(response);
930         } else {
931             writeLoggedResponse("Aliasing not active for" + username);
932         }
933         out.flush();
934         return true;
935     }
936 
937     /***
938      * Handler method called upon receipt of an UNSETFORWARDING command.
939      * Returns whether further commands should be read off the wire.
940      *
941      * @param argument the argument passed in with the command
942      */
943     private boolean doUNSETFORWARDING(String argument) {
944         if ((argument == null) || (argument.equals(""))) {
945             writeLoggedFlushedResponse("Usage: unsetforwarding [username]");
946             return true;
947         }
948         String username = argument;
949         JamesUser user = (JamesUser) users.getUserByName(username);
950         if (user == null) {
951             writeLoggedFlushedResponse("No such user " + username);
952         } else if (user.getForwarding()){
953             user.setForwarding(false);
954             users.updateUser(user);
955             StringBuffer responseBuffer =
956                 new StringBuffer(64)
957                         .append("Forward for ")
958                         .append(username)
959                         .append(" unset");
960             String response = responseBuffer.toString();
961             out.println(response);
962             out.flush();
963             getLogger().info(response);
964         } else {
965             writeLoggedFlushedResponse("Forwarding not active for" + username);
966         }
967         return true;
968     }
969 
970     /***
971      * Handler method called upon receipt of a USER command.
972      * Returns whether further commands should be read off the wire.
973      *
974      * @param argument the argument passed in with the command
975      */
976     private boolean doUSER(String argument) {
977         if (argument == null || argument.equals("")) {
978             writeLoggedFlushedResponse("Usage: user [repositoryName]");
979             return true;
980         }
981         String repositoryName = argument.toLowerCase(Locale.US);
982         UsersRepository repos = theConfigData.getUserStore().getRepository(repositoryName);
983         if ( repos == null ) {
984             writeLoggedFlushedResponse("No such repository: " + repositoryName);
985         } else {
986             users = repos;
987             StringBuffer responseBuffer =
988                 new StringBuffer(64)
989                         .append("Changed to repository '")
990                         .append(repositoryName)
991                         .append("'.");
992             writeLoggedFlushedResponse(responseBuffer.toString());
993         }
994         return true;
995     }
996 
997     /***
998      * Handler method called upon receipt of a QUIT command.
999      * Returns whether further commands should be read off the wire.
1000      *
1001      * @param argument the argument passed in with the command
1002      */
1003     private boolean doQUIT(String argument) {
1004         writeLoggedFlushedResponse("Bye");
1005         return false;
1006     }
1007 
1008     /***
1009      * Handler method called upon receipt of a SHUTDOWN command.
1010      * Returns whether further commands should be read off the wire.
1011      *
1012      * @param argument the argument passed in with the command
1013      */
1014     private boolean doSHUTDOWN(String argument) {
1015         writeLoggedFlushedResponse("Shutting down, bye bye");
1016         System.exit(0);
1017         return false;
1018     }
1019 
1020     /***
1021      * Handler method called upon receipt of an unrecognized command.
1022      * Returns whether further commands should be read off the wire.
1023      *
1024      * @param argument the unknown command
1025      */
1026     private boolean doUnknownCommand(String argument) {
1027         writeLoggedFlushedResponse("Unknown command " + argument);
1028         return true;
1029     }
1030 
1031     /***
1032      * This method logs at a "DEBUG" level the response string that
1033      * was sent to the RemoteManager client.  The method is provided largely
1034      * as syntactic sugar to neaten up the code base.  It is declared
1035      * private and final to encourage compiler inlining.
1036      *
1037      * @param responseString the response string sent to the client
1038      */
1039     private final void logResponseString(String responseString) {
1040         if (getLogger().isDebugEnabled()) {
1041             getLogger().debug("Sent: " + responseString);
1042         }
1043     }
1044 
1045     /***
1046      * Write and flush a response string.  The response is also logged.
1047      * Should be used for the last line of a multi-line response or
1048      * for a single line response.
1049      *
1050      * @param responseString the response string sent to the client
1051      */
1052     final void writeLoggedFlushedResponse(String responseString) {
1053         out.println(responseString);
1054         out.flush();
1055         logResponseString(responseString);
1056     }
1057 
1058     /***
1059      * Write a response string.  The response is also logged.
1060      * Used for multi-line responses.
1061      *
1062      * @param responseString the response string sent to the client
1063      */
1064     final void writeLoggedResponse(String responseString) {
1065         out.println(responseString);
1066         logResponseString(responseString);
1067     }
1068 
1069     /***
1070      * A private inner class which serves as an adaptor
1071      * between the WatchdogTarget interface and this
1072      * handler class.
1073      */
1074     private class RemoteManagerWatchdogTarget
1075         implements WatchdogTarget {
1076 
1077         /***
1078          * @see org.apache.james.util.watchdog.WatchdogTarget#execute()
1079          */
1080         public void execute() {
1081             RemoteManagerHandler.this.idleClose();
1082         }
1083     }
1084 }