PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/james-2.2.0/proposals/imap/java/org/apache/james/imapserver/SingleThreadedConnectionHandler.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 655 lines | 483 code | 87 blank | 85 comment | 29 complexity | 36abd32fdaf346d0279d5c327534323d MD5 | raw file
  1. /***********************************************************************
  2. * Copyright (c) 2000-2004 The Apache Software Foundation. *
  3. * All rights reserved. *
  4. * ------------------------------------------------------------------- *
  5. * Licensed under the Apache License, Version 2.0 (the "License"); you *
  6. * may not use this file except in compliance with the License. You *
  7. * may obtain a copy of the License at: *
  8. * *
  9. * http://www.apache.org/licenses/LICENSE-2.0 *
  10. * *
  11. * Unless required by applicable law or agreed to in writing, software *
  12. * distributed under the License is distributed on an "AS IS" BASIS, *
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or *
  14. * implied. See the License for the specific language governing *
  15. * permissions and limitations under the License. *
  16. ***********************************************************************/
  17. package org.apache.james.imapserver;
  18. import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
  19. import org.apache.avalon.cornerstone.services.scheduler.PeriodicTimeTrigger;
  20. import org.apache.avalon.cornerstone.services.scheduler.Target;
  21. import org.apache.avalon.cornerstone.services.scheduler.TimeScheduler;
  22. import org.apache.avalon.framework.activity.Disposable;
  23. import org.apache.avalon.framework.activity.Initializable;
  24. import org.apache.avalon.framework.component.ComponentException;
  25. import org.apache.avalon.framework.component.ComponentManager;
  26. import org.apache.avalon.framework.component.Composable;
  27. import org.apache.avalon.framework.configuration.Configurable;
  28. import org.apache.avalon.framework.logger.Logger;
  29. import org.apache.james.imapserver.AccessControlException;
  30. import org.apache.james.imapserver.AuthorizationException;
  31. import org.apache.james.Constants;
  32. import org.apache.james.imapserver.commands.ImapCommand;
  33. import org.apache.james.imapserver.commands.ImapCommandFactory;
  34. import org.apache.james.services.MailServer;
  35. import org.apache.james.services.UsersRepository;
  36. import org.apache.james.services.UsersStore;
  37. import org.apache.james.util.InternetPrintWriter;
  38. import java.io.*;
  39. import java.net.Socket;
  40. import java.util.List;
  41. import java.util.StringTokenizer;
  42. /**
  43. * An IMAP Handler handles one IMAP connection. TBC - it may spawn worker
  44. * threads someday.
  45. *
  46. * <p> Based on SMTPHandler and POP3Handler by Federico Barbieri <scoobie@systemy.it>
  47. *
  48. * @version 0.3 on 08 Aug 2002
  49. */
  50. public class SingleThreadedConnectionHandler
  51. extends BaseCommand
  52. implements ConnectionHandler, Composable, Configurable,
  53. Initializable, Disposable, Target, MailboxEventListener,
  54. ImapSession, ImapConstants
  55. {
  56. private Logger securityLogger;
  57. private MailServer mailServer;
  58. private UsersRepository users;
  59. private TimeScheduler scheduler;
  60. private ImapSession _session;
  61. private MailboxEventListener _mailboxListener;
  62. private ImapCommandFactory _imapCommands;
  63. private Socket socket;
  64. private BufferedReader in;
  65. private PrintWriter out;
  66. private OutputStream outs;
  67. private String remoteHost;
  68. private String remoteIP;
  69. private String softwaretype = "JAMES IMAP4rev1 Server " + Constants.SOFTWARE_VERSION;
  70. private ImapSessionState state;
  71. private String user;
  72. private IMAPSystem imapSystem;
  73. private Host imapHost;
  74. private String namespaceToken;
  75. private String currentNamespace = null;
  76. private String currentSeperator = null;
  77. private String commandRaw;
  78. //currentFolder holds the client-dependent absolute address of the current
  79. //folder, that is current Namespace and full mailbox hierarchy.
  80. private String currentFolder = null;
  81. private ACLMailbox currentMailbox = null;
  82. private boolean currentIsReadOnly = false;
  83. private boolean connectionClosed = false;
  84. private String tag;
  85. private boolean checkMailboxFlag = false;
  86. private int exists;
  87. private int recent;
  88. private List sequence;
  89. private boolean canParseCommand = true;
  90. public SingleThreadedConnectionHandler()
  91. {
  92. _session = this;
  93. _mailboxListener = this;
  94. _imapCommands = new ImapCommandFactory();
  95. }
  96. /**
  97. * Set the components logger.
  98. *
  99. * @param logger the logger
  100. */
  101. public void enableLogging( Logger logger )
  102. {
  103. super.enableLogging( logger );
  104. _imapCommands.enableLogging( logger );
  105. }
  106. public void compose( final ComponentManager componentManager )
  107. throws ComponentException
  108. {
  109. mailServer = (MailServer) componentManager.
  110. lookup( "org.apache.james.services.MailServer" );
  111. UsersStore usersStore = (UsersStore) componentManager.
  112. lookup( "org.apache.james.services.UsersStore" );
  113. users = usersStore.getRepository( "LocalUsers" );
  114. scheduler = (TimeScheduler) componentManager.
  115. lookup( "org.apache.avalon.cornerstone.services.scheduler.TimeScheduler" );
  116. imapSystem = (IMAPSystem) componentManager.
  117. lookup( "org.apache.james.imapserver.IMAPSystem" );
  118. imapHost = (Host) componentManager.
  119. lookup( "org.apache.james.imapserver.Host" );
  120. }
  121. public void initialize() throws Exception
  122. {
  123. getLogger().info( "SingleThreadedConnectionHandler starting ..." );
  124. securityLogger = getLogger().getChildLogger( "security" );
  125. getLogger().info( "SingleThreadedConnectionHandler initialized" );
  126. }
  127. /**
  128. * Handle a connection.
  129. * This handler is responsible for processing connections as they occur.
  130. *
  131. * @param connection the connection
  132. * @exception IOException if an error reading from socket occurs
  133. * @exception ProtocolException if an error handling connection occurs
  134. */
  135. public void handleConnection( final Socket connection )
  136. throws IOException
  137. {
  138. try {
  139. this.socket = connection;
  140. setIn( new BufferedReader( new
  141. InputStreamReader( socket.getInputStream() ) ) );
  142. outs = socket.getOutputStream();
  143. setOut( new InternetPrintWriter( outs, true ) );
  144. remoteHost = socket.getInetAddress().getHostName();
  145. remoteIP = socket.getInetAddress().getHostAddress();
  146. }
  147. catch ( Exception e ) {
  148. getLogger().error( "Cannot open connection from " + getRemoteHost() + " ("
  149. + getRemoteIP() + "): " + e.getMessage() );
  150. }
  151. getLogger().info( "Connection from " + getRemoteHost() + " (" + getRemoteIP() + ")" );
  152. try {
  153. final PeriodicTimeTrigger trigger = new PeriodicTimeTrigger( timeout, -1 );
  154. scheduler.addTrigger( this.toString(), trigger, this );
  155. if ( false ) { // arbitrary rejection of connection
  156. // could screen connections by IP or host or implement
  157. // connection pool management
  158. setConnectionClosed( closeConnection( UNTAGGED_BYE,
  159. " connection rejected.",
  160. "" ) );
  161. }
  162. else {
  163. if ( false ) { // connection is pre-authenticated
  164. untaggedResponse( "PREAUTH" + SP + VERSION + SP
  165. + "server" + SP + this.helloName + SP
  166. + "logged in as" + SP + _session.getCurrentUser() );
  167. _session.setState( ImapSessionState.AUTHENTICATED );
  168. _session.setCurrentUser( "preauth user" );
  169. getSecurityLogger().info( "Pre-authenticated connection from "
  170. + getRemoteHost() + "(" + getRemoteIP()
  171. + ") received by SingleThreadedConnectionHandler" );
  172. }
  173. else {
  174. _session.getOut().println( UNTAGGED + SP + OK + SP + VERSION + SP
  175. + "Server " + this.helloName + SP + "ready" );
  176. _session.setState( ImapSessionState.NON_AUTHENTICATED );
  177. _session.setCurrentUser( "unknown" );
  178. getSecurityLogger().info( "Non-authenticated connection from "
  179. + getRemoteHost() + "(" + getRemoteIP()
  180. + ") received by SingleThreadedConnectionHandler" );
  181. }
  182. while ( true ) {
  183. if (this.getCanParseCommand()) {
  184. if(! parseCommand( in.readLine())) break;
  185. }
  186. scheduler.resetTrigger( this.toString() );
  187. }
  188. }
  189. if ( !isConnectionClosed() ) {
  190. setConnectionClosed( closeConnection( UNTAGGED_BYE,
  191. "Server error, closing connection", "" ) );
  192. }
  193. }
  194. catch ( Exception e ) {
  195. // This should never happen once code is debugged
  196. getLogger().error( "Exception during connection from " + getRemoteHost()
  197. + " (" + getRemoteIP() + ") : " + e.getMessage() );
  198. e.printStackTrace();
  199. setConnectionClosed( closeConnection( UNTAGGED_BYE,
  200. "Error processing command.", "" ) );
  201. }
  202. scheduler.removeTrigger( this.toString() );
  203. }
  204. public void targetTriggered( final String triggerName )
  205. {
  206. getLogger().info( "Connection timeout on socket" );
  207. setConnectionClosed( closeConnection( UNTAGGED_BYE,
  208. "Autologout. Idle too long.", "" ) );
  209. }
  210. public boolean closeConnection( int exitStatus,
  211. String message1,
  212. String message2 )
  213. {
  214. scheduler.removeTrigger( this.toString() );
  215. if ( _session.getState() == ImapSessionState.SELECTED ) {
  216. getCurrentMailbox().removeMailboxEventListener( this );
  217. getImapHost().releaseMailbox( _session.getCurrentUser(), getCurrentMailbox() );
  218. }
  219. try {
  220. switch ( exitStatus ) {
  221. case 0:
  222. untaggedResponse( "BYE" + SP + "Server logging out" );
  223. okResponse( "LOGOUT" );
  224. break;
  225. case 1:
  226. untaggedResponse( "BYE" + SP + message1 );
  227. okResponse( message2 );
  228. break;
  229. case 2:
  230. untaggedResponse( "BYE" + SP + message1 );
  231. break;
  232. case 3:
  233. noResponse( message1 );
  234. break;
  235. case 4:
  236. untaggedResponse( "BYE" + SP + message1 );
  237. noResponse( message2 );
  238. break;
  239. }
  240. _session.getOut().flush();
  241. socket.close();
  242. getLogger().info( "Connection closed" + SP + exitStatus + SP + message1
  243. + SP + message2 );
  244. }
  245. catch ( IOException ioe ) {
  246. getLogger().error( "Exception while closing connection from " + getRemoteHost()
  247. + " (" + getRemoteIP() + ") : " + ioe.getMessage() );
  248. try {
  249. socket.close();
  250. }
  251. catch ( IOException ioe2 ) {
  252. }
  253. }
  254. return true;
  255. }
  256. private boolean parseCommand( String next )
  257. {
  258. commandRaw = next;
  259. String folder = null;
  260. String command = null;
  261. boolean subscribeOnly = false;
  262. System.out.println("PARSING COMMAND FROM CILENT: "+next);
  263. if ( commandRaw == null ) return false;
  264. StringTokenizer commandLine = new StringTokenizer( commandRaw.trim(), " " );
  265. int arguments = commandLine.countTokens();
  266. if ( arguments == 0 ) {
  267. return true;
  268. }else {
  269. tag = commandLine.nextToken();
  270. if ( tag.length() > 10 ) {
  271. // this stops overlong junk.
  272. // Should do more validation
  273. badResponse( "tag too long" );
  274. return true;
  275. }
  276. }
  277. if ( arguments > 1 ) {
  278. command = commandLine.nextToken();
  279. if ( command.length() > 13 ) {// this stops overlong junk.
  280. // we could validate the command contents,
  281. // but may not be worth it
  282. badResponse( "overlong command attempted" );
  283. return true;
  284. }
  285. } else {
  286. badResponse( "no command sent" );
  287. return true;
  288. }
  289. // Create ImapRequestImpl object here - is this the right stage?
  290. ImapRequestImpl request = new ImapRequestImpl( this, command );
  291. request.setCommandLine( commandLine );
  292. request.setUseUIDs( false );
  293. request.setCurrentMailbox( getCurrentMailbox() );
  294. request.setCommandRaw( commandRaw );
  295. request.setTag( tag );
  296. request.setCurrentFolder( getCurrentFolder() );
  297. // At this stage we have a tag and a string which may be a command
  298. // Start with commands that are valid in any state
  299. // CAPABILITY, NOOP, LOGOUT
  300. // Commands only valid in NON_AUTHENTICATED state
  301. // AUTHENTICATE, LOGIN
  302. // Commands valid in both Authenticated and Selected states
  303. // NAMESPACE, GETACL, SETACL, DELETEACL, LISTRIGHTS, MYRIGHTS, SELECT
  304. // Commands valid only in Authenticated State
  305. // None
  306. // Commands valid only in Selected state
  307. // CHECK CLOSE COPY EXPUNGE FETCH STORE UID
  308. ImapCommand cmd = getImapCommand( command );
  309. if ( ! cmd.validForState( state ) ) {
  310. badResponse( command + " not valid in this state" );
  311. return true;
  312. }
  313. return cmd.process( request, this );
  314. }
  315. public ImapCommand getImapCommand( String command )
  316. {
  317. return _imapCommands.getCommand( command );
  318. }
  319. private void invalidStateResponse( String command )
  320. {
  321. badResponse( command + " not valid in this state" );
  322. }
  323. public void okResponse( String command )
  324. {
  325. taggedResponse( OK + SP + command + " completed" );
  326. }
  327. public void noResponse( String command )
  328. {
  329. noResponse( command, "failed" );
  330. }
  331. public void noResponse( String command, String msg )
  332. {
  333. taggedResponse( NO + SP + command + SP + msg );
  334. }
  335. public void badResponse( String badMsg )
  336. {
  337. taggedResponse( BAD + SP + badMsg );
  338. }
  339. public void notImplementedResponse( String command )
  340. {
  341. badResponse( command + " not implemented." );
  342. }
  343. public void taggedResponse( String msg )
  344. {
  345. _session.getOut().println( tag + SP + msg );
  346. }
  347. public void untaggedResponse( String msg )
  348. {
  349. _session.getOut().println( UNTAGGED + SP + msg );
  350. }
  351. public void dispose()
  352. {
  353. // todo
  354. getLogger().error( "Stop IMAPHandler" );
  355. }
  356. public void receiveEvent( MailboxEvent me )
  357. {
  358. if ( _session.getState() == ImapSessionState.SELECTED ) {
  359. checkMailboxFlag = true;
  360. }
  361. }
  362. // public ACLMailbox getBox( String user, String mailboxName ) throws MailboxException, AccessControlException
  363. // {
  364. // return
  365. // ACLMailbox tempMailbox = null;
  366. // try {
  367. // tempMailbox = getImapHost().getMailbox( user, mailboxName );
  368. // }
  369. // catch ( MailboxException me ) {
  370. // if ( me.isRemote() ) {
  371. // _session.getOut().println( tag + SP + NO + SP + "[REFERRAL " + me.getRemoteServer() + "]" + SP + "Remote mailbox" );
  372. // }
  373. // else {
  374. // _session.noResponse(
  375. // _session.getOut().println( tag + SP + NO + SP + "Unknown mailbox" );
  376. // getLogger().info( "MailboxException in method getBox for user: "
  377. // + user + " mailboxName: " + mailboxName + " was "
  378. // + me.getMessage() );
  379. // }
  380. //
  381. // }
  382. // catch ( AccessControlException e ) {
  383. // _session.getOut().println( tag + SP + NO + SP + "Unknown mailbox" );
  384. // }
  385. // return tempMailbox;
  386. // }
  387. public void logACE( AccessControlException ace )
  388. {
  389. getSecurityLogger().error( "AccessControlException by user " + _session.getCurrentUser()
  390. + " from " + getRemoteHost() + "(" + getRemoteIP()
  391. + ") with " + commandRaw + " was "
  392. + ace.getMessage() );
  393. }
  394. public void logAZE( AuthorizationException aze )
  395. {
  396. getSecurityLogger().error( "AuthorizationException by user " + _session.getCurrentUser()
  397. + " from " + getRemoteHost() + "(" + getRemoteIP()
  398. + ") with " + commandRaw + " was "
  399. + aze.getMessage() );
  400. }
  401. public PrintWriter getPrintWriter()
  402. {
  403. return _session.getOut();
  404. }
  405. public OutputStream getOutputStream()
  406. {
  407. return outs;
  408. }
  409. public String getUser()
  410. {
  411. return _session.getCurrentUser();
  412. }
  413. public void checkSize()
  414. {
  415. int newExists = getCurrentMailbox().getExists();
  416. if ( newExists != exists ) {
  417. _session.getOut().println( UNTAGGED + SP + newExists + " EXISTS" );
  418. exists = newExists;
  419. }
  420. int newRecent = getCurrentMailbox().getRecent();
  421. if ( newRecent != recent ) {
  422. _session.getOut().println( UNTAGGED + SP + newRecent + " RECENT" );
  423. recent = newRecent;
  424. }
  425. return;
  426. }
  427. public void checkExpunge()
  428. {
  429. List newList = getCurrentMailbox().listUIDs( _session.getCurrentUser() );
  430. for ( int k = 0; k < newList.size(); k++ ) {
  431. getLogger().debug( "New List msn " + (k + 1) + " is uid " + newList.get( k ) );
  432. }
  433. for ( int i = sequence.size() - 1; i > -1; i-- ) {
  434. Integer j = (Integer) sequence.get( i );
  435. getLogger().debug( "Looking for old msn " + (i + 1) + " was uid " + j );
  436. if ( !newList.contains( (Integer) sequence.get( i ) ) ) {
  437. _session.getOut().println( UNTAGGED + SP + (i + 1) + " EXPUNGE" );
  438. }
  439. }
  440. sequence = newList;
  441. //newList = null;
  442. return;
  443. }
  444. public ImapSessionState getState()
  445. {
  446. return state;
  447. }
  448. public void setState( ImapSessionState state )
  449. {
  450. this.state = state;
  451. exists = -1;
  452. recent = -1;
  453. }
  454. public BufferedReader getIn()
  455. {
  456. return in;
  457. }
  458. public void setIn( BufferedReader in )
  459. {
  460. this.in = in;
  461. }
  462. public PrintWriter getOut()
  463. {
  464. return out;
  465. }
  466. public void setOut( PrintWriter out )
  467. {
  468. this.out = out;
  469. }
  470. public String getRemoteHost()
  471. {
  472. return remoteHost;
  473. }
  474. public String getRemoteIP()
  475. {
  476. return remoteIP;
  477. }
  478. public Logger getDebugLogger()
  479. {
  480. return getLogger();
  481. }
  482. public Logger getSecurityLogger()
  483. {
  484. return securityLogger;
  485. }
  486. public UsersRepository getUsers()
  487. {
  488. return users;
  489. }
  490. public IMAPSystem getImapSystem()
  491. {
  492. return imapSystem;
  493. }
  494. public Host getImapHost()
  495. {
  496. return imapHost;
  497. }
  498. public String getCurrentNamespace()
  499. {
  500. return currentNamespace;
  501. }
  502. public void setCurrentNamespace( String currentNamespace )
  503. {
  504. this.currentNamespace = currentNamespace;
  505. }
  506. public String getCurrentSeperator()
  507. {
  508. return currentSeperator;
  509. }
  510. public void setCurrentSeperator( String currentSeperator )
  511. {
  512. this.currentSeperator = currentSeperator;
  513. }
  514. public String getCurrentFolder()
  515. {
  516. return currentFolder;
  517. }
  518. public void setCurrentFolder( String currentFolder )
  519. {
  520. this.currentFolder = currentFolder;
  521. }
  522. public ACLMailbox getCurrentMailbox()
  523. {
  524. return currentMailbox;
  525. }
  526. public void setCurrentMailbox( ACLMailbox currentMailbox )
  527. {
  528. this.currentMailbox = currentMailbox;
  529. }
  530. public boolean isCurrentIsReadOnly()
  531. {
  532. return currentIsReadOnly;
  533. }
  534. public void setCurrentIsReadOnly( boolean currentIsReadOnly )
  535. {
  536. this.currentIsReadOnly = currentIsReadOnly;
  537. }
  538. public boolean isConnectionClosed()
  539. {
  540. return connectionClosed;
  541. }
  542. public void setConnectionClosed( boolean connectionClosed )
  543. {
  544. this.connectionClosed = connectionClosed;
  545. }
  546. public String getCurrentUser()
  547. {
  548. return user;
  549. }
  550. public void setCurrentUser( String user )
  551. {
  552. this.user = user;
  553. }
  554. public void setSequence( List sequence )
  555. {
  556. this.sequence = sequence;
  557. }
  558. public List decodeSet( String rawSet, int exists ) throws IllegalArgumentException
  559. {
  560. return super.decodeSet( rawSet, exists );
  561. }
  562. public void setCanParseCommand(boolean canParseCommand) {
  563. this.canParseCommand = canParseCommand;
  564. }
  565. public boolean getCanParseCommand() {
  566. return this.canParseCommand;
  567. }
  568. }