1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
2 
3 #include "fetch.h"
4 
5 #include "messagecache.h"
6 #include "imapsession.h"
7 #include "transaction.h"
8 #include "annotation.h"
9 #include "integerset.h"
10 #include "estringlist.h"
11 #include "mimefields.h"
12 #include "imapparser.h"
13 #include "bodypart.h"
14 #include "address.h"
15 #include "mailbox.h"
16 #include "message.h"
17 #include "ustring.h"
18 #include "section.h"
19 #include "listext.h"
20 #include "fetcher.h"
21 #include "iso8859.h"
22 #include "codec.h"
23 #include "query.h"
24 #include "scope.h"
25 #include "store.h"
26 #include "timer.h"
27 #include "imap.h"
28 #include "date.h"
29 #include "user.h"
30 #include "dict.h"
31 #include "map.h"
32 #include "utf.h"
33 
34 
35 
36 static const char * legalAnnotationAttributes[] = {
37     "value",
38     "value.priv",
39     "value.shared",
40     "size",
41     "size.priv",
42     "size.shared",
43     0
44 };
45 
46 
47 class FetchData
48     : public Garbage
49 {
50 public:
FetchData()51     FetchData()
52         : state( 0 ), peek( true ), processed( 0 ),
53           changedSince( 0 ), those( 0 ), findIds( 0 ),
54           deleted( 0 ), store( 0 ),
55           uid( false ),
56           flags( false ), envelope( false ),
57           body( false ), bodystructure( false ),
58           internaldate( false ), rfc822size( false ),
59           annotation( false ), modseq( false ),
60           databaseId( false ), threadId( false ), vanished( false ),
61           needsHeader( false ), needsAddresses( false ),
62           needsBody( false ), needsPartNumbers( false ),
63           seenDeletedFetcher( 0 ), flagFetcher( 0 ),
64           annotationFetcher( 0 ), modseqFetcher( 0 )
65     {}
66 
67     int state;
68     bool peek;
69     IntegerSet set;
70     IntegerSet remaining;
71     IntegerSet expunged;
72     Map<Message> messages;
73     uint processed;
74     int64 changedSince;
75     Query * those;
76     Query * findIds;
77     Query * deleted;
78     Store * store;
79 
80     // we want to ask for...
81     bool uid;
82     bool flags;
83     bool envelope;
84     bool body;
85     bool bodystructure;
86     bool internaldate;
87     bool rfc822size;
88     bool annotation;
89     bool modseq;
90     bool databaseId;
91     bool threadId;
92     bool vanished;
93     List<Section> sections;
94 
95     // and the sections imply that we...
96     bool needsHeader;
97     bool needsAddresses;
98     bool needsBody;
99     bool needsPartNumbers;
100 
101     EStringList entries;
102     EStringList attribs;
103 
104     struct DynamicData
105         : public Garbage
106     {
107     public:
DynamicDataFetchData::DynamicData108         DynamicData(): modseq( 0 ) {}
109         int64 modseq;
110         Dict<EString> flags;
111         List<Annotation> annotations;
112     };
113     Map<DynamicData> dynamics;
114     Query * seenDeletedFetcher;
115     Query * flagFetcher;
116     Query * annotationFetcher;
117     Query * modseqFetcher;
118 };
119 
120 
121 /*! \class Fetch fetch.h
122 
123     Returns message data (RFC 3501, section 6.4.5, extended by RFC
124     4551 and RFC 5257).
125 
126     Our parser used to be slightly more permissive than the RFC. This
127     is a bug (is it? why?), and many of the problems have been
128     corrected (but not tested).
129 */
130 
131 
132 /*! Creates a new handler for FETCH if \a u is false, or for UID FETCH
133     if \a u is true.
134 */
135 
Fetch(bool u)136 Fetch::Fetch( bool u )
137     : Command(), d( new FetchData )
138 {
139     d->uid = u;
140     if ( u )
141         setGroup( 1 );
142     else
143         setGroup( 2 );
144 }
145 
146 
147 /*! Constructs a handler for the implicit fetch which is executed by
148     ImapSession for flag updates, etc. If \a f is true the updates
149     will include FLAGS sections and if \a a is true, ANNOTATION. The
150     handler starts fetching those messagges in \a set that have a
151     modseq greater than \a limit. The responses are sent via \a i.
152 
153     If \a t is non-null, the fetch operates within a subtransaction
154     of \a t.
155 */
156 
Fetch(bool f,bool a,bool v,const IntegerSet & set,int64 limit,IMAP * i,Transaction * t)157 Fetch::Fetch( bool f, bool a, bool v, const IntegerSet & set,
158               int64 limit, IMAP * i, Transaction * t )
159     : Command( i ), d( new FetchData )
160 {
161     setLog( new Log );
162     Scope x( log() );
163     d->uid = true;
164     d->flags = f;
165     d->annotation = a;
166     d->set = set;
167     d->changedSince = limit;
168     d->modseq = i->clientSupports( IMAP::Condstore );
169     d->vanished = v;
170     if ( t )
171         setTransaction( t->subTransaction( this ) );
172 
173     d->peek = true;
174 
175     Transaction * parent = t;
176     while( parent && parent->parent() )
177         parent = parent->parent();
178 
179     List<Command>::Iterator c( i->commands() );
180     while ( c && c->state() == Command::Retired )
181         ++c;
182     while ( c && c->tag().isEmpty() )
183         ++c;
184     if ( c &&
185          ( ( parent && parent == c->transaction() ) || c->group() > 0 ) &&
186          ( c->state() == Command::Blocked ||
187            c->state() == Command::Finished ||
188            c->state() == Command::Executing ) ) {
189         log( EString("Inserting flag update for modseq>") + fn( limit ) +
190              " and UIDs " + set.set() + " before " +
191              c->tag() + " " + c->name() );
192         i->commands()->insert( c, this );
193         if ( c->group() == 1 || c->group() == 2 )
194             setGroup( c->group() );
195     }
196     else {
197         log( "Appending flag update for modseq>" + fn( limit ) +
198              " and UIDs " + set.set() );
199         i->commands()->append( this );
200     }
201 
202     setAllowedState( IMAP::Selected );
203 }
204 
205 
206 // fetch           = "FETCH" SP set SP ("ALL" / "FULL" / "FAST" / fetch-att /
207 //                   "(" fetch-att *(SP fetch-att) ")")
208 // fetch-att       = "ENVELOPE" / "FLAGS" / "INTERNALDATE" /
209 //                   "RFC822" [".HEADER" / ".SIZE" / ".TEXT"] /
210 //                   "BODY" ["STRUCTURE"] / "UID" /
211 //                   "BODY" [".PEEK"] section ["<" number "." nz-number ">"]
212 //                 / "MODSEQ" ; 4551
213 // section         = "[" [section-spec] "]"
214 // section-spec    = section-msgtext / (section-part ["." section-text])
215 // section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list /
216 //                   "TEXT"
217 // section-part    = nz-number *("." nz-number)
218 // section-text    = section-msgtext / "MIME"
219 // header-list     = "(" header-fld-name *(SP header-fld-name) ")"
220 // header-fld-name = astring
221 
222 
parse()223 void Fetch::parse()
224 {
225     space();
226     d->set = set( !d->uid );
227     space();
228     if ( nextChar() == '(' ) {
229         // "(" fetch-att *(SP fetch-att) ")")
230         step();
231         parseAttribute( false );
232         while( nextChar() == ' ' ) {
233             step();
234             parseAttribute( false );
235         }
236         require( ")" );
237     }
238     else {
239         // single fetch-att, or the macros
240         parseAttribute( true );
241     }
242     if ( present( " (" ) ) {
243         // RFC 4466 fetch-modifiers
244         parseFetchModifier();
245         while ( present( " " ) )
246             parseFetchModifier();
247         require( ")" );
248     }
249     end();
250     if ( d->envelope ) {
251         d->needsHeader = true;
252         d->needsAddresses = true;
253     }
254     if ( d->body || d->bodystructure ) {
255         // message/rfc822 body[structure] includes envelope in some
256         // cases, so we need both here too.
257         d->needsHeader = true;
258         d->needsAddresses = true;
259         // and we even need some data about the bodies
260         d->needsPartNumbers = true;
261     }
262     if ( d->needsBody )
263         d->needsHeader = true; // Bodypart::asText() needs mime type etc
264     if ( !ok() )
265         return;
266     EStringList l;
267     l.append( new EString( "Fetch <=" + fn( d->set.count() ) + " messages: " ) );
268     if ( d->needsAddresses )
269         l.append( "address" );
270     if ( d->needsHeader )
271         l.append( "header" );
272     if ( d->needsBody )
273         l.append( "body" );
274     if ( d->flags )
275         l.append( "flags" );
276     if ( d->internaldate || d->rfc822size || d->databaseId || d->threadId )
277         l.append( "trivia" );
278     if ( d->needsPartNumbers )
279         l.append( "bytes/lines" );
280     if ( d->annotation )
281         l.append( "annotations" );
282     log( l.join( " " ) );
283 }
284 
285 
286 /*! This helper is responsible for parsing a single attribute from the
287     fetch arguments. If \a alsoMacro is true, this function parses a
288     macro as well as a single attribute.
289 */
290 
parseAttribute(bool alsoMacro)291 void Fetch::parseAttribute( bool alsoMacro )
292 {
293     EString keyword = dotLetters( 3, 13 ).lower(); // UID/ALL, RFC822.HEADER
294     if ( alsoMacro && keyword == "all" ) {
295         // equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)
296         d->flags = true;
297         d->envelope = true;
298         d->internaldate = true;
299         d->rfc822size = true;
300     }
301     else if ( alsoMacro && keyword == "full" ) {
302         // equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)
303         d->flags = true;
304         d->envelope = true;
305         d->body = true;
306         d->internaldate = true;
307         d->rfc822size = true;
308     }
309     else if ( alsoMacro && keyword == "fast" ) {
310         // equivalent to: (FLAGS INTERNALDATE RFC822.SIZE)
311         d->flags = true;
312         d->internaldate = true;
313         d->rfc822size = true;
314     }
315     else if ( keyword == "envelope" ) {
316         d->envelope = true;
317     }
318     else if ( keyword == "flags" ) {
319         d->flags = true;
320     }
321     else if ( keyword == "internaldate" ) {
322         d->internaldate = true;
323     }
324     else if ( keyword == "rfc822" ) {
325         d->peek = false;
326         d->needsAddresses = true;
327         d->needsHeader = true;
328         d->needsBody = true;
329         Section * s = new Section;
330         s->id = keyword;
331         d->sections.append( s );
332     }
333     else if ( keyword == "rfc822.header" ) {
334         d->needsAddresses = true;
335         d->needsHeader = true;
336         Section * s = new Section;
337         s->id = keyword;
338         d->sections.append( s );
339     }
340     else if ( keyword == "rfc822.size" ) {
341         d->rfc822size = true;
342     }
343     else if ( keyword == "annotation" ) {
344         d->annotation = true;
345         require( " " );
346         parseAnnotation();
347     }
348     else if ( keyword == "rfc822.text" ) {
349         d->peek = false;
350         d->needsHeader = true;
351         d->needsBody = true;
352         Section * s = new Section;
353         s->id = keyword;
354         d->sections.append( s );
355     }
356     else if ( keyword == "body.peek" && nextChar() == '[' ) {
357         step();
358         parseBody( false );
359     }
360     else if ( keyword == "body" ) {
361         if ( nextChar() == '[' ) {
362             d->peek = false;
363             step();
364             parseBody( false );
365         }
366         else {
367             d->body = true;
368             // poor man's bodystructure
369         }
370     }
371     else if ( keyword == "bodystructure" ) {
372         d->bodystructure = true;
373         // like body, but with bells and whistles
374     }
375     else if ( keyword == "uid" ) {
376         d->uid = true;
377     }
378     else if ( keyword == "binary.peek" && nextChar() == '[' ) {
379         step();
380         parseBody( true );
381     }
382     else if ( keyword == "binary" && nextChar() == '[' ) {
383         d->peek = false;
384         step();
385         parseBody( true );
386     }
387     else if ( keyword == "binary.size" && nextChar() == '[' ) {
388         step();
389         parseBody( true );
390         Section * s = d->sections.last();
391         s->id = "size";
392         if ( s->partial )
393             error( Bad, "Fetching partial BINARY.SIZE is not meaningful" );
394         if ( s->part.isEmpty() )
395             d->rfc822size = true;
396     }
397     else if ( keyword == "modseq" ) {
398         d->modseq = true;
399     }
400     else if ( keyword == "msgid" ) {
401         d->databaseId = true;
402     }
403     else if ( keyword == "thrid" ) {
404         d->threadId = true;
405     }
406     else {
407         error( Bad, "expected fetch attribute, saw word " + keyword );
408     }
409 }
410 
411 
412 /*! This utility function fetches at least \a min, at most \a max
413     characters, all of which must be a letter, a digit or a dot.
414     Consecutive dots ARE allowed.
415 */
416 
dotLetters(uint min,uint max)417 EString Fetch::dotLetters( uint min, uint max )
418 {
419     EString r( parser()->dotLetters( min, max ) );
420     if ( !parser()->ok() )
421         error( Bad, parser()->error() );
422     return r;
423 }
424 
425 
426 /*! Uses the ImapParser \a ip to parse a section-text production, and
427     returns a pointer to a suitably constructed Section object. Upon
428     return, the ImapParser's cursor is advanced to point past the end
429     of the section-text. \a ip must not be 0; and the return value of
430     this function is also guaranteed to be non-zero.
431 
432     If \a binary is false (the default), then the BINARY extensions of
433     RFC 3516 are summarily ignored.
434 
435     If there were any parsing errors, Section::error will be non-empty.
436 */
437 
parseSection(ImapParser * ip,bool binary)438 Section * Fetch::parseSection( ImapParser * ip, bool binary )
439 {
440     Section * s = new Section;
441     s->binary = binary;
442 
443     // section-spec    = section-msgtext / (section-part ["." section-text])
444     // section-msgtext = "HEADER" /
445     //                   "HEADER.FIELDS" [".NOT"] SP header-list /
446     //                   "TEXT"
447     // section-part    = nz-number *("." nz-number)
448     // section-text    = section-msgtext / "MIME"
449 
450     // Parse a section-part.
451     bool dot = false;
452     if ( ip->nextChar() >= '0' && ip->nextChar() <= '9' ) {
453         EString part;
454         part.append( fn( ip->nzNumber() ) );
455         while ( ip->nextChar() == '.' ) {
456             ip->step();
457             if ( ip->nextChar() >= '0' && ip->nextChar() <= '9' ) {
458                 part.append( "." );
459                 part.appendNumber( ip->nzNumber() );
460             }
461             else {
462                 dot = true;
463                 break;
464             }
465         }
466         s->part = part;
467     }
468 
469     // Parse any section-text.
470     EString item = ip->dotLetters( 0, 17 ).lower();
471     if ( binary && !item.isEmpty() ) {
472         s->error = "BINARY with section-text is not legal, saw " + item;
473     }
474     else if ( item.isEmpty() || item == "text" ) {
475         s->needsBody = true;
476         // and because we might need headers and addresses of subparts:
477         s->needsHeader = true;
478         s->needsAddresses = true;
479     }
480     else if ( item == "header" ) {
481         s->needsHeader = true;
482         s->needsAddresses = true;
483     }
484     else if ( item == "header.fields" ||
485               item == "header.fields.not" )
486     {
487         ip->require( " (" );
488         s->fields.append( new EString( ip->astring().headerCased() ) );
489         while ( ip->nextChar() == ' ' ) {
490             ip->require( " " );
491             s->fields.append( new EString( ip->astring().headerCased() ) );
492         }
493         ip->require( ")" );
494         if ( item == "header.fields.not" ) {
495             // if we need to hand out "all other" fields...
496             s->needsAddresses = true;
497             s->needsHeader = true;
498         }
499         EStringList::Iterator i( s->fields );
500         while ( i && ( !s->needsAddresses || !s->needsHeader ) ) {
501             uint t = HeaderField::fieldType( *i );
502             if ( t > 0 && t <= HeaderField::LastAddressField )
503                 s->needsAddresses = true;
504             else
505                 s->needsHeader = true;
506             ++i;
507         }
508     }
509     else if ( item == "mime" ) {
510         if ( s->part.isEmpty() )
511             s->error = "MIME requires a section-part.";
512         s->needsHeader = true;
513     }
514     else if ( dot ) {
515         s->error =
516             "Expected text, header, header.fields etc, not " + item +
517             ip->following();
518     }
519 
520     s->id = item;
521     return s;
522 }
523 
524 
525 /*! Parses a bodypart description - the bit following "body[" in an
526     attribute. The cursor must be after '[' on entry, and is left
527     after the trailing ']'.
528 
529     If \a binary is true, the parsed section will be sent using the
530     BINARY extension (RFC 3516). If not, it'll be sent using a normal
531     BODY.
532 */
533 
parseBody(bool binary)534 void Fetch::parseBody( bool binary )
535 {
536     Section * s = parseSection( parser(), binary );
537     if ( !s->error.isEmpty() ) {
538         error( Bad, s->error );
539         return;
540     }
541 
542     require( "]" );
543 
544     // Parse any range specification.
545     if ( nextChar() == '<' ) {
546         s->partial = true;
547         step();
548         s->offset = number();
549         require( "." );
550         s->length = nzNumber();
551         require( ">" );
552     }
553 
554     d->sections.append( s );
555     if ( s->needsAddresses )
556         d->needsAddresses = true;
557     if ( s->needsHeader )
558         d->needsHeader = true;
559     if ( s->needsBody )
560         d->needsBody = true;
561 }
562 
563 
record(EStringList & l,Dict<void> & d,const EString & a)564 void record( EStringList & l, Dict<void> & d, const EString & a )
565 {
566     if ( !d.contains( a.lower() ) )
567         l.append( new EString( a ) );
568     d.insert( a.lower(), (void *)1 );
569 }
570 
571 
572 /*! Parses the entries and attributes from an ANNOTATION fetch-att.
573     Expects the cursor to be on the first parenthesis, and advances
574     it to past the last one.
575 */
576 
parseAnnotation()577 void Fetch::parseAnnotation()
578 {
579     bool atEnd;
580     bool paren;
581 
582     // Simplified ABNF from draft-ietf-imapext-annotate-15:
583     //
584     //  fetch-att =/ "ANNOTATION" SP "(" entries SP attribs ")"
585     //  entries   = list-mailbox /
586     //              "(" list-mailbox *(SP list-mailbox) ")"
587     //  attribs   = astring /
588     //              "(" astring *(SP astring) ")"
589 
590     require( "(" );
591 
592     paren = false;
593     if ( nextChar() == '(' ) {
594         step();
595         paren = true;
596     }
597 
598     atEnd = false;
599     while ( !atEnd ) {
600         d->entries.append( new EString( parser()->listMailbox() ) );
601         if ( !parser()->ok() )
602             error( Bad, parser()->error() );
603 
604         if ( paren ) {
605             if ( nextChar() == ')' ) {
606                 step();
607                 atEnd = true;
608             }
609             else {
610                 space();
611             }
612         }
613         else {
614             atEnd = true;
615         }
616     }
617 
618     require( " " );
619 
620     paren = false;
621     if ( nextChar() == '(' ) {
622         step();
623         paren = true;
624     }
625 
626     Dict<void> attribs;
627 
628     atEnd = false;
629     while ( !atEnd ) {
630         EString a( astring() );
631 
632         // XXX: This check (and the legalAnnotationAttributes table) is
633         // duplicated in Search::parseKey(). But where should a common
634         // attribute-checking function live?
635         uint i = 0;
636         while ( ::legalAnnotationAttributes[i] &&
637                 a != ::legalAnnotationAttributes[i] )
638             i++;
639         if ( !::legalAnnotationAttributes[i] )
640             error( Bad, "Unknown annotation attribute: " + a );
641 
642         if ( a.endsWith( ".priv" ) || a.endsWith( ".shared" ) ) {
643             record( d->attribs, attribs, a );
644         }
645         else {
646             record( d->attribs, attribs, a + ".priv" );
647             record( d->attribs, attribs, a + ".shared" );
648         }
649 
650         if ( paren ) {
651             if ( nextChar() == ')' ) {
652                 step();
653                 atEnd = true;
654             }
655             else {
656                 space();
657             }
658         }
659         else {
660             atEnd = true;
661         }
662     }
663 
664     require( ")" );
665 }
666 
667 
execute()668 void Fetch::execute()
669 {
670     if ( state() != Executing )
671         return;
672 
673     ImapSession * s = session();
674 
675     if ( !d->peek && s->readOnly() )
676         d->peek = true;
677 
678     if ( d->state == 0 ) {
679         if ( !transaction() &&
680              ( !d->peek ||
681                ( d->modseq && ( d->flags || d->annotation || d->vanished ) ) ) )
682             setTransaction( new Transaction( this ) );
683 
684         if ( d->vanished && d->changedSince > 0 && !d->deleted ) {
685             d->deleted = new Query( "select uid from deleted_messages "
686                                      "where mailbox=$1 and modseq>$2 "
687                                      "and uid=any($3)",
688                                      this );
689             d->deleted->bind( 1, s->mailbox()->id() );
690             d->deleted->bind( 2, d->changedSince );
691             IntegerSet s( d->set );
692             s.remove( session()->messages() );
693             d->deleted->bind( 3, s );
694             transaction()->enqueue( d->deleted );
695         }
696 
697         Mailbox * mb = s->mailbox();
698         if ( !d->those ) {
699             d->set = d->set.intersection( session()->messages() );
700             if ( d->changedSince ) {
701                 d->those = new Query( "select uid, message "
702                                       "from mailbox_messages "
703                                       "where mailbox=$1 and uid=any($2) "
704                                       "and modseq>$3",
705                                       this );
706                 d->those->bind( 1, s->mailbox()->id() );
707                 d->those->bind( 2, d->set );
708             }
709             else if ( d->modseq ||
710                       d->needsAddresses || d->needsHeader ||
711                       d->needsBody || d->needsPartNumbers ||
712                       d->rfc822size || d->internaldate ||
713                       d->databaseId || d->threadId ) {
714                 IntegerSet r;
715                 IntegerSet s( d->set );
716                 while ( !s.isEmpty() ) {
717                     uint uid = s.smallest();
718                     s.remove( uid );
719                     Message * m = MessageCache::find( mb, uid );
720                     if ( m )
721                         d->messages.insert( uid, m );
722                     if ( !m || !m->databaseId() || d->modseq )
723                         r.add( uid );
724                 }
725                 if ( !r.isEmpty() ) {
726                     d->those = new Query( "select uid, message "
727                                           "from mailbox_messages "
728                                           "where mailbox=$1 and uid=any($2)",
729                                           this );
730                     d->those->bind( 1, session()->mailbox()->id() );
731                     d->those->bind( 2, d->set );
732                 }
733             }
734             if ( d->those ) {
735                 if ( d->changedSince )
736                     d->those->bind( 3, d->changedSince );
737                 if ( d->modseq ) {
738                     if ( !d->peek ) {
739                         // if we aren't peeking, then we have to lock
740                         // the mailbox before we lock the messages,
741                         // otherwise we might deadlock with Store or
742                         // Expunge.
743                         Query * q = new Query( "select nextmodseq "
744                                                "from mailboxes "
745                                                "where id=$1 for update", 0 );
746                         q->bind( 1, mb->id() );
747                         transaction()->enqueue( q );
748                     }
749                     EString s = d->those->string();
750                     s.append( " order by uid for update" );
751                     d->those->setString( s );
752                 }
753                 enqueue( d->those );
754             }
755         }
756         if ( transaction() )
757             transaction()->execute();
758         if ( d->those ) {
759             if ( !d->those->done() )
760                 return;
761             d->set.clear();
762             Row * r;
763             while ( d->those->hasResults() ) {
764                 r = d->those->nextRow();
765                 uint uid = r->getInt( "uid" );
766                 d->set.add( uid );
767                 Message * m = d->messages.find( uid );
768                 if ( !m ) {
769                     m = MessageCache::provide( mb, uid );
770                     d->messages.insert( uid, m );
771                 }
772                 m->setDatabaseId( r->getInt( "message" ) );
773                 if ( d->modseq || d->flags || d->annotation ) {
774                     FetchData::DynamicData * dd = new FetchData::DynamicData;
775                     d->dynamics.insert( uid, dd );
776                 }
777             }
778         }
779         else {
780             IntegerSet r( d->set );
781             while ( !r.isEmpty() ) {
782                 uint uid = r.smallest();
783                 r.remove( uid );
784                 d->dynamics.insert( uid, new FetchData::DynamicData );
785             }
786         }
787         d->state = 1;
788     }
789 
790     if ( d->deleted && d->deleted->done() ) {
791         IntegerSet vanished;
792         while ( d->deleted->hasResults() ) {
793             Row * r = d->deleted->nextRow();
794             uint uid = r->getInt( "uid" );
795             vanished.add( uid );
796         }
797         if ( !vanished.isEmpty() )
798             respond( "VANISHED (EARLIER) " + vanished.set() );
799         d->deleted = 0;
800     }
801 
802     if ( d->state == 1 ) {
803         if ( group() == 2 ) // then RFC 2180 section 4.1.2 applies
804             d->expunged = s->expunged().intersection( d->set );
805         shrink( &d->set );
806         d->remaining = d->set;
807         d->state = 2;
808         if ( d->set.isEmpty() ) {
809             d->state = 5;
810             if ( transaction() )
811                 transaction()->commit();
812         }
813     }
814 
815     if ( d->state == 2 ) {
816         if ( d->peek ) {
817             d->state = 3;
818         }
819         else {
820             if ( !d->store ) {
821                 List<Command>::Iterator c = imap()->commands()->find( this );
822                 if ( c ) {
823                     d->store = new Store( imap(), d->set, d->flags,
824                                           transaction() );
825                     d->store->setState( Executing );
826                     imap()->commands()->insert( c, d->store );
827                     // should we feed the Store a subtransaction, if
828                     // we're using one? I don't know.
829                     d->store->execute();
830                 }
831             }
832             if ( d->store && d->store->state() == Executing )
833                 return;
834             d->state = 3;
835         }
836     }
837 
838     if ( d->state == 3 ) {
839         d->state = 4;
840         sendFetchQueries();
841         if ( d->flags )
842             sendFlagQuery();
843         if ( d->annotation )
844             sendAnnotationsQuery();
845         if ( d->modseq )
846             sendModSeqQuery();
847         if ( transaction() )
848             transaction()->commit();
849     }
850 
851     if ( d->state < 4 )
852         return;
853 
854     pickup();
855 
856     if ( d->processed < d->set.largest() )
857         return;
858 
859     if ( !d->expunged.isEmpty() ) {
860         s->recordExpungedFetch( d->expunged );
861         error( No, "UID(s) " + d->expunged.set() + " has/have been expunged" );
862     }
863     finish();
864 }
865 
866 
867 /*! Issues queries to resolve any questions this FETCH needs to answer.
868 */
869 
sendFetchQueries()870 void Fetch::sendFetchQueries()
871 {
872     bool haveAddresses = true;
873     bool haveHeader = true;
874     bool haveBody = true;
875     bool havePartNumbers = true;
876     bool haveTrivia = true;
877 
878     List<Message> * l = new List<Message>;
879 
880     Map<Message>::Iterator i( d->messages );
881     while ( i ) {
882         Message * m = i;
883         ++i;
884         if ( !m->hasAddresses() )
885             haveAddresses = false;
886         if ( !m->hasHeaders() )
887             haveHeader = false;
888         if ( !m->hasBytesAndLines() )
889             havePartNumbers = false;
890         if ( !m->hasBodies() )
891             haveBody = false;
892         if ( !m->hasTrivia() )
893             haveTrivia = false;
894         l->append( m );
895     }
896 
897     Fetcher * f = new Fetcher( l, this, imap() );
898     if ( d->needsAddresses && !haveAddresses )
899         f->fetch( Fetcher::Addresses );
900     if ( d->needsHeader && !haveHeader )
901         f->fetch( Fetcher::OtherHeader );
902     if ( d->needsBody && !haveBody )
903         f->fetch( Fetcher::Body );
904     if ( ( d->rfc822size || d->internaldate ||
905            d->databaseId || d->threadId ) && !haveTrivia )
906         f->fetch( Fetcher::Trivia );
907     if ( d->needsPartNumbers && !havePartNumbers )
908         f->fetch( Fetcher::PartNumbers );
909     f->execute();
910 }
911 
912 
913 /*! This function returns the text of that portion of the Message \a m
914     that is described by the Section \a s. It is publicly available so
915     that Append may use it for CATENATE.
916 
917     If \a unicodable is true, the result may contain unquoted unicode.
918 */
919 
sectionData(Section * s,Message * m,bool unicodable)920 EString Fetch::sectionData( Section * s, Message * m, bool unicodable )
921 {
922     EString item, data;
923 
924     if ( s->id == "rfc822" ) {
925         item = s->id.upper();
926         data = m->rfc822( !unicodable );
927     }
928 
929     else if ( s->id == "mime" ||
930               s->id == "rfc822.header" ||
931               s->id.startsWith( "header" ) ) {
932         bool rfc822 = s->id == "rfc822.header";
933         bool fields = s->id.startsWith( "header.fields" );
934         bool exclude = s->id.endsWith( ".not" );
935 
936         data.reserve( 80 * s->fields.count() ); // suboptimal for .not, but...
937 
938         Header * hdr = m->header();
939         if ( !s->part.isEmpty() ) {
940             Bodypart * bp = m->bodypart( s->part, false );
941             if ( bp && bp->header() )
942                 hdr = bp->header();
943             else
944                 hdr = 0;
945         }
946 
947         List< HeaderField >::Iterator it;
948         if ( hdr )
949             it = hdr->fields()->first();
950         while ( it ) {
951             bool include = false;
952             if ( !fields ) {
953                 include = true;
954             }
955             else {
956                 bool listed = s->fields.find( it->name() );
957                 if ( exclude )
958                     include = !listed;
959                 else
960                     include = listed;
961             }
962             if ( include ) {
963                 EString n = it->name().headerCased();
964                 data.append( n );
965                 data.append( ": " );
966                 data.append( it->rfc822( !unicodable ) );
967                 data.append( "\r\n" );
968             }
969             ++it;
970         }
971 
972         item = s->id.upper();
973         if ( !rfc822 ) {
974             if ( !s->part.isEmpty() )
975                 item = s->part + "." + item;
976             item = "BODY[" + item;
977             if ( fields )
978                 item.append( " (" + s->fields.join( " " ) + ")" );
979             item.append( "]" );
980         }
981         data.append( "\r\n" );
982     }
983 
984     else if ( s->id == "rfc822.text" ) {
985         item = s->id.upper();
986         data = m->body( !unicodable );
987     }
988 
989     else if ( s->id == "text" ) {
990         if ( s->part.isEmpty() ) {
991             item = "TEXT";
992             data = m->body( !unicodable );
993         }
994         else {
995             item = s->part + ".TEXT";
996             Bodypart *bp = m->bodypart( s->part, false );
997             if ( bp && bp->message() )
998                 data = bp->message()->body( !unicodable );
999         }
1000         item = "BODY[" + item + "]";
1001     }
1002 
1003     else if ( ( s->id.isEmpty() || s->id == "size" ) &&
1004               s->part.isEmpty() )
1005     {
1006         if ( s->id == "size" ) {
1007             item = "BINARY.SIZE[]";
1008             data = fn( m->rfc822Size() );
1009         }
1010         else {
1011             item = "BODY[]";
1012             data = m->rfc822( !unicodable );
1013         }
1014     }
1015 
1016     else if ( s->id.isEmpty() || s->id == "size" ) {
1017         item = "BODY";
1018         Bodypart * bp = m->bodypart( s->part, false );
1019         if ( !bp ) {
1020             // nonexistent part number
1021             if ( s->binary )
1022                 item = "BINARY";
1023             // should we report an error?  the fetch responses will be
1024             // sent anyway.
1025             // error( No, "No such bodypart: " + s->part );
1026         }
1027         else if ( bp->message() ) {
1028             // message/rfc822 part
1029             data = bp->message()->rfc822( !unicodable );
1030         }
1031         else if ( bp->children()->isEmpty() ) {
1032             // leaf part
1033             data = bp->data();
1034 
1035             ContentType * ct = bp->contentType();
1036             if ( !ct || ct->type() == "text" ) {
1037                 UString text;
1038 
1039                 if ( data.isEmpty() ) {
1040                     text = bp->text();
1041                 }
1042                 else {
1043                     Codec * c = new Utf8Codec;
1044                     text = c->toUnicode( data );
1045                 }
1046 
1047                 Codec * c = 0;
1048                 if ( ct )
1049                     c = Codec::byName( ct->parameter( "charset" ) );
1050                 if ( !c && ct && ct->subtype() == "html" )
1051                     c = new Iso88591Codec;
1052                 if ( !c )
1053                     c = new Utf8Codec;
1054                 data = c->fromUnicode( text );
1055             }
1056             if ( !s->binary )
1057                 data = data.encoded( bp->contentTransferEncoding(), 70 );
1058         }
1059         else {
1060             // nonleaf part. probably wrong - this might use the wrong
1061             // content-transfer-encoding.
1062             data = bp->asText( !unicodable );
1063         }
1064 
1065         if ( s->binary )
1066             item = "BINARY";
1067 
1068         if ( s->id == "size" ) {
1069             item = "BINARY.SIZE";
1070             data = fn( data.length() );
1071         }
1072 
1073         item = item + "[" + s->part + "]";
1074     }
1075 
1076     if ( s->partial ) {
1077         item.append( "<" + fn( s->offset ) + ">" );
1078         data = data.mid( s->offset, s->length );
1079     }
1080 
1081     s->item = item;
1082     return data;
1083 }
1084 
1085 
1086 /* This function returns the response data for an element in
1087    d->sections, to be included in the FETCH response by
1088    fetchResponses() below. If \a unicode is false, the result will be
1089    downgraded rather than contain unicode.
1090 */
1091 
sectionResponse(Section * s,Message * m,bool unicode)1092 static EString sectionResponse( Section * s, Message * m, bool unicode )
1093 {
1094     EString data( Fetch::sectionData( s, m, unicode ) );
1095     if ( !s->item.startsWith( "BINARY.SIZE" ) )
1096         data = Command::imapQuoted( data, Command::NString );
1097     EString r;
1098     r.reserve( data.length() + s->item.length() + 1 );
1099     r.append( s->item );
1100     r.append( " " );
1101     r.append( data );
1102     return r;
1103 }
1104 
1105 
1106 /*! Emits a single FETCH response for the message \a m, which is
1107     trusted to have UID \a uid and MSN \a msn.
1108 
1109     The message must have all necessary content.
1110 */
1111 
makeFetchResponse(Message * m,uint uid,uint msn)1112 EString Fetch::makeFetchResponse( Message * m, uint uid, uint msn )
1113 {
1114     EStringList l;
1115     if ( d->uid )
1116         l.append( "UID " + fn( uid ) );
1117     if ( d->databaseId )
1118         l.append( "MSGID " + fn( m->databaseId() ) );
1119     if ( d->threadId )
1120         l.append( "THRID " + fn( m->threadId() ) );
1121     if ( d->rfc822size )
1122         l.append( "RFC822.SIZE " + fn( m->rfc822Size() ) );
1123     if ( d->flags )
1124         l.append( "FLAGS (" + flagList( uid ) + ")" );
1125     if ( d->internaldate )
1126         l.append( "INTERNALDATE " + internalDate( m ) );
1127     if ( d->envelope )
1128         l.append( "ENVELOPE " + envelope( m ) );
1129     if ( d->body )
1130         l.append( "BODY " + bodyStructure( m, false ) );
1131     if ( d->bodystructure )
1132         l.append( "BODYSTRUCTURE " + bodyStructure( m, true ) );
1133     if ( d->annotation )
1134         l.append( "ANNOTATION " + annotation( imap()->user(), uid,
1135                                               d->entries, d->attribs ) );
1136     if ( d->modseq ) {
1137         FetchData::DynamicData * dd = d->dynamics.find( uid );
1138         if ( dd && dd->modseq )
1139             l.append( "MODSEQ (" + fn( dd->modseq ) + ")" );
1140     }
1141 
1142     List< Section >::Iterator it( d->sections );
1143     bool unicode = imap()->clientSupports( IMAP::Unicode );
1144     while ( it ) {
1145         l.append( sectionResponse( it, m, unicode ) );
1146         ++it;
1147     }
1148 
1149     EString r;
1150     EString payload = l.join( " " );
1151     r.reserve( payload.length() + 30 );
1152     r.appendNumber( msn );
1153     r.append( " FETCH (" );
1154     r.append( payload );
1155     r.append( ")" );
1156     return r;
1157 }
1158 
1159 
1160 /*! Returns a string containing all the flags that are set for the
1161     message with \a uid.
1162 */
1163 
flagList(uint uid)1164 EString Fetch::flagList( uint uid )
1165 {
1166     EStringList r;
1167 
1168     FetchData::DynamicData * dd = d->dynamics.find( uid );
1169     if ( dd ) {
1170         if ( session()->isRecent( uid ) )
1171             dd->flags.insert( "\\recent", new EString( "\\Recent" ) );
1172         Dict<EString>::Iterator i( dd->flags );
1173         while ( i ) {
1174             r.append( *i );
1175             ++i;
1176         }
1177     }
1178 
1179     return r.join( " " );
1180 }
1181 
1182 
1183 /*! Returns the internaldate of \a m in IMAP format. */
1184 
internalDate(Message * m)1185 EString Fetch::internalDate( Message * m )
1186 {
1187     Date date;
1188     date.setUnixTime( m->internalDate() );
1189     return "\"" + date.imap() + "\"";
1190 }
1191 
1192 
hf(Header * f,HeaderField::Type t,bool unicodable)1193 static EString hf( Header * f, HeaderField::Type t, bool unicodable )
1194 {
1195     List<Address> * a = f->addresses( t );
1196     if ( !a || a->isEmpty() )
1197         return "NIL ";
1198     EString r;
1199     r.reserve( 50 );
1200     r.append( "(" );
1201     List<Address>::Iterator it( a );
1202     while ( it ) {
1203         r.append( "(" );
1204         if ( it->type() == Address::EmptyGroup ) {
1205             r.append( "NIL NIL " );
1206             r.append( Command::imapQuoted( it->name( !unicodable ),
1207                                            Command::NString ) );
1208             r.append( " NIL)(NIL NIL NIL NIL" );
1209         } else if ( it->type() == Address::Local ||
1210                     it->type() == Address::Normal ) {
1211             UString u = it->uname();
1212             EString eu;
1213             if ( u.isAscii() || unicodable )
1214                 eu = u.simplified().utf8();
1215             else
1216                 eu = HeaderField::encodePhrase( u );
1217             r.append( Command::imapQuoted( eu, Command::NString ) );
1218             r.append( " NIL " );
1219             if ( unicodable ||
1220                  ( it->localpart().isAscii() && it->domain().isAscii() ) ) {
1221                 r.append( Command::imapQuoted( it->localpart().utf8(),
1222                                                Command::NString ) );
1223                 r.append( " " );
1224                 if ( it->domain().isEmpty() )
1225                     r.append( "\" \"" ); // RFC 3501, page 77 near bottom
1226                 else
1227                     r.append( Command::imapQuoted( it->domain().utf8(),
1228                                                    Command::NString ) );
1229             }
1230             else {
1231                 r.append( "noreply unicode-needed.invalid" );
1232             }
1233         }
1234         r.append( ")" );
1235         ++it;
1236     }
1237     r.append( ") " );
1238     return r;
1239 }
1240 
1241 
1242 /*! Returns the IMAP envelope for \a m. */
1243 
envelope(Message * m)1244 EString Fetch::envelope( Message * m )
1245 {
1246     Header * h = m->header();
1247 
1248     // envelope = "(" env-date SP env-subject SP env-from SP
1249     //                env-sender SP env-reply-to SP env-to SP env-cc SP
1250     //                env-bcc SP env-in-reply-to SP env-message-id ")"
1251 
1252     EString r;
1253     r.reserve( 300 );
1254     r.append( "(" );
1255 
1256     Date * date = h->date();
1257     if ( date )
1258         r.append( imapQuoted( date->rfc822(), NString ) );
1259     else
1260         r.append( "NIL" );
1261     r.append( " " );
1262 
1263     r.append( imapQuoted( h->subject(), NString ) + " " );
1264     bool unicode = imap()->clientSupports( IMAP::Unicode );
1265     r.append( hf( h, HeaderField::From, unicode ) );
1266     r.append( hf( h, HeaderField::Sender, unicode ) );
1267     r.append( hf( h, HeaderField::ReplyTo, unicode ) );
1268     r.append( hf( h, HeaderField::To, unicode ) );
1269     r.append( hf( h, HeaderField::Cc, unicode ) );
1270     r.append( hf( h, HeaderField::Bcc, unicode ) );
1271     r.append( imapQuoted( h->inReplyTo(), NString ) + " " );
1272     r.append( imapQuoted( h->messageId(), NString ) );
1273 
1274     r.append( ")" );
1275     return r;
1276 }
1277 
1278 
parameterEString(MimeField * mf)1279 static EString parameterEString( MimeField *mf )
1280 {
1281     EStringList *p = 0;
1282 
1283     if ( mf )
1284         p = mf->parameters();
1285     if ( !mf || !p || p->isEmpty() )
1286         return "NIL";
1287 
1288     EStringList l;
1289     EStringList::Iterator it( p );
1290     while ( it ) {
1291         l.append( Command::imapQuoted( *it ) );
1292         l.append( Command::imapQuoted( mf->parameter( *it ) ) );
1293         ++it;
1294     }
1295 
1296     EString r = l.join( " " );
1297     r.prepend( "(" );
1298     r.append( ")" );
1299     return r;
1300 }
1301 
1302 
dispositionEString(ContentDisposition * cd)1303 static EString dispositionEString( ContentDisposition *cd )
1304 {
1305     if ( !cd )
1306         return "NIL";
1307 
1308     EString s;
1309     switch ( cd->disposition() ) {
1310     case ContentDisposition::Inline:
1311         s = "inline";
1312         break;
1313     case ContentDisposition::Attachment:
1314         s = "attachment";
1315         break;
1316     }
1317 
1318     return "(\"" + s + "\" " + parameterEString( cd ) + ")";
1319 }
1320 
1321 
languageEString(ContentLanguage * cl)1322 static EString languageEString( ContentLanguage *cl )
1323 {
1324     if ( !cl )
1325         return "NIL";
1326 
1327     EStringList m;
1328     const EStringList *l = cl->languages();
1329     EStringList::Iterator it( l );
1330     while ( it ) {
1331         m.append( Command::imapQuoted( *it ) );
1332         ++it;
1333     }
1334 
1335     if ( l->count() == 1 )
1336         return *m.first();
1337     EString r = m.join( " " );
1338     r.prepend( "(" );
1339     r.append( ")" );
1340     return r;
1341 }
1342 
1343 
1344 /*! Returns either the IMAP BODY or BODYSTRUCTURE production for \a
1345     m. If \a extended is true, BODYSTRUCTURE is returned. If it's
1346     false, BODY.
1347 */
1348 
bodyStructure(Multipart * m,bool extended)1349 EString Fetch::bodyStructure( Multipart * m, bool extended )
1350 {
1351     EString r;
1352     bool isSigned = false;
1353     Multipart * ancestor = m;
1354     while ( ancestor->parent() != NULL )
1355         ancestor = ancestor->parent();
1356     if ( ancestor->isMessage() ) {
1357         Message *msg = (Message *)ancestor;
1358         if ( msg->hasPGPsignedPart() ) {
1359             ::log( "Fetch::bodyStructure - signed message", Log::Debug );
1360             isSigned = true;
1361         }
1362     }
1363 
1364     Header * hdr = m->header();
1365     ContentType * ct = hdr->contentType();
1366     if ( ct && ct->type() == "multipart" ) {
1367         EStringList children;
1368         List< Bodypart >::Iterator it( m->children() );
1369         if ( ( m == ancestor ) && isSigned ) {  // if top level, consider raw part
1370             if ( !extended ) {
1371                 log( "Fetch::bodyStructure - append raw part", Log::Debug );
1372                 children.append( bodyStructure( it, extended ) );
1373                 uint i;
1374                 for ( i = 1; i <= m->children()->count(); i++ )
1375                     ++it;
1376             } else {  // skip raw part
1377                 log( "Fetch::bodyStructure - skip raw part", Log::Debug );
1378                 ++it;
1379             }
1380         }
1381         while ( it ) {
1382             children.append( bodyStructure( it, extended ) );
1383             ++it;
1384         }
1385 
1386         r = children.join( "" );
1387         r.prepend( "(" );
1388         r.append( " " );
1389         r.append( imapQuoted( ct->subtype() ));
1390 
1391         if ( extended ) {
1392             r.append( " " );
1393             r.append( parameterEString( ct ) );
1394             r.append( " " );
1395             r.append( dispositionEString( hdr->contentDisposition() ) );
1396             r.append( " " );
1397             r.append( languageEString( hdr->contentLanguage() ) );
1398             r.append( " " );
1399             r.append( imapQuoted( hdr->contentLocation(), NString ) );
1400         }
1401 
1402         r.append( ")" );
1403     }
1404     else {
1405         r = singlePartStructure( (Bodypart*)m, extended );
1406     }
1407     return r;
1408 }
1409 
1410 
1411 /*! Returns the structure of the single-part bodypart \a mp.
1412 
1413     If \a extended is true, extended BODYSTRUCTURE attributes are
1414     included.
1415 */
1416 
singlePartStructure(Multipart * mp,bool extended)1417 EString Fetch::singlePartStructure( Multipart * mp, bool extended )
1418 {
1419     EStringList l;
1420 
1421     if ( !mp )
1422         return "";
1423 
1424     ContentType * ct = mp->header()->contentType();
1425 
1426     if ( ct ) {
1427         l.append( imapQuoted( ct->type() ) );
1428         l.append( imapQuoted( ct->subtype() ) );
1429     }
1430     else {
1431         // XXX: What happens to the default if this is a /digest?
1432         l.append( "\"text\"" );
1433         l.append( "\"plain\"" );
1434     }
1435 
1436     l.append( parameterEString( ct ) );
1437     l.append( imapQuoted( mp->header()->messageId( HeaderField::ContentId ),
1438                           NString ) );
1439     l.append( imapQuoted( mp->header()->contentDescription(), NString ) );
1440 
1441     if ( mp->header()->contentTransferEncoding() ) {
1442         switch( mp->header()->contentTransferEncoding()->encoding() ) {
1443         case EString::Binary:
1444             l.append( "\"8BIT\"" ); // hm. is this entirely sound?
1445             break;
1446         case EString::Uuencode:
1447             l.append( "\"x-uuencode\"" ); // should never happen
1448             break;
1449         case EString::Base64:
1450             l.append( "\"BASE64\"" );
1451             break;
1452         case EString::QP:
1453             l.append( "\"QUOTED-PRINTABLE\"" );
1454             break;
1455         }
1456     }
1457     else {
1458         l.append( "\"7BIT\"" );
1459     }
1460 
1461     Bodypart * bp = 0;
1462     if ( mp->isBodypart() )
1463         bp = (Bodypart*)mp;
1464     else if ( mp->isMessage() )
1465         bp = ((Message*)mp)->children()->first();
1466 
1467     if ( bp ) {
1468         l.append( fn( bp->numEncodedBytes() ) );
1469         if ( ct && ct->type() == "message" && ct->subtype() == "rfc822" ) {
1470             // body-type-msg   = media-message SP body-fields SP envelope
1471             //                   SP body SP body-fld-lines
1472             l.append( envelope( bp->message() ) );
1473             l.append( bodyStructure( bp->message(), extended ) );
1474             l.append( fn ( bp->numEncodedLines() ) );
1475         }
1476         else if ( !ct || ct->type() == "text" ) {
1477             // body-type-text  = media-text SP body-fields SP body-fld-lines
1478             l.append( fn( bp->numEncodedLines() ) );
1479         }
1480     }
1481 
1482     if ( extended ) {
1483         EString md5;
1484         HeaderField *f = mp->header()->field( HeaderField::ContentMd5 );
1485         if ( f )
1486             md5 = f->rfc822( false );
1487 
1488         l.append( imapQuoted( md5, NString ) );
1489         l.append( dispositionEString( mp->header()->contentDisposition() ) );
1490         l.append( languageEString( mp->header()->contentLanguage() ) );
1491         l.append( imapQuoted( mp->header()->contentLocation(), NString ) );
1492     }
1493 
1494     EString r = l.join( " " );
1495     r.prepend( "(" );
1496     r.append( ")" );
1497     return r;
1498 }
1499 
1500 
1501 /*! Returns the IMAP ANNOTATION production for the message with \a
1502     uid, from the point of view of \a u (0 for no user, only public
1503     annotations). \a entrySpecs is a list of the entries to be
1504     matched, each of which can contain the * and % wildcards. \a
1505     attributes is a list of attributes to be returned (each including
1506     the .priv or .shared suffix).
1507 */
1508 
annotation(User * u,uint uid,const EStringList & entrySpecs,const EStringList & attributes)1509 EString Fetch::annotation( User * u, uint uid,
1510                           const EStringList & entrySpecs,
1511                           const EStringList & attributes )
1512 {
1513     FetchData::DynamicData * dd = d->dynamics.find( uid );
1514     if ( !dd ) {
1515         setRespTextCode( "SERVERBUG" );
1516         return "()";
1517     }
1518 
1519     typedef Dict< EString > AttributeDict;
1520     Dict< AttributeDict > entries;
1521 
1522     EStringList entryNames;
1523 
1524     uint user = 0;
1525     if ( u )
1526         user = u->id();
1527     List<Annotation>::Iterator i( dd->annotations );
1528     while ( i ) {
1529         Annotation * a = i;
1530         ++i;
1531 
1532         EString entry( a->entryName() );
1533         bool entryWanted = false;
1534         EStringList::Iterator e( entrySpecs );
1535         while ( e && !entryWanted ) {
1536             AsciiCodec c;
1537             if ( Mailbox::match( c.toUnicode( *e ), 0,
1538                                  c.toUnicode( entry ), 0 ) == 2 ) {
1539                 if ( !entries.find( entry ) )
1540                     entryNames.append( entry );
1541                 entryWanted = true;
1542             }
1543             ++e;
1544         }
1545 
1546         if ( ( a->ownerId() == 0 || a->ownerId() == user ) &&
1547              entryWanted )
1548         {
1549             AttributeDict * atts = entries.find( entry );
1550             if ( !atts ) {
1551                 atts = new AttributeDict;
1552                 entries.insert( entry, atts );
1553             }
1554 
1555             const char * suffix = ".shared";
1556             if ( a->ownerId() )
1557                 suffix = ".priv";
1558 
1559             EString * v = new EString( a->value() );
1560             EString * s = new EString( fn( v->length() ) );
1561 
1562             atts->insert( EString( "value" ) + suffix, v );
1563             atts->insert( EString( "size" ) + suffix, s );
1564         }
1565     }
1566 
1567     EString r( "(" );
1568     EStringList::Iterator e( entryNames );
1569     while ( e ) {
1570         EString entry( *e );
1571 
1572         EStringList l;
1573         EStringList::Iterator a( attributes );
1574         while ( a ) {
1575             EString attrib( *a );
1576 
1577             EString * value = 0;
1578             AttributeDict * atts = entries.find( entry );
1579             if ( atts )
1580                 value = atts->find( attrib );
1581 
1582             EString tmp = attrib;
1583             tmp.append( " " );
1584             if ( value )
1585                 tmp.append( imapQuoted( *value ) );
1586             else if ( attrib.startsWith( "size." ) )
1587                 tmp.append( "\"0\"" );
1588             else
1589                 tmp.append( "NIL" );
1590             ++a;
1591             l.append( tmp );
1592         }
1593 
1594         r.append( entry );
1595         if ( !l.isEmpty() ) {
1596             r.append( " (" );
1597             r.append( l.join( " " ) );
1598             r.append( ")" );
1599         }
1600 
1601         ++e;
1602         if ( e )
1603             r.append( " " );
1604     }
1605     r.append( ")" );
1606     return r;
1607 }
1608 
1609 
1610 /*! Parses a single RFC 4466 fetch-modifier. At the moment RFC 4551
1611     and RFC 7162 are supported.
1612 */
1613 
parseFetchModifier()1614 void Fetch::parseFetchModifier()
1615 {
1616     EString name = atom().lower();
1617     if ( name == "changedsince" ) {
1618         space();
1619         d->changedSince = number();
1620         d->modseq = true;
1621     }
1622     else if ( name == "vanished" ) {
1623         d->vanished = true;
1624     }
1625     else {
1626         error( Bad, "Unknown fetch modifier: " + name );
1627     }
1628 }
1629 
1630 
1631 /*! Retrieves completed messages and builds ImapFetchResponse objects.
1632 */
1633 
pickup()1634 void Fetch::pickup()
1635 {
1636     ImapSession * s = (ImapSession *)imap()->session();
1637     if ( !s )
1638         return;
1639 
1640     if ( d->seenDeletedFetcher ) {
1641         EString seenl( "\\seen" );
1642         EString * seen = new EString( "\\Seen" );
1643         EString deletedl( "\\deleted" );
1644         EString * deleted = new EString( "\\Deleted" );
1645         while ( d->seenDeletedFetcher->hasResults() ) {
1646             Row * r = d->seenDeletedFetcher->nextRow();
1647             uint uid = r->getInt( "uid" );
1648             FetchData::DynamicData * dd = d->dynamics.find( uid );
1649             if ( !dd ) {
1650                 dd = new FetchData::DynamicData;
1651                 d->dynamics.insert( uid, dd );
1652             }
1653             if ( r->getBoolean( "seen" ) )
1654                 dd->flags.insert( seenl, seen );
1655             if ( r->getBoolean( "deleted" ) )
1656                 dd->flags.insert( deletedl, deleted );
1657         }
1658         while ( d->flagFetcher->hasResults() ) {
1659             Row * r = d->flagFetcher->nextRow();
1660             uint uid = r->getInt( "uid" );
1661             FetchData::DynamicData * dd = d->dynamics.find( uid );
1662             if ( !dd ) {
1663                 dd = new FetchData::DynamicData;
1664                 d->dynamics.insert( uid, dd );
1665             }
1666             EString f = r->getEString( "name" );
1667             if ( !f.isEmpty() )
1668                 dd->flags.insert( f.lower(), new EString( f ) );
1669         }
1670         if ( d->seenDeletedFetcher->done() &&
1671              d->flagFetcher->done() ) {
1672             d->seenDeletedFetcher = 0;
1673             d->flagFetcher = 0;
1674         }
1675     }
1676 
1677     if ( d->annotationFetcher ) {
1678         while ( d->annotationFetcher->hasResults() ) {
1679             Row * r = d->annotationFetcher->nextRow();
1680             uint uid = r->getInt( "uid" );
1681             FetchData::DynamicData * dd = d->dynamics.find( uid );
1682             if ( !dd ) {
1683                 dd = new FetchData::DynamicData;
1684                 d->dynamics.insert( uid, dd );
1685             }
1686 
1687             EString n = r->getEString( "name" );
1688             EString v( r->getEString( "value" ) );
1689 
1690             uint owner = 0;
1691             if ( !r->isNull( "owner" ) )
1692                 owner = r->getInt( "owner" );
1693 
1694             dd->annotations.append( new Annotation( n, v, owner ) );
1695         }
1696     }
1697 
1698     if ( d->modseqFetcher ) {
1699         while ( d->modseqFetcher->hasResults() ) {
1700             Row * r = d->modseqFetcher->nextRow();
1701             uint uid = r->getInt( "uid" );
1702             FetchData::DynamicData * dd = d->dynamics.find( uid );
1703             if ( !dd ) {
1704                 dd = new FetchData::DynamicData;
1705                 d->dynamics.insert( uid, dd );
1706             }
1707             dd->modseq = r->getBigint( "modseq" );
1708         }
1709     }
1710 
1711     if ( d->seenDeletedFetcher && !d->seenDeletedFetcher->done() )
1712         return;
1713 
1714     if ( d->flagFetcher && !d->flagFetcher->done() )
1715         return;
1716 
1717     if ( d->annotationFetcher && !d->annotationFetcher->done() )
1718         return;
1719 
1720     if ( d->modseqFetcher && !d->modseqFetcher->done() )
1721         return;
1722 
1723     bool ok = true;
1724     uint done = 0;
1725     while ( ok && !d->remaining.isEmpty() ) {
1726         uint uid = d->remaining.smallest();
1727         Message * m = d->messages.find( uid );
1728         if ( d->needsAddresses && !m->hasAddresses() )
1729             ok = false;
1730         if ( d->needsHeader && !m->hasHeaders() )
1731             ok = false;
1732         if ( d->needsPartNumbers && !m->hasBytesAndLines() )
1733             ok = false;
1734         if ( d->needsBody && !m->hasBodies() )
1735             ok = false;
1736         if ( ( d->rfc822size || d->internaldate ||
1737                d->databaseId || d->threadId ) && !m->hasTrivia() )
1738             ok = false;
1739         if ( ok ) {
1740             d->processed = uid;
1741             d->remaining.remove( uid );
1742             done++;
1743             waitFor( new ImapFetchResponse( s, this, uid ) );
1744         }
1745     }
1746 
1747     if ( !done )
1748         return;
1749     log( "Processed " + fn( done ) + " messages", Log::Debug );
1750     imap()->emitResponses();
1751 }
1752 
1753 
1754 /*! \class ImapFetchResponse fetch.h
1755 
1756     The ImapFetchResponse class models a single FETCH response. Its
1757     primary responsibity is to pick the right MSN at send time.
1758 */
1759 
1760 
1761 /*! Constructs a FETCH response for the message with \a uid with the
1762     data \a fetch fetched, if and only if \a s is active when it's
1763     time to send.
1764 */
1765 
ImapFetchResponse(ImapSession * s,Fetch * fetch,uint uid)1766 ImapFetchResponse::ImapFetchResponse( ImapSession * s,
1767                                       Fetch * fetch, uint uid )
1768     : ImapResponse( s ), f( fetch ), u( uid )
1769 {
1770 }
1771 
1772 
text() const1773 EString ImapFetchResponse::text() const
1774 {
1775     uint msn = session()->msn( u );
1776     if ( u && msn )
1777         return f->makeFetchResponse( f->message( u ), u, msn );
1778     return "";
1779 }
1780 
1781 
1782 /*! This reimplementation of setSent() frees up memory... that
1783     shouldn't be necessary when using garbage collection, but in this
1784     case it's important to remove messages from the data structures
1785     when they've been sent, so the collector sees that the memory can
1786     be reused. If we don't, then all of the messages occupy RAM until
1787     the last one has been sent.
1788 */
1789 
setSent()1790 void ImapFetchResponse::setSent()
1791 {
1792     f->forget( u );
1793     ImapResponse::setSent();
1794 }
1795 
1796 
1797 /*! This dangerous function makes the Fetch handler forget (part of)
1798     what it knows about \a uid. If Fetch has processed \a uid to
1799     completion, then forget() frees up memory for other use. To be
1800     used only by ImapFetchResponse::setSent().
1801 */
1802 
forget(uint uid)1803 void Fetch::forget( uint uid )
1804 {
1805     d->messages.remove( uid );
1806 }
1807 
1808 
1809 /*! Returns a pointer to the message with \a uid that this command has
1810     fetched or will fetch.
1811 */
1812 
message(uint uid) const1813 Message * Fetch::message( uint uid ) const
1814 {
1815     return d->messages.find( uid );
1816 }
1817 
1818 
1819 /*! Sends a query to retrieve all flags. */
1820 
sendFlagQuery()1821 void Fetch::sendFlagQuery()
1822 {
1823     d->seenDeletedFetcher = new Query(
1824         "select uid, seen, deleted from mailbox_messages "
1825         "where mailbox=$1 and uid=any($2)",
1826         this );
1827     d->seenDeletedFetcher->bind( 1, session()->mailbox()->id() );
1828     d->seenDeletedFetcher->bind( 2, d->set );
1829     enqueue( d->seenDeletedFetcher );
1830 
1831     d->flagFetcher = new Query(
1832         "select f.uid, fn.name from flags f "
1833         "join flag_names fn on (f.flag=fn.id) "
1834         "where f.mailbox=$1 and f.uid=any($2)",
1835         this );
1836     d->flagFetcher->bind( 1, session()->mailbox()->id() );
1837     d->flagFetcher->bind( 2, d->set );
1838     enqueue( d->flagFetcher );
1839 }
1840 
1841 
1842 /*! Sends a query to retrieve all annotations. */
1843 
sendAnnotationsQuery()1844 void Fetch::sendAnnotationsQuery()
1845 {
1846     d->annotationFetcher = new Query(
1847         "select a.uid, "
1848         "a.owner, a.value, an.name "
1849         "from annotations a "
1850         "join annotation_names an on (a.name=an.id) "
1851         "where a.mailbox=$1 and a.uid=any($2) "
1852         "order by an.name",
1853         this );
1854     d->annotationFetcher->bind( 1, session()->mailbox()->id() );
1855     d->annotationFetcher->bind( 2, d->set );
1856     enqueue( d->annotationFetcher );
1857 }
1858 
1859 
1860 /*! Sends a query to retrieve the modseq. */
1861 
sendModSeqQuery()1862 void Fetch::sendModSeqQuery()
1863 {
1864     d->modseqFetcher = new Query(
1865         "select uid, modseq "
1866         "from mailbox_messages "
1867         "where mailbox=$1 and uid=any($2)",
1868         this );
1869     d->modseqFetcher->bind( 1, session()->mailbox()->id() );
1870     d->modseqFetcher->bind( 2, d->set );
1871     enqueue( d->modseqFetcher );
1872 }
1873 
1874 
1875 /*! This helper enqueues \a q for execution, either directly of via a
1876     transaction.
1877 */
1878 
enqueue(Query * q)1879 void Fetch::enqueue( Query * q )
1880 {
1881     if ( transaction() )
1882         transaction()->enqueue( q );
1883     else
1884         q->execute();
1885 }
1886