1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
2 
3 #include "command.h"
4 
5 #include "log.h"
6 #include "utf.h"
7 #include "imap.h"
8 #include "user.h"
9 #include "buffer.h"
10 #include "mailbox.h"
11 #include "integerset.h"
12 #include "imapparser.h"
13 #include "transaction.h"
14 #include "imapsession.h"
15 #include "mailboxgroup.h"
16 
17 // Keep these alphabetical.
18 #include "handlers/acl.h"
19 #include "handlers/append.h"
20 #include "handlers/authenticate.h"
21 #include "handlers/capability.h"
22 #include "handlers/close.h"
23 #include "handlers/compress.h"
24 #include "handlers/copy.h"
25 #include "handlers/create.h"
26 #include "handlers/delete.h"
27 #include "handlers/enable.h"
28 #include "handlers/expunge.h"
29 #include "handlers/fetch.h"
30 #include "handlers/genurlauth.h"
31 #include "handlers/id.h"
32 #include "handlers/idle.h"
33 #include "handlers/listext.h"
34 #include "handlers/login.h"
35 #include "handlers/logout.h"
36 #include "handlers/lsub.h"
37 #include "handlers/move.h"
38 #include "handlers/namespace.h"
39 #include "handlers/notify.h"
40 #include "handlers/noop.h"
41 #include "handlers/quota.h"
42 #include "handlers/rename.h"
43 #include "handlers/resetkey.h"
44 #include "handlers/search.h"
45 #include "handlers/select.h"
46 #include "handlers/sort.h"
47 #include "handlers/starttls.h"
48 #include "handlers/status.h"
49 #include "handlers/store.h"
50 #include "handlers/subscribe.h"
51 #include "handlers/thread.h"
52 #include "handlers/unselect.h"
53 #include "handlers/urlfetch.h"
54 
55 #include <sys/time.h> // gettimeofday, struct timeval
56 
57 
58 class MailboxGroup;
59 
60 
61 class CommandData
62     : public Garbage
63 {
64 public:
CommandData()65     CommandData()
66         : args( 0 ),
67           tagged( false ),
68           usesRelativeMailbox( false ),
69           usesAbsoluteMailbox( false ),
70           usesMsn( false ),
71           error( false ),
72           emittingResponses( false ),
73           state( Command::Unparsed ), group( 0 ),
74           permittedStates( 0 ),
75           imap( 0 ), session( 0 ), checker( 0 ),
76           mailbox( 0 ), mailboxGroup( 0 ),
77           checkedMailboxGroup( false ),
78           transaction( 0 )
79     {
80         (void)::gettimeofday( &started, 0 );
81     }
82 
83     EString tag;
84     EString name;
85     ImapParser * args;
86 
87     List<ImapResponse> untagged;
88 
89     EString respTextCode;
90     bool tagged;
91 
92     bool usesRelativeMailbox;
93     bool usesAbsoluteMailbox;
94     bool usesMsn;
95     bool error;
96     bool emittingResponses;
97     Command::State state;
98     uint group;
99     Command::Error errorCode;
100     EString errorText;
101 
102     uint permittedStates;
103 
104     struct timeval started;
105 
106     IMAP * imap;
107     ImapSession * session;
108     PermissionsChecker * checker;
109 
110     Mailbox * mailbox;
111     MailboxGroup * mailboxGroup;
112     bool checkedMailboxGroup;
113 
114     Transaction * transaction;
115 };
116 
117 
118 /*! \class Command command.h
119     The Command class represents a single IMAP command.
120 
121     Subclasses implement each command (e.g. Noop implements Noop), this
122     class provides the overall framework.
123 
124     It contains convenience functions to parse the various arguments,
125     such as atom(), astring(), set and so on, as well as utility
126     functions for the Command subclasses and, naturally, some functions
127     that are tightly bound with the Commands, viz:
128 
129     setGroup() and group() provide the IMAP class with information about
130     which Commands can be executed concurrently.
131 
132     setState() and state() decribe a command's state, which is either
133     Blocked (waiting until IMAP permits executing this command),
134     Executing (Command subclass working), Finished (done, but no
135     response sent) or Retired (done, responses sent).
136 
137     respond(), emitResponses(), error() and ok() all help sending
138     responses to the IMAP client. respond() is mostly used for
139     untagged responses, error() for tagged NO/BAD responses. If
140     neither respond() nor error() is called, a tagged OK is sent by
141     default when emitResponses() is called at the end of
142     processing. Finally, ok() returns false if anything has happened
143     to warrant NO/BAD, and true if everything is still OK.
144 */
145 
146 
147 /*! Constructs a simple Command, entirely empty. */
148 
Command()149 Command::Command()
150     : d( new CommandData )
151 {
152 }
153 
154 
155 /*!  Constructs a simple Command and ties it to \a i. create() doesn't
156      need this, but maybe, just maybe, there is a world beyond create().
157 */
158 
Command(IMAP * i)159 Command::Command( IMAP * i )
160     : d( new CommandData )
161 {
162     d->imap = i;
163 }
164 
165 /*! Destroys the object and frees any allocated resources. */
166 
~Command()167 Command::~Command()
168 {
169 }
170 
171 
172 /*! This static function creates an instance of the right subclass of
173     Command, depending on \a name and the state of \a imap.
174 
175     \a args is a pointer to the ImapParser object for the command; it is
176     expected to point to the first character after the command's \a tag
177     and \a name, so that it may be used to parse any arguments. Command
178     assumes ownership of \a args, which must be non-zero.
179 
180     If \a name is not a valid command, create() return a null pointer.
181 */
182 
create(IMAP * imap,const EString & tag,const EString & name,ImapParser * args)183 Command * Command::create( IMAP * imap,
184                            const EString & tag,
185                            const EString & name,
186                            ImapParser * args )
187 {
188     Command * c = 0;
189     EString n = name.lower();
190     bool uid = false;
191     if ( n.startsWith( "uid " ) ) {
192         uid = true;
193         n = n.mid( 4 );
194     }
195 
196     bool notAuthenticated = false;
197     bool authenticated = false;
198     bool selected = false;
199     bool logout = false;
200 
201     // Create an appropriate Command handler.
202     if ( n == "login" )
203         c = new Login;
204     else if ( n == "authenticate" )
205         c = new Authenticate;
206     else if ( n == "starttls" )
207         c = new StartTLS;
208 
209     if ( c )
210         notAuthenticated = true;
211 
212     if ( !c ) {
213         if ( n == "select" )
214             c = new Select;
215         else if ( n == "examine" )
216             c = new Examine;
217         else if ( n == "create" )
218             c = new Create;
219         else if ( n == "delete" )
220             c = new Delete;
221         else if ( n == "list" )
222             c = new Listext;
223         else if ( n == "lsub" )
224             c = new Lsub;
225         else if ( n == "namespace" )
226             c = new Namespace;
227         else if ( n == "status" )
228             c = new Status;
229         else if ( n == "rename" )
230             c = new Rename;
231         else if ( n == "subscribe" )
232             c = new Subscribe;
233         else if ( n == "unsubscribe" )
234             c = new Unsubscribe;
235         else if ( n == "append" )
236             c = new Append;
237         else if ( n == "setacl" )
238             c = new Acl( Acl::SetAcl );
239         else if ( n == "deleteacl" )
240             c = new Acl( Acl::DeleteAcl );
241         else if ( n == "getacl" )
242             c = new Acl( Acl::GetAcl );
243         else if ( n == "listrights" )
244             c = new Acl( Acl::ListRights );
245         else if ( n == "myrights" )
246             c = new Acl( Acl::MyRights );
247         else if ( n == "resetkey" )
248             c = new ResetKey;
249         else if ( n == "genurlauth" )
250             c = new GenUrlauth;
251         else if ( n == "urlfetch" )
252             c = new UrlFetch;
253         else if ( n == "notify" )
254             c = new Notify;
255         else if ( n == "compress" )
256             c = new Compress;
257         else if ( n == "getquota" )
258             c = new GetQuota();
259         else if ( n == "setquota" )
260             c = new SetQuota();
261         else if ( n == "getquotaroot" )
262             c = new GetQuotaRoot();
263         else if ( n == "setquotaroot" )
264             c = new SetQuotaRoot();
265 
266         if ( c ) {
267             authenticated = true;
268             selected = true;
269         }
270     }
271 
272     if ( !c ) {
273         if ( n == "fetch" )
274             c = new Fetch( uid );
275         else if ( n == "search" )
276             c = new Search( uid );
277         else if ( n == "expunge" )
278             c = new Expunge( uid );
279         else if ( n == "check" )
280             c = new Check;
281         else if ( n == "close" )
282             c = new Close;
283         else if ( n == "store" )
284             c = new Store( uid );
285         else if ( n == "copy" )
286             c = new Copy( uid );
287         else if ( n == "thread" )
288             c = new Thread( uid );
289         else if ( n == "unselect" )
290             c = new Unselect;
291         else if ( n == "sort" )
292             c = new Sort( uid );
293         else if ( n == "move" )
294             c = new Move( uid );
295 
296         if ( c )
297             selected = true;
298     }
299 
300     if ( !c ) {
301         if ( n == "noop" )
302             c = new Noop;
303         else if ( n == "capability" )
304             c = new Capability;
305         else if ( n == "logout" )
306             c = new Logout;
307         else if ( n == "idle" )
308             c = new Idle;
309         else if ( n == "id" )
310             c = new Id;
311         else if ( n == "enable" )
312             c = new Enable;
313 
314         if ( c ) {
315             notAuthenticated = true;
316             authenticated = true;
317             selected = true;
318             logout = true;
319         }
320     }
321 
322     if ( !c )
323         return 0;
324 
325     c->d->tag = tag;
326     c->d->name = name.lower();
327     c->setParser( args );
328     c->d->imap = imap;
329 
330     if ( notAuthenticated )
331         c->d->permittedStates |= ( 1 << IMAP::NotAuthenticated );
332     if ( authenticated )
333         c->d->permittedStates |= ( 1 << IMAP::Authenticated );
334     if ( selected )
335         c->d->permittedStates |= ( 1 << IMAP::Selected );
336     if ( logout )
337         c->d->permittedStates |= ( 1 << IMAP::Logout );
338 
339     c->setLog( new Log );
340     c->log( "IMAP Command: " + tag + " " + name );
341 
342     return c;
343 }
344 
345 
346 /*! This virtual function is responsible for parsing the entire
347     command. It may not return any value; instead, it may set an error
348     by calling error(). It may also not do any database lookups or
349     other "slow" work.
350 
351     If this function (or a reimplementation) is called and does not
352     call error() or set the command's state, IMAP changes the state to
353     Executing afterwards.
354 
355     The default implementation is suitable for argumentless commands
356     such as logout, capability and starttls.
357 */
358 
parse()359 void Command::parse()
360 {
361     end();
362 }
363 
364 
365 /*! \fn void Command::execute()
366 
367     This virtual function is responsible for executing the command, as
368     appopriate, and setting state() to Finished when it is. It is
369     called by IMAP after parse() finishes, and only if parse()
370     succeeds.
371 
372     If state() is still Executing after a call to execute(), IMAP will
373     call it again later.
374 */
375 
376 
377 /*! This virtual function is responsible for reading from the IMAP
378     stream and eventually releasing a reservation. Most subclasses
379     will not need to implement this; only those that call
380     IMAP::reserve() to the IMAP input stream do.
381 */
382 
read()383 void Command::read()
384 {
385     imap()->reserve( 0 );
386 }
387 
388 
389 /*! Returns true if there haven't been any errors so far during
390     parsing or execution of this command.
391 
392     Calling error() makes this function return false.
393 */
394 
ok() const395 bool Command::ok() const
396 {
397     if ( d->error )
398         return false;
399     return true;
400 }
401 
402 
403 /*! Instructs this command to parse itself using \a p. Most commands
404     expect that nextChar() will return the space after the command
405     name, before the first argument.
406 */
407 
setParser(ImapParser * p)408 void Command::setParser( ImapParser * p )
409 {
410     d->args = p;
411 }
412 
413 
414 /*! Returns a pointer to the ImapParser object that was set by
415     setParser() (usually by create()).
416 */
417 
parser() const418 ImapParser * Command::parser() const
419 {
420     return d->args;
421 }
422 
423 
424 /*! Returns the state of this command, which may be Blocked, Executing
425     or Finished. See setState().
426 */
427 
state() const428 Command::State Command::state() const
429 {
430     return d->state;
431 }
432 
433 
434 /*! Sets the state of this command to \a s. The state is always one of
435     three values, Blocked, Executing and Finished. The initial value is
436     Executing. execute() must set it to Finished when done.
437 
438     The Blocked state means that execute() cannot be called until all
439     currently executing commands have finished. parse() and read() both
440     may be called.
441 
442     The Executing state means that execute() should be called (again).
443 
444     The Finished state means that the command is done. IMAP rechecks the
445     state after calling execute.
446 */
447 
setState(State s)448 void Command::setState( State s )
449 {
450     if ( d->state == s )
451         return;
452 
453     d->state = s;
454     switch( s ) {
455     case Retired:
456         log( "Retired", Log::Debug );
457         break;
458     case Unparsed:
459         // this is the initial state, it should never be called.
460         break;
461     case Blocked:
462         log( "Deferring execution", Log::Debug );
463         break;
464     case Executing:
465         (void)::gettimeofday( &d->started, 0 );
466         if ( d->permittedStates & ( 1 << imap()->state() ) ) {
467             log( "Executing", Log::Debug );
468             d->session = (ImapSession*)(imap()->session());
469             if ( d->session )
470                 d->session->emitUpdates( 0 );
471         }
472         else {
473             error( Bad, "" );
474         }
475         break;
476     case Finished:
477         if ( d->name != "idle" ) {
478             struct timeval end;
479             (void)::gettimeofday( &end, 0 );
480             long elapsed =
481                 ( end.tv_sec - d->started.tv_sec ) * 1000000 +
482                 ( end.tv_usec - d->started.tv_usec );
483             Log::Severity level = Log::Debug;
484             if ( elapsed > 3000 )
485                 level = Log::Info;
486             EString m;
487             m.append( "Execution time " );
488             m.append( fn( ( elapsed + 499 ) / 1000 ) );
489             m.append( "ms" );
490             log( m, level );
491         }
492         log( "Finished", Log::Debug );
493         break;
494     }
495     imap()->unblockCommands();
496 }
497 
498 
499 /*! Returns the tag of this command. Useful for logging. */
500 
tag() const501 EString Command::tag() const
502 {
503     return d->tag;
504 }
505 
506 
507 /*! Returns the name of this command, e.g. 'uid fetch', in lower
508     case. */
509 
name() const510 EString Command::name() const
511 {
512     return d->name;
513 }
514 
515 
516 /*! Returns true if this command has parsed at least one MSN, and
517     false if it has not (ie. it returns false before parse()).
518 */
519 
usesMsn() const520 bool Command::usesMsn() const
521 {
522     return d->usesMsn;
523 }
524 
525 
526 /*! Returns the command group of this Command. Commands in group 0 may
527     only be executed singly, commands in other groups may be executed
528     concurrently with other commands in the same group.
529 
530     The initial value is 0. setGroup() defines the available groups.
531 */
532 
group() const533 uint Command::group() const
534 {
535     return d->group;
536 }
537 
538 
539 /*! Sets this command to belong to group \a g. If \a g is 0, the
540     command must be executed singly. If \a g is nonzero, IMAP may try to
541     execute this command concurrently with any other commands whose
542     group is \a g.
543 
544     The groups are (subject to later change):
545 
546     0) Most commands. All commands which change state() or expunge
547        messages must be here.
548 
549     1) UID SEARCH and UID FETCH.
550 
551     2) FETCH and SEARCH.
552 
553     3) STORE and UID STORE. (Note that for this group to work, the
554        server cannot emit side-effect expunges during UID STORE
555        processing.) This group exists because a fetch after a store
556        could otherwise fetch old data.
557 
558     4) STATUS, LIST. Perhaps other read-only commands that look at
559        mailboxes.
560 
561     The initial value is 0.
562 */
563 
setGroup(uint g)564 void Command::setGroup( uint g )
565 {
566     d->group = g;
567 }
568 
569 
570 /*! Returns a pointer to the IMAP session to which this command
571     belongs.
572 */
573 
imap() const574 IMAP * Command::imap() const
575 {
576     return d->imap;
577 }
578 
579 
580 /*! Adds \a r to the list of strings to be sent to the client. Neither
581     the leading star-space nor the trailing CRLF should be included in
582     \a r.
583 */
584 
respond(const EString & r)585 void Command::respond( const EString & r )
586 {
587     waitFor( new ImapResponse( d->imap, r ) );
588 }
589 
590 
591 
592 /*! Sets the command's status code to be \a e and the attendant
593     debugging message to be \a t, provided no status code has been set
594     yet.
595 
596     Only the first call to error() has any effect, and only if it's
597     before the call to emitResponses(); subsequent calls are ignored
598     entirely.
599 
600     \a t should not be CRLF-terminated.
601 */
602 
error(Error e,const EString & t)603 void Command::error( Error e, const EString & t )
604 {
605     if ( d->error )
606         return;
607 
608     if ( !imap() )
609         return;
610 
611     if ( d->permittedStates & ( 1 << imap()->state() ) ) {
612         d->errorCode = e;
613         d->errorText = t;
614     }
615     else if ( !t.isEmpty() && imap()->state() != IMAP::NotAuthenticated ) {
616         d->errorCode = e;
617         d->errorText = t;
618     }
619     else {
620         d->errorCode = Bad;
621         switch ( imap()->state() ) {
622         case IMAP::NotAuthenticated:
623             d->errorText = "Not permitted before authentication";
624             break;
625         case IMAP::Authenticated:
626             d->errorText = "Not permitted without mailbox";
627             break;
628         case IMAP::Selected:
629             d->errorText = "Not permitted while a mailbox is selected";
630             break;
631         case IMAP::Logout:
632             d->errorText = "Not permitted during logout";
633             // in this case we give the client a NO, not a BAD, since
634             // the logout might be initiated by us.
635             d->errorCode = No;
636             break;
637         };
638     }
639     d->error = true;
640     if ( d->transaction )
641         d->transaction->rollback();
642     finish();
643 }
644 
645 
646 /*! Make sure that this command's tagged OK is not sent until \a
647     response has been sent.
648 */
649 
waitFor(class ImapResponse * response)650 void Command::waitFor( class ImapResponse * response )
651 {
652     d->untagged.append( response );
653 }
654 
655 
656 /*! Checks that everything we waitFor() has been sent. */
657 
checkUntaggedResponses()658 void Command::checkUntaggedResponses()
659 {
660     while ( !d->untagged.isEmpty() &&
661             d->untagged.firstElement()->sent() )
662         d->untagged.shift();
663 }
664 
665 
666 /*! Sets this Command's state to ::Finished and emit any queued
667     responses as soon as possible.
668 */
669 
finish()670 void Command::finish()
671 {
672     if ( state() == Retired )
673         return;
674 
675     if ( d->usesRelativeMailbox )
676         imap()->setPrefersAbsoluteMailboxes( false );
677     else if ( d->usesAbsoluteMailbox )
678         imap()->setPrefersAbsoluteMailboxes( true );
679 
680     if ( d->mailboxGroup && d->mailbox )
681         d->mailboxGroup->remove( d->mailbox );
682 
683     setState( Finished );
684 }
685 
686 
687 /*! Dumps all responses issued during the command's parsing and
688     execution to the write buffer. This may turn out to be insufficient,
689     but for the moment it guarantees that each command's untagged
690     responses and final tagged response come together.
691 */
692 
emitResponses()693 void Command::emitResponses()
694 {
695     if ( state() == Retired )
696         return;
697 
698     Session * s = imap()->session();
699     if ( s && !s->initialised() )
700         return;
701 
702     imap()->emitResponses();
703     if ( !d->untagged.isEmpty() )
704         return;
705     setState( Retired );
706 
707     // we don't have a tag if we're an implicit Fetch or Store used by
708     // ImapSession.
709     if ( d->tag.isEmpty() )
710         return;
711 
712     EString t = tag();
713     if ( !d->error ) {
714         t.append( " OK " );
715     }
716     else if ( d->errorCode == Bad ) {
717         imap()->recordSyntaxError();
718         t.append( " BAD " );
719     }
720     else {
721         t.append( " NO " );
722     }
723     if ( !d->respTextCode.isEmpty() ) {
724         t.append( "[" );
725         t.append( d->respTextCode );
726         t.append( "] " );
727     }
728     if ( d->error ) {
729         t.append( d->errorText );
730     }
731     else {
732         t.append( "done" );
733     }
734     log( t );
735     t.append( "\r\n" );
736     imap()->enqueue( t );
737 }
738 
739 
740 /*! Returns the next, unparsed character, without consuming
741     it. Returns 0 in case of error, but does not emit any error
742     messages.
743 */
744 
nextChar()745 char Command::nextChar()
746 {
747     return d->args->nextChar();
748 }
749 
750 
751 /*! Steps past \a n characters of the unparsed arguments. */
752 
step(uint n)753 void Command::step( uint n )
754 {
755     d->args->step( n );
756 }
757 
758 
759 /*! Checks whether the next characters in the input match \a s. If so,
760     present() steps past the matching characters and returns true. If
761     not, it returns false without changing the input.
762 
763     Note that the match is completely case insensitive.
764 */
765 
present(const EString & s)766 bool Command::present( const EString & s )
767 {
768     return d->args->present( s );
769 }
770 
771 
772 /*! Verifies that the next characters in the input match \a s (case
773     insensitively), and removes whatever matches. If input isn't as
774     required, require() calls error().
775 */
776 
require(const EString & s)777 void Command::require( const EString & s )
778 {
779     d->args->require( s );
780     if ( !d->args->ok() )
781         error( Bad, d->args->error() );
782 }
783 
784 
785 /*! Parses from \a min to \a max digits and returns them in string
786     form. If fewer than \a min digits are available, error() is called.
787 */
788 
digits(uint min,uint max)789 EString Command::digits( uint min, uint max )
790 {
791     EString r( d->args->digits( min, max ) );
792     if ( !d->args->ok() )
793         error( Bad, d->args->error() );
794     return r;
795 }
796 
797 
798 /*! Parses from \a min to \a max letters and returns them in string
799     form. If fewer than \a min letters are available, error() is
800     called.
801 */
802 
letters(uint min,uint max)803 EString Command::letters( uint min, uint max )
804 {
805     EString r( d->args->letters( min, max ) );
806     if ( !d->args->ok() )
807         error( Bad, d->args->error() );
808     return r;
809 }
810 
811 
812 /*! Checks that the atom "nil" is next at the parse position, and
813     steps past. */
814 
nil()815 void Command::nil()
816 {
817     d->args->nil();
818     if ( !d->args->ok() )
819         error( Bad, d->args->error() );
820 }
821 
822 
823 /*! Checks that a single space is next at the parse position, and
824     steps past it if all is ok.
825 
826     This command accepts more than one space, and gives a
827     warning. This is to tolerate broken clients, while giving client
828     authors a strong hint.
829 */
830 
space()831 void Command::space()
832 {
833     d->args->require( " " );
834     if ( d->args->nextChar() != ' ' )
835         return;
836 
837     while ( d->args->nextChar() == ' ' )
838         d->args->step();
839     (void)new ImapResponse( imap(),
840                             "BAD Illegal space seen before this text: " +
841                             following() );
842 }
843 
844 
845 /*! Parses a single number and returns it. */
846 
number()847 uint Command::number()
848 {
849     uint n = d->args->number();
850     if ( !d->args->ok() )
851         error( Bad, d->args->error() );
852     return n;
853 }
854 
855 
856 /*! Parses a single nzNumber and returns it. */
857 
nzNumber()858 uint Command::nzNumber()
859 {
860     uint n = d->args->nzNumber();
861     if ( !d->args->ok() )
862         error( Bad, d->args->error() );
863     return n;
864 }
865 
866 
867 /*! Parses an IMAP atom and returns it as a string. Calls error() and
868     turns an empty string in case of error.
869 */
870 
atom()871 EString Command::atom()
872 {
873     EString r( d->args->atom() );
874     if ( !d->args->ok() )
875         error( Bad, d->args->error() );
876     return r;
877 }
878 
879 
880 /*! Parses one or more consecutive list-chars (ATOM-CHAR/list-wildcards/
881     resp-specials) and returns them as a EString. Calls error() and
882     returns an empty string in case of error.
883 */
884 
listChars()885 EString Command::listChars()
886 {
887     EString r( d->args->atom() );
888     if ( !d->args->ok() )
889         error( Bad, d->args->error() );
890     return r;
891 }
892 
893 
894 /*! Parses an IMAP quoted string and return the relevant string. In
895     case of error an empty string is returned.
896 
897     Note that any character can be quoted. IMAP properly allows only
898     the quote character and the backslash to be quoted. In this
899     respect, we deviate from the standard.
900 */
901 
quoted()902 EString Command::quoted()
903 {
904     EString r( d->args->quoted() );
905     if ( !d->args->ok() )
906         error( Bad, d->args->error() );
907     return r;
908 }
909 
910 
911 /*! Parses an IMAP literal and returns the relevant string. Returns an
912     empty string in case of error.
913 */
914 
literal()915 EString Command::literal()
916 {
917     EString r( d->args->literal() );
918     if ( !d->args->ok() )
919         error( Bad, d->args->error() );
920     return r;
921 }
922 
923 
924 /*! Parses an IMAP string and returns it. If there is none, error() is
925     called appropriately.
926 */
927 
string()928 EString Command::string()
929 {
930     EString r( d->args->string() );
931     if ( !d->args->ok() )
932         error( Bad, d->args->error() );
933     return r;
934 }
935 
936 
937 /*! Parses an IMAP nstring and returns that string. If the nstring is
938     NIL, an empty string is returned and error() is called.
939 */
940 
nstring()941 EString Command::nstring()
942 {
943     EString r( d->args->nstring() );
944     if ( !d->args->ok() )
945         error( Bad, d->args->error() );
946     return r;
947 }
948 
949 
950 /*! Parses an IMAP astring and returns that string. In case of error,
951     astring() calls error() appropriately and returns an empty string.
952 */
953 
astring()954 EString Command::astring()
955 {
956     EString r( d->args->astring() );
957     if ( !d->args->ok() )
958         error( Bad, d->args->error() );
959     return r;
960 }
961 
962 
963 /*! Parses and returns a list-mailbox. This is the same as an atom(),
964     except that the three additional characters %, * and ] are
965     accepted. The return value has been mutf-7 decoded.
966 */
967 
listMailbox()968 UString Command::listMailbox()
969 {
970     EString r( d->args->listMailbox() );
971     if ( !d->args->ok() )
972         error( Bad, d->args->error() );
973 
974     if ( imap()->clientSupports( IMAP::Unicode ) ) {
975         Utf8Codec c;
976         UString u( c.toUnicode( r ) );
977         if ( !c.wellformed() )
978             error( Bad,
979                    "List-mailbox misparsed as UTF-8: " + c.error() );
980         return u;
981     }
982 
983     MUtf7Codec m;
984     UString u( m.toUnicode( r ) );
985     if ( !m.wellformed() ) {
986         AsciiCodec a;
987         u = a.toUnicode( r );
988         if ( !a.valid() )
989             error( Bad,
990                    "List-mailbox misparsed both as ASCII and mUTF-7: " +
991                    m.error() + " (mUTF7) + " + a.error() + " (ASCII)" );
992     }
993     return u;
994 }
995 
996 
997 /*! Parses an IMAP set and returns the corresponding IntegerSet object.
998     The set always contains UIDs; this function creates an UID set even
999     if \a parseMsns is true.
1000 */
1001 
set(bool parseMsns=false)1002 IntegerSet Command::set( bool parseMsns = false )
1003 {
1004     IntegerSet result;
1005     ImapSession *s = 0;
1006     if ( imap() )
1007         s = (ImapSession*)imap()->session();
1008 
1009     uint n1 = 0, n2 = 0;
1010     bool done = false;
1011     while ( ok() && !done ) {
1012         char c = nextChar();
1013         if ( c == '*' ) {
1014             step();
1015             n1 = 0;
1016             if ( s )
1017                 n1 = s->largestUid();
1018             else
1019                 error( Bad, "Need a mailbox session to use * as an UID/MSN" );
1020         }
1021         else if ( c >= '1' && c <= '9' ) {
1022             if ( parseMsns )
1023                 n1 = msn();
1024             else
1025                 n1 = nzNumber();
1026         }
1027         else {
1028             error( Bad, "number or '*' expected, saw: " + following() );
1029         }
1030         c = nextChar();
1031         if ( c == ':' ) {
1032             if ( n2 )
1033                 error( Bad,
1034                        "saw colon after range (" + fn( n1 ) + ":" +
1035                        fn( n2 ) + "), saw:" + following() );
1036             n2 = n1;
1037             n1 = 0;
1038             step();
1039         }
1040         else if ( ok() ) {
1041             if ( n2 )
1042                 result.add( n1, n2 );
1043             else
1044                 result.add( n1 );
1045             n1 = 0;
1046             n2 = 0;
1047             if ( c == ',' )
1048                 step();
1049             else
1050                 done = true;
1051         }
1052     };
1053     return result;
1054 }
1055 
1056 
1057 
1058 /*! Shrinks \a set by removing expunged and nonexistent UIDs. Quiet:
1059     Does not emit any kind of error or response.
1060 */
1061 
shrink(IntegerSet * set)1062 void Command::shrink( IntegerSet * set )
1063 {
1064     Session * s = imap()->session();
1065     if ( !s || !set || set->isEmpty() )
1066         return;
1067 
1068     set->remove( s->expunged() );
1069     *set = set->intersection( s->messages() );
1070 }
1071 
1072 
1073 /*! Parses a single MSN and returns the accompanying UID. */
1074 
msn()1075 uint Command::msn()
1076 {
1077     Mailbox *m;
1078     Session *session = imap()->session();
1079     if ( !session || ( m = session->mailbox() ) == 0 ) {
1080         error( Bad, "Need mailbox to parse MSN" );
1081         return 1;
1082     }
1083 
1084     d->usesMsn = true;
1085 
1086     uint star = session->count();
1087     uint r = star;
1088     if ( nextChar() == '*' ) {
1089         step();
1090         if ( star == 0 )
1091             error( Bad, "* is not valid as MSN in an empty mailbox" );
1092     }
1093     else {
1094         r = nzNumber();
1095     }
1096 
1097     if ( r > star ) {
1098         respond( "OK MSN " + fn( r ) + " is too large. "
1099                  "I hope you mean " + fn( star ) +
1100                  " and will act accordingly." );
1101         r = star;
1102     }
1103 
1104     return session->uid( r );
1105 }
1106 
1107 
1108 /*! Parses a flag name and returns it as a string, or calls error() if
1109     no valid flag name was present. The return value may contain both
1110     upper and lower case letters.
1111 */
1112 
flag()1113 EString Command::flag()
1114 {
1115     EString r( d->args->flag() );
1116     if ( !d->args->ok() )
1117         error( Bad, d->args->error() );
1118     return r;
1119 }
1120 
1121 
1122 /*! Asserts that the end of parsing has been reached. If the IMAP
1123     client has supplied more text, that text is a parse error and
1124     results in a BAD response.
1125 */
1126 
end()1127 void Command::end()
1128 {
1129     d->args->end();
1130     if ( !d->args->ok() )
1131         error( Bad, d->args->error() );
1132 }
1133 
1134 
1135 /*! This private utility function returns a const string of no more
1136     than 15 characters containing the first unparsed bits of input.
1137 */
1138 
following() const1139 const EString Command::following() const
1140 {
1141     return d->args->following();
1142 }
1143 
1144 
1145 /*! This helper returns \a s, quoted such that an IMAP client will
1146     recover \a s. The quoted string fits the IMAP productions astring,
1147     nstring or string, depending on \a mode. The default is string.
1148 
1149     We avoid using the escape characters and unusual atoms. "\"" is a
1150     legal one-character string. But we're easy on the poor client
1151     parser, and we make life easy for ourselves too.
1152 */
1153 
imapQuoted(const EString & s,const QuoteMode mode)1154 EString Command::imapQuoted( const EString & s, const QuoteMode mode )
1155 {
1156     // if we're asked for an nstring, NIL may do
1157     if ( mode == NString && s.isEmpty() )
1158         return "NIL";
1159 
1160     // if the string is really boring and we can send an atom, we do
1161     if ( mode == AString && s.boring() &&
1162          !( s.length() == 3 && s.lower() == "nil" ) )
1163         return s;
1164 
1165     // will quoted do?
1166     uint i = 0;
1167     while ( i < s.length() &&
1168             s[i] >= ' ' && s[i] < 128 &&
1169             s[i] != '\\' && s[i] != '"' )
1170         i++;
1171     if ( i >= s.length() ) // yes
1172         return s.quoted( '"' );
1173 
1174     EString r;
1175     r.reserve( s.length() + 20 );
1176     // if there's a null byte, we need to send a literal8
1177     if ( s.contains( 0 ) )
1178         r.append( '~' );
1179     r.append( '{' );
1180     r.appendNumber( s.length() );
1181     r.append( "}\r\n" );
1182     r.append( s );
1183     return r;
1184 }
1185 
1186 
1187 /*! Parses a mailbox name and returns a pointer to the relevant
1188     mailbox, which is guaranteed to be either a real mailbox or a
1189     view.
1190 
1191     In case of error, mailbox() returns a null pointer and calls
1192     error() appropriately.
1193 */
1194 
mailbox()1195 class Mailbox * Command::mailbox()
1196 {
1197     UString n = mailboxName();
1198     if ( n.isEmpty() )
1199         return 0;
1200 
1201     Mailbox * m = Mailbox::obtain( n, false );
1202     if ( !m ) {
1203         error( No, "No such mailbox: " + n.ascii() );
1204         return 0;
1205     }
1206     if ( m->deleted() ) {
1207         error( No, "Mailbox deleted: " + n.ascii() );
1208         return 0;
1209     }
1210 
1211     if ( !d->mailbox )
1212         d->mailbox = m;
1213 
1214     return m;
1215 }
1216 
1217 
1218 /*! Parse a mailbox name and returns either it or the fully qualified
1219     name of the same name. Returns an empty string and calls error()
1220     in case there is a parse problem.
1221 */
1222 
mailboxName()1223 UString Command::mailboxName()
1224 {
1225     EString n = astring();
1226     if ( n.endsWith( "/" ) )
1227         n = n.mid( 0, n.length() - 1 );
1228 
1229     User * u = imap()->user();
1230     if ( u && n.lower() == "inbox" ) {
1231         return u->inbox()->name();
1232     }
1233 
1234     UString un;
1235     if ( imap()->clientSupports( IMAP::Unicode ) ) {
1236         Utf8Codec c;
1237         un = c.toUnicode( n );
1238         if ( !c.wellformed() ) {
1239             error( Bad,
1240                    "List-mailbox misparsed as UTF-8: " + c.error() );
1241             return UString();
1242         }
1243     }
1244     else {
1245         MUtf7Codec m;
1246         un = m.toUnicode( n );
1247         if ( !m.wellformed() ) {
1248             AsciiCodec a;
1249             un = a.toUnicode( n );
1250             if ( !a.valid() ) {
1251                 error( Bad,
1252                        "Mailbox name misparsed both as ASCII and mUTF-7: " +
1253                        m.error() + " (mUTF7) + " + a.error() + " (ASCII)" );
1254                 return UString();
1255             }
1256         }
1257     }
1258     if ( un.startsWith( "/" ) ) {
1259         if ( u &&
1260              un[u->home()->name().length()] == '/' &&
1261              un.startsWith( u->home()->name() ) )
1262             d->usesAbsoluteMailbox = true;
1263     }
1264     else if ( !u ) {
1265         error( Bad, "Relative mailbox name is invalid before login" );
1266         return un;
1267     }
1268     else {
1269         d->usesRelativeMailbox = true;
1270         UString abs = u->home()->name();
1271         abs.append( "/" );
1272         abs.append( un );
1273         un = abs;
1274     }
1275     if ( !Mailbox::validName( un ) ) {
1276         error( Bad, "Syntax error in mailbox name: " + n );
1277         return UString();
1278     }
1279     return un;
1280 }
1281 
1282 
1283 /*! Returns the name of \a m in the right format for sending to the
1284     client. The result is relative to \a r (if it is supplied), encoded
1285     using mUTF-7 if necessary, quoted appropriately, etc.
1286 
1287     If \a r is null (this is the default), a user is logged in, and
1288     the mailbox is within the user's own namespace, then the result
1289     may be relative or absolute, depending on whether the client seems
1290     to prefer relative or absolute mailbox names.
1291 */
1292 
imapQuoted(Mailbox * m,Mailbox * r)1293 EString Command::imapQuoted( Mailbox * m, Mailbox * r )
1294 {
1295     Mailbox * base = 0;
1296     bool rel = false;
1297     if ( r )
1298         base = r;
1299     else if ( imap()->user() )
1300         base = imap()->user()->home();
1301     // find out whether this name can be expressed as a relative name
1302     if ( base ) {
1303         Mailbox * p = m;
1304         while ( p && p != base )
1305             p = p->parent();
1306         if ( p )
1307             rel = true;
1308         else
1309             rel = false;
1310     }
1311     // if it can, should it? does the client use relative names?
1312     if ( rel ) {
1313         if ( r )
1314             ; // yes, we've explicitly been told to
1315         else if ( d->usesRelativeMailbox )
1316             ; // yes, the client likes relative mailboxes
1317         else if ( d->usesAbsoluteMailbox )
1318             rel = false; // no, the client sent an absolute name
1319         else if ( imap()->user() && imap()->user()->inbox() == m )
1320             rel = true; // the client sent 'inbox'
1321         else if ( imap()->prefersAbsoluteMailboxes() )
1322             rel = false; // past commands used absolute names
1323     }
1324     // find the actual name to return
1325     UString n = m->name();
1326     if ( rel && base != Mailbox::root() )
1327         n = n.mid( base->name().length() + 1 );
1328     // pick the right way to encode the string
1329     Codec * c = 0;
1330     if ( imap()->clientSupports( IMAP::Unicode ) )
1331         c = new Utf8Codec();
1332     else
1333         c = new MUtf7Codec;
1334     return imapQuoted( c->fromUnicode( n ), AString );
1335 }
1336 
1337 
1338 /*! Notes that this command requires \a r on \a m. execute() should
1339     proceed only if and when permitted() is true.
1340 */
1341 
requireRight(Mailbox * m,Permissions::Right r)1342 void Command::requireRight( Mailbox * m, Permissions::Right r )
1343 {
1344     if ( !m )
1345         return;
1346 
1347     if ( !d->checker )
1348         d->checker = new PermissionsChecker;
1349 
1350     Permissions * p = 0;
1351     if ( imap()->state() == IMAP::Selected &&
1352          m == imap()->session()->mailbox() )
1353         p = imap()->session()->permissions();
1354     else
1355         p = d->checker->permissions( m, imap()->user() );
1356     if ( !p )
1357         p = new Permissions( m, imap()->user(), this );
1358 
1359     d->checker->require( p, r );
1360 }
1361 
1362 
1363 /*! Returns true if this command is permitted to proceed, and false if
1364     it either must abort due to lack of rights or wait until
1365     Permissions has fetched more information.
1366 
1367     If permitted() denies permission, it also sets a suitable error
1368     message.
1369 */
1370 
permitted()1371 bool Command::permitted()
1372 {
1373     if ( !d->checker )
1374         return false;
1375     if ( !d->checker->ready() )
1376         return false;
1377     if ( d->checker->allowed() )
1378         return true;
1379     error( No, d->checker->error().simplified() );
1380     setRespTextCode( "ACL" );
1381     return false;
1382 }
1383 
1384 
1385 /*! Remembers that when the time comes to send a tagged OK, \a s
1386     should be sent as resp-text-code. \a s should not contain [],
1387     emitResponses() adds those itself.
1388 */
1389 
setRespTextCode(const EString & s)1390 void Command::setRespTextCode( const EString & s )
1391 {
1392     d->respTextCode = s;
1393 }
1394 
1395 
1396 /*! Records that this Command may be executed in state \a s. The
1397     default is none, or what create() set.
1398 */
1399 
setAllowedState(IMAP::State s) const1400 void Command::setAllowedState( IMAP::State s  ) const
1401 {
1402     d->permittedStates |= ( 1 << s );
1403 }
1404 
1405 
1406 /*! Returns a pointer to the Session for this Command. The Session is
1407     the one that applied when the Command started running. If there
1408     isn't one, then session() logs an error and throws an exception
1409     (which in turn closes the IMAP connection).
1410 
1411     If the returned Session isn't active any longer, then ImapResponse
1412     will make sure the command's results aren't displayed.
1413 */
1414 
session()1415 ImapSession * Command::session()
1416 {
1417     if ( d->imap && !d->session )
1418         d->session = (ImapSession*)d->imap->session();
1419     if ( d->session )
1420         return d->session;
1421     log( "Mailbox session needed, but none present" );
1422     throw Invariant;
1423     return 0;
1424 }
1425 
1426 
1427 /*! Guesses whether this command is part of a client loop processing
1428     group of mailboxes, and returns a pointer to the mailbox group if
1429     that seems to be the case. Returns a null pointer if not.
1430 */
1431 
mailboxGroup()1432 MailboxGroup * Command::mailboxGroup()
1433 {
1434     if ( d->mailbox && !d->checkedMailboxGroup ) {
1435         d->mailboxGroup = imap()->mostLikelyGroup( d->mailbox, 3 );
1436         d->checkedMailboxGroup = true;
1437     }
1438 
1439     return d->mailboxGroup;
1440 }
1441 
1442 
1443 /*! Returns this Command's Transaction, if any was set using
1444     setTransaction(). Returns a null pointer if not.
1445 */
1446 
transaction() const1447 class Transaction * Command::transaction() const
1448 {
1449     return d->transaction;
1450 }
1451 
1452 
1453 /*! Records that this Command uses \a t for its database work. */
1454 
setTransaction(class Transaction * t)1455 void Command::setTransaction( class Transaction * t )
1456 {
1457     d->transaction = t;
1458 }
1459