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
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
229 } finally {
230 socket = null;
231 }
232
233 synchronized (this) {
234
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
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
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
373 users = theConfigData.getUsersRepository();
374
375
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
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
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("") ) {
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
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 ) {
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 }