22 #include <QtCore/QTimer>
24 #include <KDE/KLocalizedString>
27 #include "message_p.h"
28 #include "session_p.h"
32 class FetchJobPrivate :
public JobPrivate
36 : JobPrivate( session, name )
45 void parseBodyStructure(
const QByteArray &structure,
int &pos, KMime::Content *content );
46 void parsePart(
const QByteArray &structure,
int &pos, KMime::Content *content );
49 void skipLeadingSpaces(
const QByteArray &structure,
int &pos );
53 if ( pendingUids.isEmpty() ) {
57 if ( !pendingParts.isEmpty() ) {
58 emit q->partsReceived( selectedMailBox,
59 pendingUids, pendingParts );
60 emit q->partsReceived( selectedMailBox,
61 pendingUids, pendingAttributes,
64 if ( !pendingSizes.isEmpty() || !pendingFlags.isEmpty() ) {
65 emit q->headersReceived( selectedMailBox,
66 pendingUids, pendingSizes,
67 pendingFlags, pendingMessages );
68 emit q->headersReceived( selectedMailBox,
69 pendingUids, pendingSizes,
70 pendingAttributes, pendingFlags,
73 if ( !pendingMessages.isEmpty() ) {
74 emit q->messagesReceived( selectedMailBox,
75 pendingUids, pendingMessages );
76 emit q->messagesReceived( selectedMailBox,
77 pendingUids, pendingAttributes,
82 pendingMessages.clear();
86 pendingAttributes.clear();
107 using namespace KIMAP;
109 FetchJob::FetchScope::FetchScope():
110 mode( FetchScope::Content ),
116 FetchJob::FetchJob( Session *session )
117 : Job( *new FetchJobPrivate( this, session, i18n(
"Fetch" ) ) )
120 connect( &d->emitPendingsTimer, SIGNAL(timeout()),
121 this, SLOT(emitPendings()) );
124 FetchJob::~FetchJob()
144 d->uidBased = uidBased;
168 return d->gmailEnabled;
174 d->gmailEnabled = enabled;
202 void FetchJob::doStart()
206 QByteArray parameters = d->set.toImapSequenceSet()+
' ';
209 switch ( d->scope.mode ) {
211 if ( d->scope.parts.isEmpty() ) {
212 parameters +=
"(RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] FLAGS UID";
215 foreach (
const QByteArray &part, d->scope.parts ) {
216 parameters +=
"BODY.PEEK[" + part +
".MIME] ";
222 parameters +=
"(FLAGS UID";
225 parameters +=
"(BODYSTRUCTURE UID";
228 if ( d->scope.parts.isEmpty() ) {
229 parameters +=
"(BODY.PEEK[] UID";
232 foreach (
const QByteArray &part, d->scope.parts ) {
233 parameters +=
"BODY.PEEK[" + part +
"] ";
239 parameters +=
"(RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID";
242 if ( d->scope.parts.isEmpty() ) {
243 parameters +=
"(BODY.PEEK[] FLAGS UID";
245 parameters +=
"(BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)]";
246 foreach (
const QByteArray &part, d->scope.parts ) {
247 parameters +=
" BODY.PEEK[" + part +
".MIME] BODY.PEEK[" + part +
"]";
249 parameters +=
" FLAGS UID";
253 parameters +=
"(RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER] FLAGS UID";
257 if ( d->gmailEnabled ) {
258 parameters +=
" X-GM-LABELS X-GM-MSGID X-GM-THRID";
262 if ( d->scope.changedSince > 0 ) {
268 command =
"UID " + command;
271 d->emitPendingsTimer.start( 100 );
272 d->selectedMailBox = d->m_session->selectedMailBox();
273 d->tags << d->sessionInternal()->sendCommand( command, parameters );
276 void FetchJob::handleResponse(
const Message &response )
282 if ( !response.content.isEmpty() &&
283 d->tags.size() == 1 &&
284 d->tags.contains( response.content.first().toString() ) ) {
285 d->emitPendingsTimer.stop();
289 if ( handleErrorReplies( response ) == NotHandled ) {
290 if ( response.content.size() == 4 &&
291 response.content[2].toString() ==
"FETCH" &&
292 response.content[3].type() == Message::Part::List ) {
294 qint64
id = response.content[1].toString().toLongLong();
297 MessagePtr message(
new KMime::Message );
298 bool shouldParseMessage =
false;
307 kWarning() <<
"FETCH reply got truncated, skipping.";
311 if ( str ==
"UID" ) {
313 }
else if ( str ==
"RFC822.SIZE" ) {
314 d->pendingSizes[id] = it->toLongLong();
315 }
else if ( str ==
"INTERNALDATE" ) {
316 message->date()->setDateTime( KDateTime::fromString(
QLatin1String(*it), KDateTime::RFCDate ) );
317 }
else if ( str ==
"FLAGS" ) {
318 if ( ( *it ).startsWith(
'(' ) && ( *it ).endsWith(
')' ) ) {
322 d->pendingFlags[id] = str.
split(
' ' );
324 d->pendingFlags[id] << *it;
326 }
else if ( str ==
"X-GM-LABELS" ) {
327 d->pendingAttributes.
insert(
id, qMakePair<QByteArray, QVariant>(
"X-GM-LABELS", *it ) );
328 }
else if ( str ==
"X-GM-THRID" ) {
329 d->pendingAttributes.insert(
id, qMakePair<QByteArray, QVariant>(
"X-GM-THRID", *it ) );
330 }
else if ( str ==
"X-GM-MSGID" ) {
331 d->pendingAttributes.insert(
id, qMakePair<QByteArray, QVariant>(
"X-GM-MSGID", *it ) );
332 }
else if ( str ==
"BODYSTRUCTURE" ) {
334 d->parseBodyStructure( *it, pos, message.get() );
336 d->pendingMessages[id] = message;
339 while ( !( *it ).endsWith(
']' ) ) {
346 if ( ( index = str.
indexOf(
"HEADER" ) ) > 0 || ( index = str.
indexOf(
"MIME" ) ) > 0 ) {
347 if ( str[index-1] ==
'.' ) {
350 parts[partId] = ContentPtr(
new KMime::Content );
352 parts[partId]->setHead( *it );
353 parts[partId]->parse();
354 d->pendingParts[id] =
parts;
356 message->setHead( *it );
357 shouldParseMessage =
true;
360 if ( str ==
"BODY[]" ) {
361 message->setContent( KMime::CRLFtoLF( *it ) );
362 shouldParseMessage =
true;
364 d->pendingMessages[id] = message;
368 parts[partId] = ContentPtr(
new KMime::Content );
370 parts[partId]->setBody( *it );
371 parts[partId]->parse();
373 d->pendingParts[id] =
parts;
379 if ( shouldParseMessage ) {
389 d->pendingMessages[id] = message;
395 void FetchJobPrivate::parseBodyStructure(
const QByteArray &structure,
int &pos, KMime::Content *content)
397 skipLeadingSpaces( structure, pos );
399 if ( structure[pos] !=
'(' ) {
405 if ( structure[pos] !=
'(' ) {
407 parsePart( structure, pos, content );
409 content->contentType()->setMimeType(
"MULTIPART/MIXED" );
410 while ( pos < structure.
size() && structure[pos] ==
'(' ) {
411 KMime::Content *child =
new KMime::Content;
412 content->addContent( child );
413 parseBodyStructure( structure, pos, child );
417 QByteArray subType = parseString( structure, pos );
418 content->contentType()->setMimeType(
"MULTIPART/" + subType );
420 QByteArray parameters = parseSentence( structure, pos );
421 if ( parameters.
contains(
"BOUNDARY" ) ) {
422 content->contentType()->setBoundary( parameters.
remove( 0, parameters.
indexOf(
"BOUNDARY" ) + 11 ).split(
'\"' )[0] );
425 QByteArray disposition = parseSentence( structure, pos );
426 if ( disposition.
contains(
"INLINE" ) ) {
427 content->contentDisposition()->setDisposition( KMime::Headers::CDinline );
428 }
else if ( disposition.
contains(
"ATTACHMENT" ) ) {
429 content->contentDisposition()->setDisposition( KMime::Headers::CDattachment );
432 parseSentence( structure, pos );
436 while ( pos < structure.
size() && structure[pos] !=
')' ) {
437 skipLeadingSpaces( structure, pos );
438 parseSentence( structure, pos );
439 skipLeadingSpaces( structure, pos );
445 void FetchJobPrivate::parsePart(
const QByteArray &structure,
int &pos, KMime::Content *content )
447 if ( structure[pos] !=
'(' ) {
453 QByteArray mainType = parseString( structure, pos );
454 QByteArray subType = parseString( structure, pos );
456 content->contentType()->setMimeType( mainType +
'/' + subType );
458 parseSentence( structure, pos );
459 parseString( structure, pos );
461 content->contentDescription()->from7BitString( parseString( structure, pos ) );
463 parseString( structure, pos );
464 parseString( structure, pos );
465 parseString( structure, pos );
467 QByteArray disposition = parseSentence( structure, pos );
468 if ( disposition.
contains(
"INLINE" ) ) {
469 content->contentDisposition()->setDisposition( KMime::Headers::CDinline );
470 }
else if ( disposition.
contains(
"ATTACHMENT" ) ) {
471 content->contentDisposition()->setDisposition( KMime::Headers::CDattachment );
473 if ( ( content->contentDisposition()->disposition() == KMime::Headers::CDattachment ||
474 content->contentDisposition()->disposition() == KMime::Headers::CDinline ) &&
475 disposition.
contains(
"FILENAME" ) ) {
477 content->contentDisposition()->setFilename(
QLatin1String(filename) );
481 while ( pos < structure.
size() && structure[pos] !=
')' ) {
482 skipLeadingSpaces( structure, pos );
483 parseSentence( structure, pos );
484 skipLeadingSpaces( structure, pos );
493 skipLeadingSpaces( structure, pos );
495 if ( structure[pos] !=
'(' ) {
496 return parseString( structure, pos );
502 switch ( structure[pos] ) {
520 skipLeadingSpaces( structure, pos );
521 parseString( structure, pos );
522 skipLeadingSpaces( structure, pos );
525 }
while ( pos < structure.
size() && stack != 0 );
527 result = structure.
mid( start, pos - start );
536 skipLeadingSpaces( structure, pos );
539 bool foundSlash =
false;
542 if ( structure[pos] ==
'"' ) {
545 if ( structure[pos] ==
'\\' ) {
550 if ( structure[pos] ==
'"' ) {
551 result = structure.
mid( start + 1, pos - start - 1 );
559 if ( structure[pos] ==
' ' ||
560 structure[pos] ==
'(' ||
561 structure[pos] ==
')' ||
562 structure[pos] ==
'[' ||
563 structure[pos] ==
']' ||
564 structure[pos] ==
'\n' ||
565 structure[pos] ==
'\r' ||
566 structure[pos] ==
'"' ) {
569 if ( structure[pos] ==
'\\' ) {
575 result = structure.
mid( start, pos - start );
578 if ( result ==
"NIL" ) {
585 while ( result.
contains(
"\\\"" ) ) {
586 result.
replace(
"\\\"",
"\"" );
588 while ( result.
contains(
"\\\\" ) ) {
589 result.
replace(
"\\\\",
"\\" );
596 void FetchJobPrivate::skipLeadingSpaces(
const QByteArray &structure,
int &pos )
598 while ( pos < structure.
size() && structure[pos] ==
' ' ) {
603 #include "moc_fetchjob.cpp"
bool isUidBased() const
How to interpret the sequence set.
void setSequenceSet(const ImapSet &set)
Set which messages to fetch data for.
bool contains(const Key &key) const
Fetch the message content (the UID is also fetched)
QList< QByteArray > split(char sep) const
QByteArray trimmed() const
bool startsWith(const QByteArray &ba) const
Fetch the complete message.
KIMAP_DEPRECATED QMap< qint64, MessageFlags > flags() const
FetchScope scope() const
Specifies what data will be fetched.
Fetch message size (in octets), internal date of the message, flags, UID and all RFC822 headers...
int indexOf(char ch, int from) const
Fetch the MIME message body structure (the UID is also fetched)
Fetch RFC-2822 or MIME message headers.
Fetch message data from the server.
void setUidBased(bool uidBased)
Set how the sequence set should be interpreted.
QByteArray number(int n, int base)
Fetch the message MIME headers and the content of parts specified in the parts field.
Fetch the message flags (the UID is also fetched)
QByteArray & replace(int pos, int len, const char *after)
QByteArray mid(int pos, int len) const
KIMAP_DEPRECATED QMap< qint64, MessageParts > parts() const
KIMAP_DEPRECATED QMap< qint64, qint64 > sizes() const
qlonglong toLongLong(bool *ok, int base) const
Represents a set of natural numbers (1-> ) in a as compact as possible form.
void insert(int i, const T &value)
KIMAP_DEPRECATED QMap< qint64, MessagePtr > messages() const
KIMAP_DEPRECATED QMap< qint64, qint64 > uids() const
QByteArray toImapSequenceSet() const
Returns a IMAP-compatible QByteArray representation of this set.
bool contains(char ch) const
const_iterator constEnd() const
const_iterator constBegin() const
bool setGmailExtensionsEnabled() const
Returns whether Gmail support is enabled.
ImapSet sequenceSet() const
The messages that will be fetched.
QByteArray & remove(int pos, int len)
void setScope(const FetchScope &scope)
Sets what data should be fetched.
Used to indicate what message data should be fetched.
bool endsWith(const QByteArray &ba) const