1 17 18 package org.apache.james.imapserver; 19 20 import org.apache.avalon.cornerstone.services.connection.ConnectionHandler; 21 import org.apache.avalon.cornerstone.services.scheduler.PeriodicTimeTrigger; 22 import org.apache.avalon.cornerstone.services.scheduler.Target; 23 import org.apache.avalon.cornerstone.services.scheduler.TimeScheduler; 24 import org.apache.avalon.framework.activity.Disposable; 25 import org.apache.avalon.framework.activity.Initializable; 26 import org.apache.avalon.framework.component.ComponentException; 27 import org.apache.avalon.framework.component.ComponentManager; 28 import org.apache.avalon.framework.component.Composable; 29 import org.apache.avalon.framework.configuration.Configurable; 30 import org.apache.avalon.framework.logger.Logger; 31 import org.apache.james.imapserver.AccessControlException; 32 import org.apache.james.imapserver.AuthorizationException; 33 import org.apache.james.Constants; 34 import org.apache.james.imapserver.commands.ImapCommand; 35 import org.apache.james.imapserver.commands.ImapCommandFactory; 36 import org.apache.james.services.MailServer; 37 import org.apache.james.services.UsersRepository; 38 import org.apache.james.services.UsersStore; 39 import org.apache.james.util.InternetPrintWriter; 40 41 import java.io.*; 42 import java.net.Socket ; 43 import java.util.List ; 44 import java.util.StringTokenizer ; 45 46 54 public class SingleThreadedConnectionHandler 55 extends BaseCommand 56 implements ConnectionHandler, Composable, Configurable, 57 Initializable, Disposable, Target, MailboxEventListener, 58 ImapSession, ImapConstants 59 { 60 61 private Logger securityLogger; 62 private MailServer mailServer; 63 private UsersRepository users; 64 private TimeScheduler scheduler; 65 66 private ImapSession _session; 67 private MailboxEventListener _mailboxListener; 68 69 private ImapCommandFactory _imapCommands; 70 71 private Socket socket; 72 private BufferedReader in; 73 private PrintWriter out; 74 private OutputStream outs; 75 private String remoteHost; 76 private String remoteIP; 77 private String softwaretype = "JAMES IMAP4rev1 Server " + Constants.SOFTWARE_VERSION; 78 private ImapSessionState state; 79 private String user; 80 81 private IMAPSystem imapSystem; 82 private Host imapHost; 83 private String namespaceToken; 84 private String currentNamespace = null; 85 private String currentSeperator = null; 86 private String commandRaw; 87 88 private String currentFolder = null; 91 private ACLMailbox currentMailbox = null; 92 private boolean currentIsReadOnly = false; 93 private boolean connectionClosed = false; 94 private String tag; 95 private boolean checkMailboxFlag = false; 96 private int exists; 97 private int recent; 98 private List sequence; 99 100 private boolean canParseCommand = true; 101 102 public SingleThreadedConnectionHandler() 103 { 104 _session = this; 105 _mailboxListener = this; 106 107 _imapCommands = new ImapCommandFactory(); 108 } 109 110 115 public void enableLogging( Logger logger ) 116 { 117 super.enableLogging( logger ); 118 _imapCommands.enableLogging( logger ); 119 } 120 121 public void compose( final ComponentManager componentManager ) 122 throws ComponentException 123 { 124 125 mailServer = (MailServer) componentManager. 126 lookup( "org.apache.james.services.MailServer" ); 127 UsersStore usersStore = (UsersStore) componentManager. 128 lookup( "org.apache.james.services.UsersStore" ); 129 users = usersStore.getRepository( "LocalUsers" ); 130 scheduler = (TimeScheduler) componentManager. 131 lookup( "org.apache.avalon.cornerstone.services.scheduler.TimeScheduler" ); 132 imapSystem = (IMAPSystem) componentManager. 133 lookup( "org.apache.james.imapserver.IMAPSystem" ); 134 imapHost = (Host) componentManager. 135 lookup( "org.apache.james.imapserver.Host" ); 136 } 137 138 public void initialize() throws Exception 139 { 140 getLogger().info( "SingleThreadedConnectionHandler starting ..." ); 141 securityLogger = getLogger().getChildLogger( "security" ); 142 getLogger().info( "SingleThreadedConnectionHandler initialized" ); 143 } 144 145 153 public void handleConnection( final Socket connection ) 154 throws IOException 155 { 156 157 try { 158 this.socket = connection; 159 setIn( new BufferedReader( new 160 InputStreamReader( socket.getInputStream() ) ) ); 161 outs = socket.getOutputStream(); 162 setOut( new InternetPrintWriter( outs, true ) ); 163 remoteHost = socket.getInetAddress().getHostName(); 164 remoteIP = socket.getInetAddress().getHostAddress(); 165 } 166 catch ( Exception e ) { 167 getLogger().error( "Cannot open connection from " + getRemoteHost() + " (" 168 + getRemoteIP() + "): " + e.getMessage() ); 169 } 170 getLogger().info( "Connection from " + getRemoteHost() + " (" + getRemoteIP() + ")" ); 171 172 try { 173 final PeriodicTimeTrigger trigger = new PeriodicTimeTrigger( timeout, -1 ); 174 scheduler.addTrigger( this.toString(), trigger, this ); 175 176 if ( false ) { setConnectionClosed( closeConnection( UNTAGGED_BYE, 180 " connection rejected.", 181 "" ) ); 182 } 183 else { 184 if ( false ) { untaggedResponse( "PREAUTH" + SP + VERSION + SP 186 + "server" + SP + this.helloName + SP 187 + "logged in as" + SP + _session.getCurrentUser() ); 188 _session.setState( ImapSessionState.AUTHENTICATED ); 189 _session.setCurrentUser( "preauth user" ); 190 getSecurityLogger().info( "Pre-authenticated connection from " 191 + getRemoteHost() + "(" + getRemoteIP() 192 + ") received by SingleThreadedConnectionHandler" ); 193 } 194 else { 195 _session.getOut().println( UNTAGGED + SP + OK + SP + VERSION + SP 196 + "Server " + this.helloName + SP + "ready" ); 197 _session.setState( ImapSessionState.NON_AUTHENTICATED ); 198 _session.setCurrentUser( "unknown" ); 199 getSecurityLogger().info( "Non-authenticated connection from " 200 + getRemoteHost() + "(" + getRemoteIP() 201 + ") received by SingleThreadedConnectionHandler" ); 202 } 203 204 while ( true ) { 205 if (this.getCanParseCommand()) { 206 if(! parseCommand( in.readLine())) break; 207 } 208 scheduler.resetTrigger( this.toString() ); 209 } 210 } 211 212 if ( !isConnectionClosed() ) { 213 setConnectionClosed( closeConnection( UNTAGGED_BYE, 214 "Server error, closing connection", "" ) ); 215 } 216 217 } 218 catch ( Exception e ) { 219 getLogger().error( "Exception during connection from " + getRemoteHost() 221 + " (" + getRemoteIP() + ") : " + e.getMessage() ); 222 e.printStackTrace(); 223 setConnectionClosed( closeConnection( UNTAGGED_BYE, 224 "Error processing command.", "" ) ); 225 } 226 227 scheduler.removeTrigger( this.toString() ); 228 } 229 230 public void targetTriggered( final String triggerName ) 231 { 232 getLogger().info( "Connection timeout on socket" ); 233 setConnectionClosed( closeConnection( UNTAGGED_BYE, 234 "Autologout. Idle too long.", "" ) ); 235 } 236 237 public boolean closeConnection( int exitStatus, 238 String message1, 239 String message2 ) 240 { 241 scheduler.removeTrigger( this.toString() ); 242 if ( _session.getState() == ImapSessionState.SELECTED ) { 243 getCurrentMailbox().removeMailboxEventListener( this ); 244 getImapHost().releaseMailbox( _session.getCurrentUser(), getCurrentMailbox() ); 245 } 246 247 try { 248 switch ( exitStatus ) { 249 case 0: 250 untaggedResponse( "BYE" + SP + "Server logging out" ); 251 okResponse( "LOGOUT" ); 252 break; 253 case 1: 254 untaggedResponse( "BYE" + SP + message1 ); 255 okResponse( message2 ); 256 break; 257 case 2: 258 untaggedResponse( "BYE" + SP + message1 ); 259 break; 260 case 3: 261 noResponse( message1 ); 262 break; 263 case 4: 264 untaggedResponse( "BYE" + SP + message1 ); 265 noResponse( message2 ); 266 break; 267 } 268 _session.getOut().flush(); 269 socket.close(); 270 getLogger().info( "Connection closed" + SP + exitStatus + SP + message1 271 + SP + message2 ); 272 } 273 catch ( IOException ioe ) { 274 getLogger().error( "Exception while closing connection from " + getRemoteHost() 275 + " (" + getRemoteIP() + ") : " + ioe.getMessage() ); 276 try { 277 socket.close(); 278 } 279 catch ( IOException ioe2 ) { 280 } 281 } 282 return true; 283 } 284 285 private boolean parseCommand( String next ) 286 { 287 commandRaw = next; 288 String folder = null; 289 String command = null; 290 boolean subscribeOnly = false; 291 System.out.println("PARSING COMMAND FROM CILENT: "+next); 292 if ( commandRaw == null ) return false; 293 StringTokenizer commandLine = new StringTokenizer ( commandRaw.trim(), " " ); 294 int arguments = commandLine.countTokens(); 295 if ( arguments == 0 ) { 296 return true; 297 }else { 298 tag = commandLine.nextToken(); 299 if ( tag.length() > 10 ) { 300 badResponse( "tag too long" ); 303 return true; 304 } 305 } 306 if ( arguments > 1 ) { 307 command = commandLine.nextToken(); 308 if ( command.length() > 13 ) { badResponse( "overlong command attempted" ); 312 return true; 313 } 314 } else { 315 badResponse( "no command sent" ); 316 return true; 317 } 318 319 ImapRequestImpl request = new ImapRequestImpl( this, command ); 321 request.setCommandLine( commandLine ); 322 request.setUseUIDs( false ); 323 request.setCurrentMailbox( getCurrentMailbox() ); 324 request.setCommandRaw( commandRaw ); 325 request.setTag( tag ); 326 request.setCurrentFolder( getCurrentFolder() ); 327 328 332 335 336 339 342 345 ImapCommand cmd = getImapCommand( command ); 346 347 if ( ! cmd.validForState( state ) ) { 348 badResponse( command + " not valid in this state" ); 349 return true; 350 } 351 return cmd.process( request, this ); 352 } 353 354 public ImapCommand getImapCommand( String command ) 355 { 356 return _imapCommands.getCommand( command ); 357 } 358 359 private void invalidStateResponse( String command ) 360 { 361 badResponse( command + " not valid in this state" ); 362 } 363 364 public void okResponse( String command ) 365 { 366 taggedResponse( OK + SP + command + " completed" ); 367 } 368 369 public void noResponse( String command ) 370 { 371 noResponse( command, "failed" ); 372 } 373 374 public void noResponse( String command, String msg ) 375 { 376 taggedResponse( NO + SP + command + SP + msg ); 377 } 378 379 public void badResponse( String badMsg ) 380 { 381 taggedResponse( BAD + SP + badMsg ); 382 } 383 384 public void notImplementedResponse( String command ) 385 { 386 badResponse( command + " not implemented." ); 387 } 388 389 public void taggedResponse( String msg ) 390 { 391 _session.getOut().println( tag + SP + msg ); 392 } 393 394 public void untaggedResponse( String msg ) 395 { 396 _session.getOut().println( UNTAGGED + SP + msg ); 397 } 398 399 public void dispose() 400 { 401 getLogger().error( "Stop IMAPHandler" ); 403 } 404 405 public void receiveEvent( MailboxEvent me ) 406 { 407 if ( _session.getState() == ImapSessionState.SELECTED ) { 408 checkMailboxFlag = true; 409 } 410 } 411 412 438 public void logACE( AccessControlException ace ) 439 { 440 getSecurityLogger().error( "AccessControlException by user " + _session.getCurrentUser() 441 + " from " + getRemoteHost() + "(" + getRemoteIP() 442 + ") with " + commandRaw + " was " 443 + ace.getMessage() ); 444 } 445 446 public void logAZE( AuthorizationException aze ) 447 { 448 getSecurityLogger().error( "AuthorizationException by user " + _session.getCurrentUser() 449 + " from " + getRemoteHost() + "(" + getRemoteIP() 450 + ") with " + commandRaw + " was " 451 + aze.getMessage() ); 452 } 453 454 public PrintWriter getPrintWriter() 455 { 456 return _session.getOut(); 457 } 458 459 public OutputStream getOutputStream() 460 { 461 return outs; 462 } 463 464 public String getUser() 465 { 466 return _session.getCurrentUser(); 467 } 468 469 public void checkSize() 470 { 471 int newExists = getCurrentMailbox().getExists(); 472 if ( newExists != exists ) { 473 _session.getOut().println( UNTAGGED + SP + newExists + " EXISTS" ); 474 exists = newExists; 475 } 476 int newRecent = getCurrentMailbox().getRecent(); 477 if ( newRecent != recent ) { 478 _session.getOut().println( UNTAGGED + SP + newRecent + " RECENT" ); 479 recent = newRecent; 480 } 481 return; 482 } 483 484 public void checkExpunge() 485 { 486 List newList = getCurrentMailbox().listUIDs( _session.getCurrentUser() ); 487 for ( int k = 0; k < newList.size(); k++ ) { 488 getLogger().debug( "New List msn " + (k + 1) + " is uid " + newList.get( k ) ); 489 } 490 for ( int i = sequence.size() - 1; i > -1; i-- ) { 491 Integer j = (Integer ) sequence.get( i ); 492 getLogger().debug( "Looking for old msn " + (i + 1) + " was uid " + j ); 493 if ( !newList.contains( (Integer ) sequence.get( i ) ) ) { 494 _session.getOut().println( UNTAGGED + SP + (i + 1) + " EXPUNGE" ); 495 } 496 } 497 sequence = newList; 498 return; 500 } 501 502 public ImapSessionState getState() 503 { 504 return state; 505 } 506 507 public void setState( ImapSessionState state ) 508 { 509 this.state = state; 510 exists = -1; 511 recent = -1; 512 } 513 514 public BufferedReader getIn() 515 { 516 return in; 517 } 518 519 public void setIn( BufferedReader in ) 520 { 521 this.in = in; 522 } 523 524 public PrintWriter getOut() 525 { 526 return out; 527 } 528 529 public void setOut( PrintWriter out ) 530 { 531 this.out = out; 532 } 533 534 public String getRemoteHost() 535 { 536 return remoteHost; 537 } 538 539 public String getRemoteIP() 540 { 541 return remoteIP; 542 } 543 544 public Logger getDebugLogger() 545 { 546 return getLogger(); 547 } 548 549 public Logger getSecurityLogger() 550 { 551 return securityLogger; 552 } 553 554 public UsersRepository getUsers() 555 { 556 return users; 557 } 558 559 public IMAPSystem getImapSystem() 560 { 561 return imapSystem; 562 } 563 564 public Host getImapHost() 565 { 566 return imapHost; 567 } 568 569 public String getCurrentNamespace() 570 { 571 return currentNamespace; 572 } 573 574 public void setCurrentNamespace( String currentNamespace ) 575 { 576 this.currentNamespace = currentNamespace; 577 } 578 579 public String getCurrentSeperator() 580 { 581 return currentSeperator; 582 } 583 584 public void setCurrentSeperator( String currentSeperator ) 585 { 586 this.currentSeperator = currentSeperator; 587 } 588 589 public String getCurrentFolder() 590 { 591 return currentFolder; 592 } 593 594 public void setCurrentFolder( String currentFolder ) 595 { 596 this.currentFolder = currentFolder; 597 } 598 599 public ACLMailbox getCurrentMailbox() 600 { 601 return currentMailbox; 602 } 603 604 public void setCurrentMailbox( ACLMailbox currentMailbox ) 605 { 606 this.currentMailbox = currentMailbox; 607 } 608 609 public boolean isCurrentIsReadOnly() 610 { 611 return currentIsReadOnly; 612 } 613 614 public void setCurrentIsReadOnly( boolean currentIsReadOnly ) 615 { 616 this.currentIsReadOnly = currentIsReadOnly; 617 } 618 619 public boolean isConnectionClosed() 620 { 621 return connectionClosed; 622 } 623 624 public void setConnectionClosed( boolean connectionClosed ) 625 { 626 this.connectionClosed = connectionClosed; 627 } 628 629 public String getCurrentUser() 630 { 631 return user; 632 } 633 634 public void setCurrentUser( String user ) 635 { 636 this.user = user; 637 } 638 639 public void setSequence( List sequence ) 640 { 641 this.sequence = sequence; 642 } 643 644 public List decodeSet( String rawSet, int exists ) throws IllegalArgumentException 645 { 646 return super.decodeSet( rawSet, exists ); 647 } 648 649 public void setCanParseCommand(boolean canParseCommand) { 650 this.canParseCommand = canParseCommand; 651 } 652 public boolean getCanParseCommand() { 653 return this.canParseCommand; 654 } 655 } 656 | Popular Tags |