22 #include <QtCore/QTimer> 24 #include <KDE/KLocalizedString> 27 #include "message_p.h" 28 #include "session_p.h" 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() ) {
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();
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;
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()
131 Q_ASSERT( !
set.toImapSequenceSet().trimmed().isEmpty() );
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>();
202 void 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 );
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();
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;
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" ) ) {
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 );
488 QByteArray 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 );
532 QByteArray 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(
"\\\\",
"\\" );
596 void FetchJobPrivate::skipLeadingSpaces(
const QByteArray &structure,
int &pos )
598 while ( pos < structure.size() && structure[pos] ==
' ' ) {
603 #include "moc_fetchjob.cpp" void setSequenceSet(const ImapSet &set)
Set which messages to fetch data for.
KIMAP_DEPRECATED QMap< qint64, MessagePtr > messages() const
Fetch the message content (the UID is also fetched)
KIMAP_DEPRECATED QMap< qint64, qint64 > uids() const
ImapSet sequenceSet() const
The messages that will be fetched.
KIMAP_DEPRECATED QMap< qint64, MessageParts > parts() const
KIMAP_DEPRECATED QMap< qint64, MessageFlags > flags() const
Fetch the complete message.
Fetch message size (in octets), internal date of the message, flags, UID and all RFC822 headers...
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.
Fetch the message MIME headers and the content of parts specified in the parts field.
Fetch the message flags (the UID is also fetched)
bool setGmailExtensionsEnabled() const
Returns whether Gmail support is enabled.
Represents a set of natural numbers (1-> ) in a as compact as possible form.
KIMAP_DEPRECATED QMap< qint64, qint64 > sizes() const
bool isUidBased() const
How to interpret the sequence set.
FetchScope scope() const
Specifies what data will be fetched.
void setScope(const FetchScope &scope)
Sets what data should be fetched.
Used to indicate what message data should be fetched.