1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
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"
36 static const char * legalAnnotationAttributes[] = {
52 : state( 0 ), peek( true ), processed( 0 ),
53 changedSince( 0 ), those( 0 ), findIds( 0 ),
54 deleted( 0 ), store( 0 ),
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 )
72 Map<Message> messages;
80 // we want to ask for...
93 List<Section> sections;
95 // and the sections imply that we...
99 bool needsPartNumbers;
108 DynamicData(): modseq( 0 ) {}
111 List<Annotation> annotations;
113 Map<DynamicData> dynamics;
114 Query * seenDeletedFetcher;
116 Query * annotationFetcher;
117 Query * modseqFetcher;
121 /*! \class Fetch fetch.h
123 Returns message data (RFC 3501, section 6.4.5, extended by RFC
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).
132 /*! Creates a new handler for FETCH if \a u is false, or for UID FETCH
136 Fetch::Fetch( bool u )
137 : Command(), d( new FetchData )
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.
153 If \a t is non-zero, the fetch operates within a subtransaction
157 Fetch::Fetch( bool f, bool a, const IntegerSet & set,
158 int64 limit, IMAP * i, Transaction * t )
159 : Command( i ), d( new FetchData )
167 d->changedSince = limit;
168 d->modseq = i->clientSupports( IMAP::Condstore );
170 setTransaction( t->subTransaction( this ) );
174 List<Command>::Iterator c( i->commands() );
175 while ( c && c->state() == Command::Retired )
177 while ( c && c->tag().isEmpty() )
179 if ( c && ( c->state() == Command::Finished ||
180 c->state() == Command::Executing ) ) {
181 log( "Inserting flag update for modseq>" + fn( limit ) +
182 " and UIDs " + set.set() + " before " +
183 c->tag() + " " + c->name() );
184 i->commands()->insert( c, this );
185 if ( c->group() == 1 || c->group() == 2 )
186 setGroup( c->group() );
189 log( "Appending flag update for modseq>" + fn( limit ) +
190 " and UIDs " + set.set() );
191 i->commands()->append( this );
194 setAllowedState( IMAP::Selected );
195 setState( Executing );
199 // fetch = "FETCH" SP set SP ("ALL" / "FULL" / "FAST" / fetch-att /
200 // "(" fetch-att *(SP fetch-att) ")")
201 // fetch-att = "ENVELOPE" / "FLAGS" / "INTERNALDATE" /
202 // "RFC822" [".HEADER" / ".SIZE" / ".TEXT"] /
203 // "BODY" ["STRUCTURE"] / "UID" /
204 // "BODY" [".PEEK"] section ["<" number "." nz-number ">"]
206 // section = "[" [section-spec] "]"
207 // section-spec = section-msgtext / (section-part ["." section-text])
208 // section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list /
210 // section-part = nz-number *("." nz-number)
211 // section-text = section-msgtext / "MIME"
212 // header-list = "(" header-fld-name *(SP header-fld-name) ")"
213 // header-fld-name = astring
219 d->set = set( !d->uid );
221 if ( nextChar() == '(' ) {
222 // "(" fetch-att *(SP fetch-att) ")")
224 parseAttribute( false );
225 while( nextChar() == ' ' ) {
227 parseAttribute( false );
232 // single fetch-att, or the macros
233 parseAttribute( true );
235 if ( present( " (" ) ) {
236 // RFC 4466 fetch-modifiers
237 parseFetchModifier();
238 while ( present( " " ) )
239 parseFetchModifier();
244 d->needsHeader = true;
245 d->needsAddresses = true;
247 if ( d->body || d->bodystructure ) {
248 // message/rfc822 body[structure] includes envelope in some
249 // cases, so we need both here too.
250 d->needsHeader = true;
251 d->needsAddresses = true;
252 // and we even need some data about the bodies
253 d->needsPartNumbers = true;
256 d->needsHeader = true; // Bodypart::asText() needs mime type etc
260 l.append( new EString( "Fetch <=" + fn( d->set.count() ) + " messages: " ) );
261 if ( d->needsAddresses )
262 l.append( "address" );
263 if ( d->needsHeader )
264 l.append( "header" );
269 if ( d->internaldate || d->rfc822size || d->databaseId || d->threadId )
270 l.append( "trivia" );
271 if ( d->needsPartNumbers )
272 l.append( "bytes/lines" );
274 l.append( "annotations" );
275 log( l.join( " " ) );
279 /*! This helper is responsible for parsing a single attribute from the
280 fetch arguments. If \a alsoMacro is true, this function parses a
281 macro as well as a single attribute.
284 void Fetch::parseAttribute( bool alsoMacro )
286 EString keyword = dotLetters( 3, 13 ).lower(); // UID/ALL, RFC822.HEADER
287 if ( alsoMacro && keyword == "all" ) {
288 // equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)
291 d->internaldate = true;
292 d->rfc822size = true;
294 else if ( alsoMacro && keyword == "full" ) {
295 // equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)
299 d->internaldate = true;
300 d->rfc822size = true;
302 else if ( alsoMacro && keyword == "fast" ) {
303 // equivalent to: (FLAGS INTERNALDATE RFC822.SIZE)
305 d->internaldate = true;
306 d->rfc822size = true;
308 else if ( keyword == "envelope" ) {
311 else if ( keyword == "flags" ) {
314 else if ( keyword == "internaldate" ) {
315 d->internaldate = true;
317 else if ( keyword == "rfc822" ) {
319 d->needsAddresses = true;
320 d->needsHeader = true;
322 Section * s = new Section;
324 d->sections.append( s );
326 else if ( keyword == "rfc822.header" ) {
327 d->needsAddresses = true;
328 d->needsHeader = true;
329 Section * s = new Section;
331 d->sections.append( s );
333 else if ( keyword == "rfc822.size" ) {
334 d->rfc822size = true;
336 else if ( keyword == "annotation" ) {
337 d->annotation = true;
341 else if ( keyword == "rfc822.text" ) {
343 d->needsHeader = true;
345 Section * s = new Section;
347 d->sections.append( s );
349 else if ( keyword == "body.peek" && nextChar() == '[' ) {
353 else if ( keyword == "body" ) {
354 if ( nextChar() == '[' ) {
361 // poor man's bodystructure
364 else if ( keyword == "bodystructure" ) {
365 d->bodystructure = true;
366 // like body, but with bells and whistles
368 else if ( keyword == "uid" ) {
371 else if ( keyword == "binary.peek" && nextChar() == '[' ) {
375 else if ( keyword == "binary" && nextChar() == '[' ) {
380 else if ( keyword == "binary.size" && nextChar() == '[' ) {
383 Section * s = d->sections.last();
386 error( Bad, "Fetching partial BINARY.SIZE is not meaningful" );
387 if ( s->part.isEmpty() )
388 d->rfc822size = true;
390 else if ( keyword == "modseq" ) {
393 else if ( keyword == "msgid" ) {
394 d->databaseId = true;
396 else if ( keyword == "thrid" ) {
400 error( Bad, "expected fetch attribute, saw word " + keyword );
405 /*! This utility function fetches at least \a min, at most \a max
406 characters, all of which must be a letter, a digit or a dot.
407 Consecutive dots ARE allowed.
410 EString Fetch::dotLetters( uint min, uint max )
412 EString r( parser()->dotLetters( min, max ) );
413 if ( !parser()->ok() )
414 error( Bad, parser()->error() );
419 /*! Uses the ImapParser \a ip to parse a section-text production, and
420 returns a pointer to a suitably constructed Section object. Upon
421 return, the ImapParser's cursor is advanced to point past the end
422 of the section-text. \a ip must not be 0; and the return value of
423 this function is also guaranteed to be non-zero.
425 If \a binary is false (the default), then the BINARY extensions of
426 RFC 3516 are summarily ignored.
428 If there were any parsing errors, Section::error will be non-empty.
431 Section * Fetch::parseSection( ImapParser * ip, bool binary )
433 Section * s = new Section;
436 // section-spec = section-msgtext / (section-part ["." section-text])
437 // section-msgtext = "HEADER" /
438 // "HEADER.FIELDS" [".NOT"] SP header-list /
440 // section-part = nz-number *("." nz-number)
441 // section-text = section-msgtext / "MIME"
443 // Parse a section-part.
445 if ( ip->nextChar() >= '0' && ip->nextChar() <= '9' ) {
447 part.append( fn( ip->nzNumber() ) );
448 while ( ip->nextChar() == '.' ) {
450 if ( ip->nextChar() >= '0' && ip->nextChar() <= '9' ) {
452 part.appendNumber( ip->nzNumber() );
462 // Parse any section-text.
463 EString item = ip->dotLetters( 0, 17 ).lower();
464 if ( binary && !item.isEmpty() ) {
465 s->error = "BINARY with section-text is not legal, saw " + item;
467 else if ( item.isEmpty() || item == "text" ) {
469 // and because we might need headers and addresses of subparts:
470 s->needsHeader = true;
471 s->needsAddresses = true;
473 else if ( item == "header" ) {
474 s->needsHeader = true;
475 s->needsAddresses = true;
477 else if ( item == "header.fields" ||
478 item == "header.fields.not" )
481 s->fields.append( new EString( ip->astring().headerCased() ) );
482 while ( ip->nextChar() == ' ' ) {
484 s->fields.append( new EString( ip->astring().headerCased() ) );
487 if ( item == "header.fields.not" ) {
488 // if we need to hand out "all other" fields...
489 s->needsAddresses = true;
490 s->needsHeader = true;
492 EStringList::Iterator i( s->fields );
493 while ( i && ( !s->needsAddresses || !s->needsHeader ) ) {
494 uint t = HeaderField::fieldType( *i );
495 if ( t > 0 && t <= HeaderField::LastAddressField )
496 s->needsAddresses = true;
498 s->needsHeader = true;
502 else if ( item == "mime" ) {
503 if ( s->part.isEmpty() )
504 s->error = "MIME requires a section-part.";
505 s->needsHeader = true;
509 "Expected text, header, header.fields etc, not " + item +
518 /*! Parses a bodypart description - the bit following "body[" in an
519 attribute. The cursor must be after '[' on entry, and is left
520 after the trailing ']'.
522 If \a binary is true, the parsed section will be sent using the
523 BINARY extension (RFC 3516). If not, it'll be sent using a normal
527 void Fetch::parseBody( bool binary )
529 Section * s = parseSection( parser(), binary );
530 if ( !s->error.isEmpty() ) {
531 error( Bad, s->error );
537 // Parse any range specification.
538 if ( nextChar() == '<' ) {
541 s->offset = number();
543 s->length = nzNumber();
547 d->sections.append( s );
548 if ( s->needsAddresses )
549 d->needsAddresses = true;
550 if ( s->needsHeader )
551 d->needsHeader = true;
557 void record( EStringList & l, Dict<void> & d, const EString & a )
559 if ( !d.contains( a.lower() ) )
560 l.append( new EString( a ) );
561 d.insert( a.lower(), (void *)1 );
565 /*! Parses the entries and attributes from an ANNOTATION fetch-att.
566 Expects the cursor to be on the first parenthesis, and advances
567 it to past the last one.
570 void Fetch::parseAnnotation()
575 // Simplified ABNF from draft-ietf-imapext-annotate-15:
577 // fetch-att =/ "ANNOTATION" SP "(" entries SP attribs ")"
578 // entries = list-mailbox /
579 // "(" list-mailbox *(SP list-mailbox) ")"
580 // attribs = astring /
581 // "(" astring *(SP astring) ")"
586 if ( nextChar() == '(' ) {
593 d->entries.append( new EString( parser()->listMailbox() ) );
594 if ( !parser()->ok() )
595 error( Bad, parser()->error() );
598 if ( nextChar() == ')' ) {
614 if ( nextChar() == '(' ) {
623 EString a( astring() );
625 // XXX: This check (and the legalAnnotationAttributes table) is
626 // duplicated in Search::parseKey(). But where should a common
627 // attribute-checking function live?
629 while ( ::legalAnnotationAttributes[i] &&
630 a != ::legalAnnotationAttributes[i] )
632 if ( !::legalAnnotationAttributes[i] )
633 error( Bad, "Unknown annotation attribute: " + a );
635 if ( a.endsWith( ".priv" ) || a.endsWith( ".shared" ) ) {
636 record( d->attribs, attribs, a );
639 record( d->attribs, attribs, a + ".priv" );
640 record( d->attribs, attribs, a + ".shared" );
644 if ( nextChar() == ')' ) {
661 void Fetch::execute()
663 if ( state() != Executing )
666 ImapSession * s = session();
668 if ( !d->peek && s->readOnly() )
671 if ( d->state == 0 ) {
672 if ( !transaction() &&
674 ( d->modseq && ( d->flags || d->annotation || d->vanished ) ) ) )
675 setTransaction( new Transaction( this ) );
677 if ( d->vanished && d->changedSince > 0 && !d->deleted ) {
678 d->deleted = new Query( "select uid from deleted_messages "
679 "where mailbox=$1 and modseq>$2 "
682 d->deleted->bind( 1, s->mailbox()->id() );
683 d->deleted->bind( 2, d->changedSince );
684 IntegerSet s( d->set );
685 s.remove( session()->messages() );
686 d->deleted->bind( 3, s );
687 transaction()->enqueue( d->deleted );
690 Mailbox * mb = s->mailbox();
692 d->set = d->set.intersection( session()->messages() );
693 if ( d->changedSince ) {
694 d->those = new Query( "select uid, message "
695 "from mailbox_messages "
696 "where mailbox=$1 and uid=any($2) "
699 d->those->bind( 1, s->mailbox()->id() );
700 d->those->bind( 2, d->set );
702 else if ( d->modseq ||
703 d->needsAddresses || d->needsHeader ||
704 d->needsBody || d->needsPartNumbers ||
705 d->rfc822size || d->internaldate ||
706 d->databaseId || d->threadId ) {
708 IntegerSet s( d->set );
709 while ( !s.isEmpty() ) {
710 uint uid = s.smallest();
712 Message * m = MessageCache::find( mb, uid );
714 d->messages.insert( uid, m );
715 if ( !m || !m->databaseId() || d->modseq )
718 if ( !r.isEmpty() ) {
719 d->those = new Query( "select uid, message "
720 "from mailbox_messages "
721 "where mailbox=$1 and uid=any($2)",
723 d->those->bind( 1, session()->mailbox()->id() );
724 d->those->bind( 2, d->set );
728 if ( d->changedSince )
729 d->those->bind( 3, d->changedSince );
732 // if we aren't peeking, then we have to lock
733 // the mailbox before we lock the messages,
734 // otherwise we might deadlock with Store or
736 Query * q = new Query( "select nextmodseq "
738 "where id=$1 for update", 0 );
739 q->bind( 1, mb->id() );
740 transaction()->enqueue( q );
742 EString s = d->those->string();
743 s.append( " order by uid for update" );
744 d->those->setString( s );
749 transaction()->execute();
754 while ( d->those->hasResults() ) {
755 r = d->those->nextRow();
756 uint uid = r->getInt( "uid" );
758 Message * m = d->messages.find( uid );
760 m = MessageCache::provide( mb, uid );
761 d->messages.insert( uid, m );
763 m->setDatabaseId( r->getInt( "message" ) );
764 if ( d->modseq || d->flags || d->annotation ) {
765 FetchData::DynamicData * dd = new FetchData::DynamicData;
766 d->dynamics.insert( uid, dd );
769 if ( !d->those->done() )
773 IntegerSet r( d->set );
774 while ( !r.isEmpty() ) {
775 uint uid = r.smallest();
777 d->dynamics.insert( uid, new FetchData::DynamicData );
783 if ( d->deleted && d->deleted->done() ) {
785 while ( d->deleted->hasResults() ) {
786 Row * r = d->deleted->nextRow();
787 uint uid = r->getInt( "uid" );
790 if ( !vanished.isEmpty() )
791 respond( "VANISHED (EARLIER) " + vanished.set() );
795 if ( d->state == 1 ) {
796 if ( group() == 2 ) // then RFC 2180 section 4.1.2 applies
797 d->expunged = s->expunged().intersection( d->set );
799 d->remaining = d->set;
801 if ( d->set.isEmpty() ) {
804 transaction()->commit();
808 if ( d->state == 2 ) {
814 List<Command>::Iterator c = imap()->commands()->find( this );
816 d->store = new Store( imap(), d->set, d->flags,
818 d->store->setState( Executing );
819 imap()->commands()->insert( c, d->store );
820 // should we feed the Store a subtransaction, if
821 // we're using one? I don't know.
825 if ( d->store && d->store->state() == Executing )
831 if ( d->state == 3 ) {
837 sendAnnotationsQuery();
841 transaction()->commit();
849 if ( d->processed < d->set.largest() )
852 if ( !d->expunged.isEmpty() ) {
853 s->recordExpungedFetch( d->expunged );
854 error( No, "UID(s) " + d->expunged.set() + " has/have been expunged" );
860 /*! Issues queries to resolve any questions this FETCH needs to answer.
863 void Fetch::sendFetchQueries()
865 bool haveAddresses = true;
866 bool haveHeader = true;
867 bool haveBody = true;
868 bool havePartNumbers = true;
869 bool haveTrivia = true;
871 List<Message> * l = new List<Message>;
873 Map<Message>::Iterator i( d->messages );
877 if ( !m->hasAddresses() )
878 haveAddresses = false;
879 if ( !m->hasHeaders() )
881 if ( !m->hasBytesAndLines() )
882 havePartNumbers = false;
883 if ( !m->hasBodies() )
885 if ( !m->hasTrivia() )
890 Fetcher * f = new Fetcher( l, this, imap() );
891 if ( d->needsAddresses && !haveAddresses )
892 f->fetch( Fetcher::Addresses );
893 if ( d->needsHeader && !haveHeader )
894 f->fetch( Fetcher::OtherHeader );
895 if ( d->needsBody && !haveBody )
896 f->fetch( Fetcher::Body );
897 if ( ( d->rfc822size || d->internaldate ||
898 d->databaseId || d->threadId ) && !haveTrivia )
899 f->fetch( Fetcher::Trivia );
900 if ( d->needsPartNumbers && !havePartNumbers )
901 f->fetch( Fetcher::PartNumbers );
906 /*! This function returns the text of that portion of the Message \a m
907 that is described by the Section \a s. It is publicly available so
908 that Append may use it for CATENATE.
910 If \a unicodable is true, the result may contain unquoted unicode.
913 EString Fetch::sectionData( Section * s, Message * m, bool unicodable )
917 if ( s->id == "rfc822" ) {
918 item = s->id.upper();
919 data = m->rfc822( !unicodable );
922 else if ( s->id == "mime" ||
923 s->id == "rfc822.header" ||
924 s->id.startsWith( "header" ) ) {
925 bool rfc822 = s->id == "rfc822.header";
926 bool fields = s->id.startsWith( "header.fields" );
927 bool exclude = s->id.endsWith( ".not" );
929 data.reserve( 80 * s->fields.count() ); // suboptimal for .not, but...
931 Header * hdr = m->header();
932 if ( !s->part.isEmpty() ) {
933 Bodypart * bp = m->bodypart( s->part, false );
934 if ( bp && bp->header() )
940 List< HeaderField >::Iterator it;
942 it = hdr->fields()->first();
944 bool include = false;
949 bool listed = s->fields.find( it->name() );
956 EString n = it->name().headerCased();
959 data.append( it->rfc822( !unicodable ) );
960 data.append( "\r\n" );
965 item = s->id.upper();
967 if ( !s->part.isEmpty() )
968 item = s->part + "." + item;
969 item = "BODY[" + item;
971 item.append( " (" + s->fields.join( " " ) + ")" );
974 data.append( "\r\n" );
977 else if ( s->id == "rfc822.text" ) {
978 item = s->id.upper();
979 data = m->body( !unicodable );
982 else if ( s->id == "text" ) {
983 if ( s->part.isEmpty() ) {
985 data = m->body( !unicodable );
988 item = s->part + ".TEXT";
989 Bodypart *bp = m->bodypart( s->part, false );
990 if ( bp && bp->message() )
991 data = bp->message()->body( !unicodable );
993 item = "BODY[" + item + "]";
996 else if ( ( s->id.isEmpty() || s->id == "size" ) &&
999 if ( s->id == "size" ) {
1000 item = "BINARY.SIZE[]";
1001 data = fn( m->rfc822Size() );
1005 data = m->rfc822( !unicodable );
1009 else if ( s->id.isEmpty() || s->id == "size" ) {
1011 Bodypart * bp = m->bodypart( s->part, false );
1013 // nonexistent part number
1016 // should we report an error? the fetch responses will be
1018 // error( No, "No such bodypart: " + s->part );
1020 else if ( bp->message() ) {
1021 // message/rfc822 part
1022 data = bp->message()->rfc822( !unicodable );
1024 else if ( bp->children()->isEmpty() ) {
1028 ContentType * ct = bp->contentType();
1029 if ( !ct || ct->type() == "text" ) {
1032 if ( data.isEmpty() ) {
1036 Codec * c = new Utf8Codec;
1037 text = c->toUnicode( data );
1042 c = Codec::byName( ct->parameter( "charset" ) );
1043 if ( !c && ct && ct->subtype() == "html" )
1044 c = new Iso88591Codec;
1047 data = c->fromUnicode( text );
1050 data = data.encoded( bp->contentTransferEncoding(), 70 );
1053 // nonleaf part. probably wrong - this might use the wrong
1054 // content-transfer-encoding.
1055 data = bp->asText( !unicodable );
1061 if ( s->id == "size" ) {
1062 item = "BINARY.SIZE";
1063 data = fn( data.length() );
1066 item = item + "[" + s->part + "]";
1070 item.append( "<" + fn( s->offset ) + ">" );
1071 data = data.mid( s->offset, s->length );
1079 /* This function returns the response data for an element in
1080 d->sections, to be included in the FETCH response by
1081 fetchResponses() below. If \a unicode is false, the result will be
1082 downgraded rather than contain unicode.
1085 static EString sectionResponse( Section * s, Message * m, bool unicode )
1087 EString data( Fetch::sectionData( s, m, unicode ) );
1088 if ( !s->item.startsWith( "BINARY.SIZE" ) )
1089 data = Command::imapQuoted( data, Command::NString );
1091 r.reserve( data.length() + s->item.length() + 1 );
1092 r.append( s->item );
1099 /*! Emits a single FETCH response for the message \a m, which is
1100 trusted to have UID \a uid and MSN \a msn.
1102 The message must have all necessary content.
1105 EString Fetch::makeFetchResponse( Message * m, uint uid, uint msn )
1109 l.append( "UID " + fn( uid ) );
1110 if ( d->databaseId )
1111 l.append( "MSGID " + fn( m->databaseId() ) );
1113 l.append( "THRID " + fn( m->threadId() ) );
1114 if ( d->rfc822size )
1115 l.append( "RFC822.SIZE " + fn( m->rfc822Size() ) );
1117 l.append( "FLAGS (" + flagList( uid ) + ")" );
1118 if ( d->internaldate )
1119 l.append( "INTERNALDATE " + internalDate( m ) );
1121 l.append( "ENVELOPE " + envelope( m ) );
1123 l.append( "BODY " + bodyStructure( m, false ) );
1124 if ( d->bodystructure )
1125 l.append( "BODYSTRUCTURE " + bodyStructure( m, true ) );
1126 if ( d->annotation )
1127 l.append( "ANNOTATION " + annotation( imap()->user(), uid,
1128 d->entries, d->attribs ) );
1130 FetchData::DynamicData * dd = d->dynamics.find( uid );
1131 if ( dd && dd->modseq )
1132 l.append( "MODSEQ (" + fn( dd->modseq ) + ")" );
1135 List< Section >::Iterator it( d->sections );
1136 bool unicode = imap()->clientSupports( IMAP::Unicode );
1138 l.append( sectionResponse( it, m, unicode ) );
1143 EString payload = l.join( " " );
1144 r.reserve( payload.length() + 30 );
1145 r.appendNumber( msn );
1146 r.append( " FETCH (" );
1147 r.append( payload );
1153 /*! Returns a string containing all the flags that are set for the
1154 message with \a uid.
1157 EString Fetch::flagList( uint uid )
1161 FetchData::DynamicData * dd = d->dynamics.find( uid );
1163 if ( session()->isRecent( uid ) )
1164 dd->flags.insert( "\\recent", new EString( "\\Recent" ) );
1165 Dict<EString>::Iterator i( dd->flags );
1172 return r.join( " " );
1176 /*! Returns the internaldate of \a m in IMAP format. */
1178 EString Fetch::internalDate( Message * m )
1181 date.setUnixTime( m->internalDate() );
1182 return "\"" + date.imap() + "\"";
1186 static EString hf( Header * f, HeaderField::Type t, bool unicodable )
1188 List<Address> * a = f->addresses( t );
1189 if ( !a || a->isEmpty() )
1194 List<Address>::Iterator it( a );
1197 if ( it->type() == Address::EmptyGroup ) {
1198 r.append( "NIL NIL " );
1199 r.append( Command::imapQuoted( it->name( !unicodable ),
1200 Command::NString ) );
1201 r.append( " NIL)(NIL NIL NIL NIL" );
1202 } else if ( it->type() == Address::Local ||
1203 it->type() == Address::Normal ) {
1204 UString u = it->uname();
1206 if ( u.isAscii() || unicodable )
1207 eu = u.simplified().utf8();
1209 eu = HeaderField::encodePhrase( u );
1210 r.append( Command::imapQuoted( eu, Command::NString ) );
1211 r.append( " NIL " );
1213 ( it->localpart().isAscii() && it->domain().isAscii() ) ) {
1214 r.append( Command::imapQuoted( it->localpart().utf8(),
1215 Command::NString ) );
1217 if ( it->domain().isEmpty() )
1218 r.append( "\" \"" ); // RFC 3501, page 77 near bottom
1220 r.append( Command::imapQuoted( it->domain().utf8(),
1221 Command::NString ) );
1224 r.append( "noreply unicode-needed.invalid" );
1235 /*! Returns the IMAP envelope for \a m. */
1237 EString Fetch::envelope( Message * m )
1239 Header * h = m->header();
1241 // envelope = "(" env-date SP env-subject SP env-from SP
1242 // env-sender SP env-reply-to SP env-to SP env-cc SP
1243 // env-bcc SP env-in-reply-to SP env-message-id ")"
1249 Date * date = h->date();
1251 r.append( imapQuoted( date->rfc822(), NString ) );
1256 r.append( imapQuoted( h->subject(), NString ) + " " );
1257 bool unicode = imap()->clientSupports( IMAP::Unicode );
1258 r.append( hf( h, HeaderField::From, unicode ) );
1259 r.append( hf( h, HeaderField::Sender, unicode ) );
1260 r.append( hf( h, HeaderField::ReplyTo, unicode ) );
1261 r.append( hf( h, HeaderField::To, unicode ) );
1262 r.append( hf( h, HeaderField::Cc, unicode ) );
1263 r.append( hf( h, HeaderField::Bcc, unicode ) );
1264 r.append( imapQuoted( h->inReplyTo(), NString ) + " " );
1265 r.append( imapQuoted( h->messageId(), NString ) );
1272 static EString parameterEString( MimeField *mf )
1277 p = mf->parameters();
1278 if ( !mf || !p || p->isEmpty() )
1282 EStringList::Iterator it( p );
1284 l.append( Command::imapQuoted( *it ) );
1285 l.append( Command::imapQuoted( mf->parameter( *it ) ) );
1289 EString r = l.join( " " );
1296 static EString dispositionEString( ContentDisposition *cd )
1302 switch ( cd->disposition() ) {
1303 case ContentDisposition::Inline:
1306 case ContentDisposition::Attachment:
1311 return "(\"" + s + "\" " + parameterEString( cd ) + ")";
1315 static EString languageEString( ContentLanguage *cl )
1321 const EStringList *l = cl->languages();
1322 EStringList::Iterator it( l );
1324 m.append( Command::imapQuoted( *it ) );
1328 if ( l->count() == 1 )
1330 EString r = m.join( " " );
1337 /*! Returns either the IMAP BODY or BODYSTRUCTURE production for \a
1338 m. If \a extended is true, BODYSTRUCTURE is returned. If it's
1342 EString Fetch::bodyStructure( Multipart * m, bool extended )
1346 Header * hdr = m->header();
1347 ContentType * ct = hdr->contentType();
1349 if ( ct && ct->type() == "multipart" ) {
1350 EStringList children;
1351 List< Bodypart >::Iterator it( m->children() );
1353 children.append( bodyStructure( it, extended ) );
1357 r = children.join( "" );
1360 r.append( imapQuoted( ct->subtype() ));
1364 r.append( parameterEString( ct ) );
1366 r.append( dispositionEString( hdr->contentDisposition() ) );
1368 r.append( languageEString( hdr->contentLanguage() ) );
1370 r.append( imapQuoted( hdr->contentLocation(), NString ) );
1376 r = singlePartStructure( (Bodypart*)m, extended );
1383 /*! Returns the structure of the single-part bodypart \a mp.
1385 If \a extended is true, extended BODYSTRUCTURE attributes are
1389 EString Fetch::singlePartStructure( Multipart * mp, bool extended )
1396 ContentType * ct = mp->header()->contentType();
1399 l.append( imapQuoted( ct->type() ) );
1400 l.append( imapQuoted( ct->subtype() ) );
1403 // XXX: What happens to the default if this is a /digest?
1404 l.append( "\"text\"" );
1405 l.append( "\"plain\"" );
1408 l.append( parameterEString( ct ) );
1409 l.append( imapQuoted( mp->header()->messageId( HeaderField::ContentId ),
1411 l.append( imapQuoted( mp->header()->contentDescription(), NString ) );
1413 if ( mp->header()->contentTransferEncoding() ) {
1414 switch( mp->header()->contentTransferEncoding()->encoding() ) {
1415 case EString::Binary:
1416 l.append( "\"8BIT\"" ); // hm. is this entirely sound?
1418 case EString::Uuencode:
1419 l.append( "\"x-uuencode\"" ); // should never happen
1421 case EString::Base64:
1422 l.append( "\"BASE64\"" );
1425 l.append( "\"QUOTED-PRINTABLE\"" );
1430 l.append( "\"7BIT\"" );
1434 if ( mp->isBodypart() )
1436 else if ( mp->isMessage() )
1437 bp = ((Message*)mp)->children()->first();
1440 l.append( fn( bp->numEncodedBytes() ) );
1441 if ( ct && ct->type() == "message" && ct->subtype() == "rfc822" ) {
1442 // body-type-msg = media-message SP body-fields SP envelope
1443 // SP body SP body-fld-lines
1444 l.append( envelope( bp->message() ) );
1445 l.append( bodyStructure( bp->message(), extended ) );
1446 l.append( fn ( bp->numEncodedLines() ) );
1448 else if ( !ct || ct->type() == "text" ) {
1449 // body-type-text = media-text SP body-fields SP body-fld-lines
1450 l.append( fn( bp->numEncodedLines() ) );
1456 HeaderField *f = mp->header()->field( HeaderField::ContentMd5 );
1458 md5 = f->rfc822( false );
1460 l.append( imapQuoted( md5, NString ) );
1461 l.append( dispositionEString( mp->header()->contentDisposition() ) );
1462 l.append( languageEString( mp->header()->contentLanguage() ) );
1463 l.append( imapQuoted( mp->header()->contentLocation(), NString ) );
1466 EString r = l.join( " " );
1473 /*! Returns the IMAP ANNOTATION production for the message with \a
1474 uid, from the point of view of \a u (0 for no user, only public
1475 annotations). \a entrySpecs is a list of the entries to be
1476 matched, each of which can contain the * and % wildcards. \a
1477 attributes is a list of attributes to be returned (each including
1478 the .priv or .shared suffix).
1481 EString Fetch::annotation( User * u, uint uid,
1482 const EStringList & entrySpecs,
1483 const EStringList & attributes )
1485 FetchData::DynamicData * dd = d->dynamics.find( uid );
1487 setRespTextCode( "SERVERBUG" );
1491 typedef Dict< EString > AttributeDict;
1492 Dict< AttributeDict > entries;
1494 EStringList entryNames;
1499 List<Annotation>::Iterator i( dd->annotations );
1504 EString entry( a->entryName() );
1505 bool entryWanted = false;
1506 EStringList::Iterator e( entrySpecs );
1507 while ( e && !entryWanted ) {
1509 if ( Mailbox::match( c.toUnicode( *e ), 0,
1510 c.toUnicode( entry ), 0 ) == 2 ) {
1511 if ( !entries.find( entry ) )
1512 entryNames.append( entry );
1518 if ( ( a->ownerId() == 0 || a->ownerId() == user ) &&
1521 AttributeDict * atts = entries.find( entry );
1523 atts = new AttributeDict;
1524 entries.insert( entry, atts );
1527 const char * suffix = ".shared";
1531 EString * v = new EString( a->value() );
1532 EString * s = new EString( fn( v->length() ) );
1534 atts->insert( EString( "value" ) + suffix, v );
1535 atts->insert( EString( "size" ) + suffix, s );
1540 EStringList::Iterator e( entryNames );
1542 EString entry( *e );
1545 EStringList::Iterator a( attributes );
1547 EString attrib( *a );
1549 EString * value = 0;
1550 AttributeDict * atts = entries.find( entry );
1552 value = atts->find( attrib );
1554 EString tmp = attrib;
1557 tmp.append( imapQuoted( *value ) );
1558 else if ( attrib.startsWith( "size." ) )
1559 tmp.append( "\"0\"" );
1561 tmp.append( "NIL" );
1567 if ( !l.isEmpty() ) {
1569 r.append( l.join( " " ) );
1582 /*! Parses a single RFC 4466 fetch-modifier. At the moment RFC 4551
1583 and RFC 7162 are supported.
1586 void Fetch::parseFetchModifier()
1588 EString name = atom().lower();
1589 if ( name == "changedsince" ) {
1591 d->changedSince = number();
1594 else if ( name == "vanished" ) {
1598 error( Bad, "Unknown fetch modifier: " + name );
1603 /*! Retrieves completed messages and builds ImapFetchResponse objects.
1606 void Fetch::pickup()
1608 ImapSession * s = (ImapSession *)imap()->session();
1612 if ( d->seenDeletedFetcher ) {
1613 EString seenl( "\\seen" );
1614 EString * seen = new EString( "\\Seen" );
1615 EString deletedl( "\\deleted" );
1616 EString * deleted = new EString( "\\Deleted" );
1617 while ( d->seenDeletedFetcher->hasResults() ) {
1618 Row * r = d->seenDeletedFetcher->nextRow();
1619 uint uid = r->getInt( "uid" );
1620 FetchData::DynamicData * dd = d->dynamics.find( uid );
1622 dd = new FetchData::DynamicData;
1623 d->dynamics.insert( uid, dd );
1625 if ( r->getBoolean( "seen" ) )
1626 dd->flags.insert( seenl, seen );
1627 if ( r->getBoolean( "deleted" ) )
1628 dd->flags.insert( deletedl, deleted );
1630 while ( d->flagFetcher->hasResults() ) {
1631 Row * r = d->flagFetcher->nextRow();
1632 uint uid = r->getInt( "uid" );
1633 FetchData::DynamicData * dd = d->dynamics.find( uid );
1635 dd = new FetchData::DynamicData;
1636 d->dynamics.insert( uid, dd );
1638 EString f = r->getEString( "name" );
1640 dd->flags.insert( f.lower(), new EString( f ) );
1642 if ( d->seenDeletedFetcher->done() &&
1643 d->flagFetcher->done() ) {
1644 d->seenDeletedFetcher = 0;
1649 if ( d->annotationFetcher ) {
1650 while ( d->annotationFetcher->hasResults() ) {
1651 Row * r = d->annotationFetcher->nextRow();
1652 uint uid = r->getInt( "uid" );
1653 FetchData::DynamicData * dd = d->dynamics.find( uid );
1655 dd = new FetchData::DynamicData;
1656 d->dynamics.insert( uid, dd );
1659 EString n = r->getEString( "name" );
1660 EString v( r->getEString( "value" ) );
1663 if ( !r->isNull( "owner" ) )
1664 owner = r->getInt( "owner" );
1666 dd->annotations.append( new Annotation( n, v, owner ) );
1670 if ( d->modseqFetcher ) {
1671 while ( d->modseqFetcher->hasResults() ) {
1672 Row * r = d->modseqFetcher->nextRow();
1673 uint uid = r->getInt( "uid" );
1674 FetchData::DynamicData * dd = d->dynamics.find( uid );
1676 dd = new FetchData::DynamicData;
1677 d->dynamics.insert( uid, dd );
1679 dd->modseq = r->getBigint( "modseq" );
1683 if ( d->seenDeletedFetcher && !d->seenDeletedFetcher->done() )
1686 if ( d->flagFetcher && !d->flagFetcher->done() )
1689 if ( d->annotationFetcher && !d->annotationFetcher->done() )
1692 if ( d->modseqFetcher && !d->modseqFetcher->done() )
1697 while ( ok && !d->remaining.isEmpty() ) {
1698 uint uid = d->remaining.smallest();
1699 Message * m = d->messages.find( uid );
1700 if ( d->needsAddresses && !m->hasAddresses() )
1702 if ( d->needsHeader && !m->hasHeaders() )
1704 if ( d->needsPartNumbers && !m->hasBytesAndLines() )
1706 if ( d->needsBody && !m->hasBodies() )
1708 if ( ( d->rfc822size || d->internaldate ||
1709 d->databaseId || d->threadId ) && !m->hasTrivia() )
1713 d->remaining.remove( uid );
1715 waitFor( new ImapFetchResponse( s, this, uid ) );
1721 log( "Processed " + fn( done ) + " messages", Log::Debug );
1722 imap()->emitResponses();
1726 /*! \class ImapFetchResponse fetch.h
1728 The ImapFetchResponse class models a single FETCH response. Its
1729 primary responsibity is to pick the right MSN at send time.
1733 /*! Constructs a FETCH response for the message with \a uid with the
1734 data \a fetch fetched, if and only if \a s is active when it's
1738 ImapFetchResponse::ImapFetchResponse( ImapSession * s,
1739 Fetch * fetch, uint uid )
1740 : ImapResponse( s ), f( fetch ), u( uid )
1745 EString ImapFetchResponse::text() const
1747 uint msn = session()->msn( u );
1749 return f->makeFetchResponse( f->message( u ), u, msn );
1754 /*! This reimplementation of setSent() frees up memory... that
1755 shouldn't be necessary when using garbage collection, but in this
1756 case it's important to remove messages from the data structures
1757 when they've been sent, so the collector sees that the memory can
1758 be reused. If we don't, then all of the messages occupy RAM until
1759 the last one has been sent.
1762 void ImapFetchResponse::setSent()
1765 ImapResponse::setSent();
1769 /*! This dangerous function makes the Fetch handler forget (part of)
1770 what it knows about \a uid. If Fetch has processed \a uid to
1771 completion, then forget() frees up memory for other use. To be
1772 used only by ImapFetchResponse::setSent().
1775 void Fetch::forget( uint uid )
1777 d->messages.remove( uid );
1781 /*! Returns a pointer to the message with \a uid that this command has
1782 fetched or will fetch.
1785 Message * Fetch::message( uint uid ) const
1787 return d->messages.find( uid );
1791 /*! Sends a query to retrieve all flags. */
1793 void Fetch::sendFlagQuery()
1795 d->seenDeletedFetcher = new Query(
1796 "select uid, seen, deleted from mailbox_messages "
1797 "where mailbox=$1 and uid=any($2)",
1799 d->seenDeletedFetcher->bind( 1, session()->mailbox()->id() );
1800 d->seenDeletedFetcher->bind( 2, d->set );
1801 enqueue( d->seenDeletedFetcher );
1803 d->flagFetcher = new Query(
1804 "select f.uid, fn.name from flags f "
1805 "join flag_names fn on (f.flag=fn.id) "
1806 "where f.mailbox=$1 and f.uid=any($2)",
1808 d->flagFetcher->bind( 1, session()->mailbox()->id() );
1809 d->flagFetcher->bind( 2, d->set );
1810 enqueue( d->flagFetcher );
1814 /*! Sends a query to retrieve all annotations. */
1816 void Fetch::sendAnnotationsQuery()
1818 d->annotationFetcher = new Query(
1820 "a.owner, a.value, an.name "
1821 "from annotations a "
1822 "join annotation_names an on (a.name=an.id) "
1823 "where a.mailbox=$1 and a.uid=any($2) "
1826 d->annotationFetcher->bind( 1, session()->mailbox()->id() );
1827 d->annotationFetcher->bind( 2, d->set );
1828 enqueue( d->annotationFetcher );
1832 /*! Sends a query to retrieve the modseq. */
1834 void Fetch::sendModSeqQuery()
1836 d->modseqFetcher = new Query(
1837 "select uid, modseq "
1838 "from mailbox_messages "
1839 "where mailbox=$1 and uid=any($2)",
1841 d->modseqFetcher->bind( 1, session()->mailbox()->id() );
1842 d->modseqFetcher->bind( 2, d->set );
1843 enqueue( d->modseqFetcher );
1847 /*! This helper enqueues \a q for execution, either directly of via a
1851 void Fetch::enqueue( Query * q )
1853 if ( transaction() )
1854 transaction()->enqueue( q );