PageRenderTime 54ms CodeModel.GetById 28ms app.highlight 22ms RepoModel.GetById 1ms 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
 18package org.apache.james.imapserver;
 19
 20import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
 21import org.apache.avalon.cornerstone.services.scheduler.PeriodicTimeTrigger;
 22import org.apache.avalon.cornerstone.services.scheduler.Target;
 23import org.apache.avalon.cornerstone.services.scheduler.TimeScheduler;
 24import org.apache.avalon.framework.activity.Disposable;
 25import org.apache.avalon.framework.activity.Initializable;
 26import org.apache.avalon.framework.component.ComponentException;
 27import org.apache.avalon.framework.component.ComponentManager;
 28import org.apache.avalon.framework.component.Composable;
 29import org.apache.avalon.framework.configuration.Configurable;
 30import org.apache.avalon.framework.logger.Logger;
 31import org.apache.james.imapserver.AccessControlException;
 32import org.apache.james.imapserver.AuthorizationException;
 33import org.apache.james.Constants;
 34import org.apache.james.imapserver.commands.ImapCommand;
 35import org.apache.james.imapserver.commands.ImapCommandFactory;
 36import org.apache.james.services.MailServer;
 37import org.apache.james.services.UsersRepository;
 38import org.apache.james.services.UsersStore;
 39import org.apache.james.util.InternetPrintWriter;
 40
 41import java.io.*;
 42import java.net.Socket;
 43import java.util.List;
 44import java.util.StringTokenizer;
 45
 46/**
 47 * An IMAP Handler handles one IMAP connection. TBC - it may spawn worker
 48 * threads someday.
 49 *
 50 * <p> Based on SMTPHandler and POP3Handler by Federico Barbieri <scoobie@systemy.it>
 51 *
 52 * @version 0.3 on 08 Aug 2002
 53 */
 54public 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    //currentFolder holds the client-dependent absolute address of the current
 89    //folder, that is current Namespace and full mailbox hierarchy.
 90    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    /**
111     * Set the components logger.
112     *
113     * @param logger the logger
114     */
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    /**
146     * Handle a connection.
147     * This handler is responsible for processing connections as they occur.
148     *
149     * @param connection the connection
150     * @exception IOException if an error reading from socket occurs
151     * @exception ProtocolException if an error handling connection occurs
152     */
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 ) { // arbitrary rejection of connection
177                // could screen connections by IP or host or implement
178                // connection pool management
179                setConnectionClosed( closeConnection( UNTAGGED_BYE,
180                                                      " connection rejected.",
181                                                      "" ) );
182            }
183            else {
184                if ( false ) { // connection is pre-authenticated
185                    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            // This should never happen once code is debugged
220            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                // this stops overlong junk.
301                // Should do more validation
302                badResponse( "tag too long" );
303                return true;
304            }
305        }
306        if ( arguments > 1 ) {
307            command = commandLine.nextToken();
308            if ( command.length() > 13 ) {// this stops overlong junk.
309                // we could validate the command contents,
310                // but may not be worth it
311                badResponse( "overlong command attempted" );
312                return true;
313            }
314        } else {
315            badResponse( "no command sent" );
316            return true;
317        }
318        
319        // Create ImapRequestImpl object here - is this the right stage?
320        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        // At this stage we have a tag and a string which may be a command
329        // Start with commands that are valid in any state
330        // CAPABILITY, NOOP, LOGOUT
331        
332        // Commands only valid in NON_AUTHENTICATED state
333        // AUTHENTICATE, LOGIN
334
335
336        // Commands valid in both Authenticated and Selected states
337        // NAMESPACE, GETACL, SETACL, DELETEACL, LISTRIGHTS, MYRIGHTS, SELECT
338        
339        // Commands valid only in Authenticated State
340        // None
341
342        // Commands valid only in Selected state
343        // CHECK CLOSE COPY EXPUNGE FETCH STORE UID
344        
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        // todo
402        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//    public ACLMailbox getBox( String user, String mailboxName ) throws MailboxException, AccessControlException
413//    {
414//        return
415//        ACLMailbox tempMailbox = null;
416//        try {
417//            tempMailbox = getImapHost().getMailbox( user, mailboxName );
418//        }
419//        catch ( MailboxException me ) {
420//            if ( me.isRemote() ) {
421//                _session.getOut().println( tag + SP + NO + SP + "[REFERRAL " + me.getRemoteServer() + "]" + SP + "Remote mailbox" );
422//            }
423//            else {
424//                _session.noResponse(
425//                _session.getOut().println( tag + SP + NO + SP + "Unknown mailbox" );
426//                getLogger().info( "MailboxException in method getBox for user: "
427//                                  + user + " mailboxName: " + mailboxName + " was "
428//                                  + me.getMessage() );
429//            }
430//
431//        }
432//        catch ( AccessControlException e ) {
433//            _session.getOut().println( tag + SP + NO + SP + "Unknown mailbox" );
434//        }
435//        return tempMailbox;
436//    }
437
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        //newList = null;
499        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}