22#include <QtCore/QTimer>
24#include <KDE/KLocalizedString>
32 class FetchJobPrivate :
public JobPrivate
35 FetchJobPrivate(
FetchJob *job, Session *session,
const QString& name )
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 );
47 QByteArray parseString(
const QByteArray &structure,
int &pos );
48 QByteArray parseSentence(
const QByteArray &structure,
int &pos );
49 void skipLeadingSpaces(
const QByteArray &structure,
int &pos );
53 if ( pendingUids.isEmpty() ) {
57 if ( !pendingParts.isEmpty() ) {
59 pendingUids, pendingParts );
61 pendingUids, pendingAttributes,
64 if ( !pendingSizes.isEmpty() || !pendingFlags.isEmpty() ) {
66 pendingUids, pendingSizes,
67 pendingFlags, pendingMessages );
69 pendingUids, pendingSizes,
70 pendingAttributes, pendingFlags,
73 if ( !pendingMessages.isEmpty() ) {
75 pendingUids, pendingMessages );
77 pendingUids, pendingAttributes,
82 pendingMessages.clear();
86 pendingAttributes.clear();
94 QString selectedMailBox;
97 QTimer emitPendingsTimer;
98 QMap<qint64, MessagePtr> pendingMessages;
99 QMap<qint64, MessageParts> pendingParts;
100 QMap<qint64, MessageFlags> pendingFlags;
101 QMap<qint64, MessageAttribute> pendingAttributes;
102 QMap<qint64, qint64> pendingSizes;
103 QMap<qint64, qint64> pendingUids;
107using namespace KIMAP;
109FetchJob::FetchScope::FetchScope():
110 mode( FetchScope::Content ),
116FetchJob::FetchJob( Session *session )
117 : Job( *new FetchJobPrivate( this, session, i18n(
"Fetch" ) ) )
120 connect( &d->emitPendingsTimer, SIGNAL(timeout()),
121 this, SLOT(emitPendings()) );
144 d->uidBased = uidBased;
168 return d->gmailEnabled;
174 d->gmailEnabled = enabled;
179 return QMap<qint64, MessagePtr>();
184 return QMap<qint64, MessageParts>();
189 return QMap<qint64, MessageFlags>();
194 return QMap<qint64, qint64>();
199 return QMap<qint64, qint64>();
202void FetchJob::doStart()
206 QByteArray parameters = d->set.toImapSequenceSet()+
' ';
207 Q_ASSERT( !parameters.trimmed().isEmpty() );
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 ) {
263 parameters +=
" (CHANGEDSINCE " + QByteArray::number( d->scope.changedSince ) +
")";
266 QByteArray command =
"FETCH";
268 command =
"UID " + command;
271 d->emitPendingsTimer.start( 100 );
272 d->selectedMailBox = d->m_session->selectedMailBox();
273 d->tags << d->sessionInternal()->sendCommand( command, parameters );
276void 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();
295 QList<QByteArray> content = response.content[3].toList();
297 MessagePtr message(
new KMime::Message );
298 bool shouldParseMessage =
false;
301 for ( QList<QByteArray>::ConstIterator it = content.constBegin();
302 it != content.constEnd(); ++it ) {
303 QByteArray str = *it;
306 if ( it == content.constEnd() ) {
307 kWarning() <<
"FETCH reply got truncated, skipping.";
311 if ( str ==
"UID" ) {
312 d->pendingUids[id] = it->toLongLong();
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(
')' ) ) {
319 QByteArray str = *it;
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;
337 }
else if ( str.startsWith(
"BODY[" ) ) {
338 if ( !str.endsWith(
']' ) ) {
339 while ( !( *it ).endsWith(
']' ) ) {
346 if ( ( index = str.indexOf(
"HEADER" ) ) > 0 || ( index = str.indexOf(
"MIME" ) ) > 0 ) {
347 if ( str[index-1] ==
'.' ) {
348 QByteArray partId = str.mid( 5, index - 6 );
349 if ( !
parts.contains( partId ) ) {
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;
366 QByteArray partId = str.mid( 5, str.size() - 6 );
367 if ( !
parts.contains( partId ) ) {
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;
395void 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 );
445void 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" ) ) {
476 QByteArray filename = disposition.remove( 0, disposition.indexOf(
"FILENAME" ) + 11 ).split(
'\"' )[0];
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 );
488QByteArray FetchJobPrivate::parseSentence(
const QByteArray &structure,
int &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 );
532QByteArray FetchJobPrivate::parseString(
const QByteArray &structure,
int &pos )
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(
"\\\\",
"\\" );
596void FetchJobPrivate::skipLeadingSpaces(
const QByteArray &structure,
int &pos )
598 while ( pos < structure.size() && structure[pos] ==
' ' ) {
603#include "moc_fetchjob.cpp"
Used to indicate what message data should be fetched.
@ FullHeaders
Fetch message size (in octets), internal date of the message, flags, UID and all RFC822 headers.
@ Full
Fetch the complete message.
@ Headers
Fetch RFC-2822 or MIME message headers.
@ Structure
Fetch the MIME message body structure (the UID is also fetched)
@ Content
Fetch the message content (the UID is also fetched)
@ HeaderAndContent
Fetch the message MIME headers and the content of parts specified in the parts field.
@ Flags
Fetch the message flags (the UID is also fetched)
Fetch message data from the server.
void setUidBased(bool uidBased)
Set how the sequence set should be interpreted.
KIMAP_DEPRECATED QMap< qint64, MessageParts > parts() const
KIMAP_DEPRECATED QMap< qint64, MessagePtr > messages() const
ImapSet sequenceSet() const
The messages that will be fetched.
void partsReceived(const QString &mailBox, const QMap< qint64, qint64 > &uids, const QMap< qint64, KIMAP::MessageParts > &parts)
Provides header and message results.
void headersReceived(const QString &mailBox, const QMap< qint64, qint64 > &uids, const QMap< qint64, qint64 > &sizes, const QMap< qint64, KIMAP::MessageFlags > &flags, const QMap< qint64, KIMAP::MessagePtr > &messages)
Provides header and message results.
KIMAP_DEPRECATED QMap< qint64, qint64 > uids() const
KIMAP_DEPRECATED QMap< qint64, qint64 > sizes() const
FetchScope scope() const
Specifies what data will be fetched.
void setSequenceSet(const ImapSet &set)
Set which messages to fetch data for.
void messagesReceived(const QString &mailBox, const QMap< qint64, qint64 > &uids, const QMap< qint64, KIMAP::MessagePtr > &messages)
Provides header and message results.
KIMAP_DEPRECATED QMap< qint64, MessageFlags > flags() const
void setScope(const FetchScope &scope)
Sets what data should be fetched.
bool isUidBased() const
How to interpret the sequence set.
bool setGmailExtensionsEnabled() const
Returns whether Gmail support is enabled.
Represents a set of natural numbers (1-> ) in a as compact as possible form.
QByteArray toImapSequenceSet() const
Returns a IMAP-compatible QByteArray representation of this set.