1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
14 #include "eventloop.h"
15 #include "transaction.h"
16 #include "imapsession.h"
17 #include "configuration.h"
18 #include "handlers/capability.h"
19 #include "mailboxgroup.h"
20 #include "imapparser.h"
31 static bool endsWithLiteral( const EString *, uint *, bool * );
39 : state( IMAP::NotAuthenticated ), reader( 0 ),
40 prefersAbsoluteMailboxes( false ),
41 runningCommands( false ), runCommandsAgain( false ),
42 readingLiteral( false ),
43 literalSize( 0 ), mailbox( 0 ),
45 eventMap( new EventMap ),
50 while ( i < IMAP::NumClientCapabilities )
51 clientCapabilities[i++] = false;
53 while ( i < IMAP::NumClientBugs )
54 clientBugs[i++] = false;
55 EventFilterSpec * normal = new EventFilterSpec;
56 normal->setNotificationWanted( EventFilterSpec::FlagChange, true );
57 normal->setNotificationWanted( EventFilterSpec::NewMessage, true );
58 normal->setNotificationWanted( EventFilterSpec::Expunge, true );
59 eventMap->add( normal );
68 bool prefersAbsoluteMailboxes;
70 bool runCommandsAgain;
74 List<Command> commands;
75 List<ImapResponse> responses;
81 bool clientCapabilities[IMAP::NumClientCapabilities];
82 bool clientBugs[IMAP::NumClientBugs];
84 List<MailboxGroup> possibleGroups;
94 BadBouncer( IMAP * owner ) : i( owner ) {}
96 void execute() { i->unblockCommands(); }
102 : public EventHandler
105 NatDefeater( IMAP * owner ) : i( owner ) {}
107 void execute() { i->defeatNat(); }
116 /*! \class IMAP imap.h
117 This class implements the IMAP server as seen by clients.
119 This class is responsible for interacting with IMAP clients, and for
120 overseeing the operation of individual command handlers. It looks at
121 client input to decide which Command to defer the real work to, and
122 ensures that the handler is called at the appropriate times.
124 Each IMAP object has a state() (RFC 3501 section 3), and may possess
125 other state information, such as the user() logged in or a
126 session(). The Idle state (RFC 2177) is also kept here.
128 The IMAP class parses incoming commands as soon as possible and
129 may keep several commands executing at a time, if the client
130 issues that. It depends on Command::group() to decide whether each
131 parsed Command can be executed concurrently with the already
132 running Command objects.
135 /*! This setup function expects to be called from ::main().
137 It reads and validates any relevant configuration variables, and
138 logs a disaster if it encounters an error.
146 /*! Creates an IMAP server on file descriptor \a s, and sends an
147 initial OK[CAPABILITY...] response to the client.
151 : SaslConnection( s, Connection::ImapServer ), d( new IMAPData )
156 EString banner = "* OK [CAPABILITY " +
157 Capability::capabilities( this ) + "] " +
158 Configuration::hostname() +
159 " Archiveopteryx IMAP Server";
160 if ( !Configuration::toggle( Configuration::Security ) )
161 banner.append( " (security checking disabled)" );
162 banner.append( "\r\n" );
164 setTimeoutAfter( 120 );
165 EventLoop::global()->addConnection( this );
169 /*! Handles the incoming event \a e as appropriate for its type. */
171 void IMAP::react( Event e )
173 d->bytesArrived += readBuffer()->size();
177 if ( d->bytesArrived > 32768 && state() == NotAuthenticated ) {
178 log( ">32k received before login" );
179 enqueue( "* BYE overlong login sequence\r\n" );
180 Connection::setState( Closing );
182 Scope s( d->reader->log() );
189 if ( state() != Logout ) {
190 log( "Idle timeout" );
191 enqueue( "* BYE Tempus fugit\r\n" );
193 Connection::setState( Closing );
195 Scope s( d->reader->log() );
207 log( "Unexpected close by client" );
210 if ( !d->commands.isEmpty() ) {
211 List<Command>::Iterator i( d->commands );
215 if ( c->state() == Command::Unparsed ||
216 c->state() == Command::Blocked ||
217 c->state() == Command::Executing )
218 c->error( Command::No,
219 "Unexpected close by client" );
225 enqueue( "* BYE server shutdown\r\n" );
226 if ( session() && d->commands.isEmpty() )
233 d->bytesArrived -= readBuffer()->size();
235 if ( timeout() == 0 ||
236 ( e == Read && state() != NotAuthenticated ) ) {
238 case NotAuthenticated:
239 setTimeoutAfter( 120 );
244 setTimeoutAfter( 3600 ); // one hour while IDLE
246 setTimeoutAfter( 1860 ); // a half-hour without
256 /*! Reads input from the client, and feeds it to the appropriate Command
263 Buffer * r = readBuffer();
266 // We read a line of client input, possibly including literals,
267 // and create a Command to deal with it.
268 if ( !d->readingLiteral && !d->reader ) {
273 // Do we have a complete line yet?
280 if ( endsWithLiteral( s, &n, &plus ) ) {
281 d->str.append( "\r\n" );
282 d->readingLiteral = true;
286 enqueue( "+ reading literal\r\n" );
289 // Have we finished reading the entire command?
290 if ( !d->readingLiteral ) {
295 else if ( d->readingLiteral ) {
296 // Have we finished reading a complete literal?
297 if ( r->size() < d->literalSize )
300 d->str.append( r->string( d->literalSize ) );
301 r->remove( d->literalSize );
302 d->readingLiteral = false;
304 else if ( d->reader ) {
305 // If a Command has reserve()d input, we just feed it.
306 Scope s( d->reader->log() );
315 /*! This function parses enough of the command line to create a Command,
316 and then uses it to parse the rest of the input.
319 void IMAP::addCommand()
321 // I love this feature
322 if ( d->str == "quit" )
323 d->str = "arnt logout";
325 ImapParser * p = new ImapParser( d->str );
327 EString tag = p->tag();
329 enqueue( "* BAD " + p->error() + "\r\n" );
331 log( p->error(), Log::Info );
337 EString name = p->command();
339 enqueue( "* BAD " + p->error() + "\r\n" );
341 log( p->error(), Log::Error );
345 if ( EventLoop::global()->inShutdown() && name != "logout" ) {
347 List< Command >::Iterator i( d->commands );
349 if ( i->state() == Command::Executing )
355 enqueue( "* BYE Server or process shutdown\r\n" );
356 Connection::setState( Closing );
359 enqueue( tag + " NO May not be started during server shutdown\r\n" );
363 Command * cmd = Command::create( this, tag, name, p );
366 if ( Command::create( this, tag, tag, p ) )
367 enqueue( "* OK Hint: An IMAP command is prefixed by a tag. "
368 "The command is the\r\n"
369 "* OK second word on the line, after the tag. In "
370 "your command, " + name.quoted() + "\r\n"
371 "* OK is the command and " + tag.quoted() +
372 " is the tag.\r\n" );
374 enqueue( tag + " BAD No such command: " + name + "\r\n" );
375 log( "Unknown command. Line: " + p->firstLine().quoted(),
380 d->commands.append( cmd );
381 d->nextOkTime = time( 0 ) + 117;
383 Scope x( cmd->log() );
384 if ( name.lower() != "login" && name.lower() != "authenticate" )
385 ::log( "First line: " + p->firstLine(), Log::Debug );
389 /*! Returns the current state of this IMAP session, which is one of
390 NotAuthenticated, Authenticated, Selected and Logout.
393 IMAP::State IMAP::state() const
399 /*! Sets this IMAP connection to be in state \a s. The initial value
403 void IMAP::setState( State s )
410 case NotAuthenticated:
411 name = "not authenticated";
414 name = "authenticated";
423 log( "Changed to " + name + " state", Log::Debug );
427 /*! Returns true if the server has no particular work to do to server
428 the peer(), and false if it's currently working on behalf of peer().
430 If there are no commands, a connection is idle(). If the command
431 currently being executed is Idle, the connection is also idle.
434 bool IMAP::idle() const
436 List<Command>::Iterator i( d->commands );
440 switch ( c->state() ) {
441 case Command::Unparsed:
444 case Command::Blocked:
447 case Command::Executing:
448 if ( c->name() != "idle" )
451 case Command::Finished:
454 case Command::Retired:
463 /*! Notifies the IMAP object that \a user was successfully
464 authenticated by way of \a mechanism. This changes the state() of
465 the IMAP object to Authenticated.
468 void IMAP::setUser( User * user, const EString & mechanism )
470 log( "Authenticated as " + user->login().ascii() + " using " +
471 mechanism, Log::Significant );
472 SaslConnection::setUser( user, mechanism );
473 setState( Authenticated );
475 bool possiblyOutlook = true;
476 List< Command >::Iterator i( d->commands );
477 while ( i && possiblyOutlook ) {
478 EString tag = i->tag();
480 if ( tag.length() != 4 || tag.contains( '.' ) )
481 possiblyOutlook = false;
483 if ( possiblyOutlook )
485 setTimeoutAfter( 1860 );
489 /*! Reserves input from the connection for \a command.
491 When more input is available, Command::read() is
492 called. Command::finish() releases control.
495 void IMAP::reserve( Command * command )
501 /*! Causes any blocked commands to be executed if possible.
504 void IMAP::unblockCommands()
506 if ( d->state != NotAuthenticated )
507 while ( d->commands.firstElement() &&
508 d->commands.firstElement()->state() == Command::Retired )
510 if ( d->runningCommands )
511 d->runCommandsAgain = true;
517 /*! Calls Command::execute() on all currently operating commands, and
518 if possible calls Command::emitResponses() and retires those which
522 void IMAP::runCommands()
524 d->runningCommands = true;
525 d->runCommandsAgain = true;
527 while ( d->runCommandsAgain ) {
528 d->runCommandsAgain = false;
529 log( "IMAP::runCommands, " + fn( d->commands.count() ) + " commands",
532 // run all currently executing commands once
534 List< Command >::Iterator i( d->commands );
539 if ( c->state() == Command::Executing ) {
548 // emit responses for zero or more finished commands and
551 i = d->commands.first();
552 while ( i && i->state() == Command::Finished ) {
555 if ( d->reader == c )
561 // slow down the command rate if the client is sending
562 // errors. specificaly, if we've sent a NO/BAD, then we don't
563 // start any new commands for n seconds, where n is the number
564 // of NO/BADs we've sent, bounded at 16.
566 int delayNeeded = (int)syntaxErrors();
567 if ( delayNeeded > 16 )
569 delayNeeded = (int)d->lastBadTime + delayNeeded - (int)::time(0);
570 if ( delayNeeded < 0 )
572 if ( user() && !user()->inbox() && delayNeeded < 4 )
574 if ( delayNeeded > 0 && !d->commands.isEmpty() ) {
575 log( "Delaying next IMAP command for " + fn( delayNeeded ) +
576 " seconds (because of " + fn( syntaxErrors() ) +
578 (void)new Timer( new IMAPData::BadBouncer( this ), delayNeeded );
579 d->runningCommands = false;
583 // we may be able to start new commands.
584 i = d->commands.first();
586 if ( first && first->state() != Command::Retired ) {
587 Scope x( first->log() );
589 if ( first->state() == Command::Unparsed )
592 first->setState( Command::Finished );
593 else if ( first->state() == Command::Unparsed ||
594 first->state() == Command::Blocked )
595 first->setState( Command::Executing );
596 if ( first->state() != Command::Executing )
600 // if we have a leading command, we can parse and execute
601 // followers in the same group.
602 if ( first && first->group() ) {
603 while ( first && i && first->state() == i->state() ) {
607 if ( c->state() == Command::Unparsed )
610 c->setState( Command::Finished );
611 else if ( c->state() == Command::Unparsed ||
612 c->state() == Command::Blocked )
613 c->setState( Command::Executing );
614 if ( c->group() != first->group() &&
615 c->state() == Command::Executing ) {
617 c->setState( Command::Blocked );
623 d->runningCommands = false;
625 List< Command >::Iterator i( d->commands );
627 if ( i->state() == Command::Retired )
628 d->commands.take( i );
632 if ( d->commands.isEmpty() ) {
633 if ( EventLoop::global()->inShutdown() &&
634 Connection::state() == Connected )
635 Connection::setState( Closing );
637 restartNatDefeater();
642 /*! Executes \a c once, provided it's in the right state, and emits its
646 void IMAP::run( Command * c )
648 if ( c->state() != Command::Executing )
660 /* This static helper function returns true if \a s ends with an IMAP
661 literal specification. If so, it sets \a *n to the number of bytes
662 in the literal, and \a *plus to true if the number had a trailing
663 '+' (for LITERAL+). Returns false if it couldn't find a literal.
666 static bool endsWithLiteral( const EString *s, uint *n, bool *plus )
668 if ( !s->endsWith( "}" ) )
671 uint i = s->length() - 2;
672 if ( (*s)[i] == '+' ) {
678 while ( i > 0 && (*s)[i] >= '0' && (*s)[i] <= '9' )
681 if ( (*s)[i] != '{' )
685 *n = s->mid( i+1, j-i ).number( &ok );
691 /*! Switches to Selected state and operates on the mailbox session \a
692 s. If the object already had a session, ends the previous session.
695 void IMAP::setSession( Session * s )
697 if ( !s && !session() )
701 (void)new ImapResponse( this, "OK [CLOSED] Ita, missa est" );
703 Connection::setSession( s );
705 setState( Selected );
706 log( "Starting session on mailbox " + s->mailbox()->name().ascii() );
709 setState( Authenticated );
714 /*! \class IMAPS imap.h
716 The IMAPS class implements the old wrapper trick still commonly
717 used on port 993. As befits a hack, it is a bit of a hack, and
718 depends on the ability to empty its writeBuffer().
721 /*! Constructs an IMAPS server on file descriptor \a s, and starts to
722 negotiate TLS immediately.
725 IMAPS::IMAPS( int s )
728 EString * tmp = writeBuffer()->removeLine();
730 enqueue( *tmp + "\r\n" );
734 /*! Returns true if the client has shown that it supports a given \a
735 capability, and false if this is still unknown.
738 bool IMAP::clientSupports( ClientCapability capability ) const
740 return d->clientCapabilities[capability];
744 /*! Records that the client supports \a capability. The initial value
745 is valse for all capabilities, and there is no way to disable a
746 capability once enabled.
749 void IMAP::setClientSupports( ClientCapability capability )
751 d->clientCapabilities[capability] = true;
752 if ( capability == QResync )
753 d->clientCapabilities[Condstore] = true;
757 /*! Returns true if the server thinks the client may have \a bug, and
761 bool IMAP::clientHasBug( ClientBug bug ) const
763 return d->clientBugs[bug];
767 static const char * clientBugMessages[IMAP::NumClientBugs] = {
768 "Mishandling of unsolicited responses",
772 /*! Records that the client is presumed to suffer from \a bug. */
774 void IMAP::setClientBug( ClientBug bug )
776 d->clientBugs[bug] = true;
777 (void)new ImapResponse( this,
778 EString( "OK Activating workaround for: " ) +
779 clientBugMessages[bug] );
783 /*! Returns a list of all Command objects currently known by this IMAP
784 server. First received command first. Commands in all states may
785 be in the list, except Retired.
789 List<Command> * IMAP::commands() const
791 while ( d->commands.firstElement() &&
792 d->commands.firstElement()->state() == Command::Retired )
798 void IMAP::sendChallenge( const EString &s )
800 enqueue( "+ "+ s +"\r\n" );
804 /*! Records that the IMAP client likes to see its mailbox names in
805 absolute form (ie. /users/kiki/lists/mja instead of lists/mja)
806 if \a b is true, and that it prefers relative names otherwise.
807 The initial value is false.
810 void IMAP::setPrefersAbsoluteMailboxes( bool b )
812 d->prefersAbsoluteMailboxes = b;
816 /*! Returns whatever setPrefersAbsoluteMailboxes() set. */
818 bool IMAP::prefersAbsoluteMailboxes() const
820 return d->prefersAbsoluteMailboxes;
824 /*! Records that \a response needs to be sent at the earliest possible
825 date. When is the earliest possible date? Well, it depends on \a
826 response, on the commands active and so on.
829 void IMAP::respond( class ImapResponse * response )
831 d->responses.append( response );
835 /*! Emits those responses which can be emitted at this time. */
837 void IMAP::emitResponses()
839 if ( clientHasBug( NoUnsolicitedResponses ) && commands()->isEmpty() )
842 // first, see if expunges are permitted
845 List<Command>::Iterator c( commands() );
847 while ( c && !cannot ) {
848 // expunges are permitted in idle mode
849 if ( c->state() == Command::Executing && c->name() == "idle" )
851 // we cannot send an expunge while a command is being
852 // executed (not without NOTIFY at least...)
853 else if ( c->state() == Command::Executing )
855 // group 2 contains commands during which we may not send
856 // expunge, group 3 contains all commands that change
858 else if ( c->group() == 2 || c->group() == 3 )
860 // if there are MSNs in the pipeline we cannot send
861 // expunge. the copy rule is due to RFC 2180 section
863 else if ( c->usesMsn() && c->name() != "copy" )
865 // if another command is finished, we can.
866 else if ( c->state() == Command::Finished && !c->tag().isEmpty() )
875 Buffer * w = writeBuffer();
876 List<ImapResponse>::Iterator r( d->responses );
879 if ( !r->meaningful() ) {
882 else if ( !r->sent() && ( can || !r->changesMsn() ) ) {
883 EString t = r->text();
884 if ( !t.isEmpty() ) {
885 w->append( "* ", 2 );
887 w->append( "\r\n", 2 );
894 d->responses.take( r );
902 c = commands()->first();
904 c->checkUntaggedResponses();
910 /*! Records that \a m is a (possibly) active mailbox group. */
912 void IMAP::addMailboxGroup( MailboxGroup * m )
914 d->possibleGroups.append( m );
918 /*! Records that \a m is no longer active. MailboxGroup calls this,
922 void IMAP::removeMailboxGroup( MailboxGroup * m )
924 d->possibleGroups.remove( m );
928 /*! Returns the MailboxGroup most likely to be the one the client is
929 working on, assuming that the client performs an operation on \a
932 Returns a null pointer if the client doesn't seem to be working on
933 any easily defined group, or if it is working on one, but
934 MailboxGroup::hits() returns a value less than \a l.
937 MailboxGroup * IMAP::mostLikelyGroup( Mailbox * m, uint l )
939 List<MailboxGroup>::Iterator i( d->possibleGroups );
940 MailboxGroup * best = 0;
943 MailboxGroup * g = i;
945 if ( g->contains( m ) && g->hits() >= l ) {
946 uint count = g->count();
947 if ( !best || bestCount < count ) {
957 /*! Returns a pointer to the event map currently in force. This is
958 never a null pointer; IMAP sets up a suitable map when it starts.
961 class EventMap * IMAP::eventMap() const
967 /*! Records that IMAP should base its notification decisions on \a map
968 henceforth. \a map must not be null.
972 void IMAP::setEventMap( class EventMap * map )
979 /*! Reimplemented in order to record the time, so we can rate-limit
980 bad IMAP commands in runCommands();
983 void IMAP::recordSyntaxError()
985 SaslConnection::recordSyntaxError();
986 d->lastBadTime = time( 0 );
990 /*! Restarts the timing logic we use to send little OK response in
991 order to defeat too-quick NAT timeouts.
994 void IMAP::restartNatDefeater()
996 if ( !clientHasBug( Nat ) )
999 if ( state() == NotAuthenticated || state() == Logout )
1002 uint now = time( 0 );
1003 uint next = now + 4;
1004 // if we've already set up a suitable timer, just quit
1005 if ( d->nextOkTime >= next && d->nextOkTime < now + 6 )
1007 // otherwise, set one up
1008 d->nextOkTime = next;
1009 (void)new Timer( new IMAPData::NatDefeater( this ), 6 );
1013 /*! Called regularly to ensure that we send an untagged OK every
1014 minute or so, in order to ensure a steady stream of packets. Some
1015 NAT gateways will kill the connection after as little as two
1016 minutes if no traffic is seen.
1019 void IMAP::defeatNat()
1023 if ( Connection::state() != Connection::Connected )
1025 if ( state() == NotAuthenticated || state() == Logout )
1028 uint now = time( 0 );
1029 if ( now < d->nextOkTime )
1032 d->nextOkTime = now + 117;
1033 (void)new Timer( new IMAPData::NatDefeater( this ), d->nextOkTime - now );
1035 x.setUnixTime( now );
1036 enqueue( "* OK (NAT keepalive: " + x.isoTime() + ")\r\n" );