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