22 #include <QtCore/QTimer>
24 #include <KDE/KLocale>
27 #include "message_p.h"
28 #include "session_p.h"
32 class FetchJobPrivate :
public JobPrivate
35 FetchJobPrivate(
FetchJob *job, Session *session,
const QString& name ) : JobPrivate( session, name ), q( job ), uidBased( false ) { }
36 ~FetchJobPrivate() { }
38 void parseBodyStructure(
const QByteArray &structure,
int &pos, KMime::Content *content );
39 void parsePart(
const QByteArray &structure,
int &pos, KMime::Content *content );
40 QByteArray parseString(
const QByteArray &structure,
int &pos );
41 QByteArray parseSentence(
const QByteArray &structure,
int &pos );
42 void skipLeadingSpaces(
const QByteArray &structure,
int &pos );
46 if ( pendingUids.isEmpty() ) {
50 if ( !pendingParts.isEmpty() ) {
51 emit q->partsReceived( selectedMailBox,
52 pendingUids, pendingParts );
54 if ( !pendingSizes.isEmpty() || !pendingFlags.isEmpty() ) {
55 emit q->headersReceived( selectedMailBox,
56 pendingUids, pendingSizes,
57 pendingFlags, pendingMessages );
59 if ( !pendingMessages.isEmpty() ) {
60 emit q->messagesReceived( selectedMailBox,
61 pendingUids, pendingMessages );
65 pendingMessages.clear();
76 QString selectedMailBox;
78 QTimer emitPendingsTimer;
79 QMap<qint64, MessagePtr> pendingMessages;
80 QMap<qint64, MessageParts> pendingParts;
81 QMap<qint64, MessageFlags> pendingFlags;
82 QMap<qint64, qint64> pendingSizes;
83 QMap<qint64, qint64> pendingUids;
87 using namespace KIMAP;
89 FetchJob::FetchJob( Session *session )
90 : Job( *new FetchJobPrivate( this, session, i18n(
"Fetch" ) ) )
93 d->scope.mode = FetchScope::Content;
94 connect( &d->emitPendingsTimer, SIGNAL(timeout()),
95 this, SLOT(emitPendings()) );
105 Q_ASSERT( !
set.toImapSequenceSet().trimmed().isEmpty() );
118 d->uidBased = uidBased;
141 return QMap<qint64, MessagePtr>();
146 return QMap<qint64, MessageParts>();
151 return QMap<qint64, MessageFlags>();
156 return QMap<qint64, qint64>();
161 return QMap<qint64, qint64>();
164 void FetchJob::doStart()
168 QByteArray parameters = d->set.toImapSequenceSet()+
' ';
169 Q_ASSERT( !parameters.trimmed().isEmpty() );
171 switch ( d->scope.mode ) {
173 if ( d->scope.parts.isEmpty() ) {
174 parameters +=
"(RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] FLAGS UID)";
177 foreach (
const QByteArray &part, d->scope.parts ) {
178 parameters +=
"BODY.PEEK[" + part +
".MIME] ";
180 parameters +=
"UID)";
184 parameters +=
"(FLAGS UID)";
187 parameters +=
"(BODYSTRUCTURE UID)";
190 if ( d->scope.parts.isEmpty() ) {
191 parameters +=
"(BODY.PEEK[] UID)";
194 foreach (
const QByteArray &part, d->scope.parts ) {
195 parameters +=
"BODY.PEEK[" + part +
"] ";
197 parameters +=
"UID)";
201 parameters +=
"(RFC822.SIZE INTERNALDATE BODY.PEEK[] FLAGS UID)";
204 if ( d->scope.parts.isEmpty() ) {
205 parameters +=
"(BODY.PEEK[] FLAGS UID)";
207 parameters +=
"(BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)]";
208 foreach (
const QByteArray &part, d->scope.parts ) {
209 parameters +=
" BODY.PEEK[" + part +
".MIME] BODY.PEEK[" + part +
"]";
211 parameters +=
" FLAGS UID)";
216 QByteArray command =
"FETCH";
218 command =
"UID " + command;
221 d->emitPendingsTimer.start( 100 );
222 d->selectedMailBox = d->m_session->selectedMailBox();
223 d->tags << d->sessionInternal()->sendCommand( command, parameters );
226 void FetchJob::handleResponse(
const Message &response )
232 if ( !response.content.isEmpty() &&
233 d->tags.size() == 1 &&
234 d->tags.contains( response.content.first().toString() ) ) {
235 d->emitPendingsTimer.stop();
239 if ( handleErrorReplies( response ) == NotHandled ) {
240 if ( response.content.size() == 4 &&
241 response.content[2].toString() ==
"FETCH" &&
242 response.content[3].type() == Message::Part::List ) {
244 qint64
id = response.content[1].toString().toLongLong();
245 QList<QByteArray> content = response.content[3].toList();
247 MessagePtr message(
new KMime::Message );
248 bool shouldParseMessage =
false;
251 for ( QList<QByteArray>::ConstIterator it = content.constBegin();
252 it != content.constEnd(); ++it ) {
253 QByteArray str = *it;
256 if ( it == content.constEnd() ) {
257 kWarning() <<
"FETCH reply got truncated, skipping.";
261 if ( str ==
"UID" ) {
262 d->pendingUids[id] = it->toLongLong();
263 }
else if ( str ==
"RFC822.SIZE" ) {
264 d->pendingSizes[id] = it->toLongLong();
265 }
else if ( str ==
"INTERNALDATE" ) {
266 message->date()->setDateTime( KDateTime::fromString( *it, KDateTime::RFCDate ) );
267 }
else if ( str ==
"FLAGS" ) {
268 if ( ( *it ).startsWith(
'(' ) && ( *it ).endsWith(
')' ) ) {
269 QByteArray str = *it;
272 d->pendingFlags[id] = str.split(
' ' );
274 d->pendingFlags[id] << *it;
276 }
else if ( str ==
"BODYSTRUCTURE" ) {
278 d->parseBodyStructure( *it, pos, message.get() );
280 d->pendingMessages[id] = message;
281 }
else if ( str.startsWith(
"BODY[" ) ) {
282 if ( !str.endsWith(
']' ) ) {
283 while ( !( *it ).endsWith(
']' ) ) {
290 if ( ( index = str.indexOf(
"HEADER" ) ) > 0 || ( index = str.indexOf(
"MIME" ) ) > 0 ) {
291 if ( str[index-1] ==
'.' ) {
292 QByteArray partId = str.mid( 5, index - 6 );
293 if ( !parts.contains( partId ) ) {
294 parts[partId] = ContentPtr(
new KMime::Content );
296 parts[partId]->setHead( *it );
297 parts[partId]->parse();
298 d->pendingParts[id] =
parts;
300 message->setHead( *it );
301 shouldParseMessage =
true;
304 if ( str ==
"BODY[]" ) {
305 message->setContent( KMime::CRLFtoLF( *it ) );
306 shouldParseMessage =
true;
308 d->pendingMessages[id] = message;
310 QByteArray partId = str.mid( 5, str.size() - 6 );
311 if ( !parts.contains( partId ) ) {
312 parts[partId] = ContentPtr(
new KMime::Content );
314 parts[partId]->setBody( *it );
315 parts[partId]->parse();
317 d->pendingParts[id] =
parts;
323 if ( shouldParseMessage ) {
331 d->pendingMessages[id] = message;
337 void FetchJobPrivate::parseBodyStructure(
const QByteArray &structure,
int &pos, KMime::Content *content)
339 skipLeadingSpaces( structure, pos );
341 if ( structure[pos] !=
'(' ) {
348 if ( structure[pos] !=
'(' ) {
350 parsePart( structure, pos, content );
352 content->contentType()->setMimeType(
"MULTIPART/MIXED" );
353 while ( pos < structure.size() && structure[pos] ==
'(' ) {
354 KMime::Content *child =
new KMime::Content;
355 content->addContent( child );
356 parseBodyStructure( structure, pos, child );
360 QByteArray subType = parseString( structure, pos );
361 content->contentType()->setMimeType(
"MULTIPART/" + subType );
363 QByteArray parameters = parseSentence( structure, pos );
364 if ( parameters.contains(
"BOUNDARY" ) ) {
365 content->contentType()->setBoundary( parameters.remove( 0, parameters.indexOf(
"BOUNDARY" ) + 11 ).split(
'\"' )[0] );
368 QByteArray disposition = parseSentence( structure, pos );
369 if ( disposition.contains(
"INLINE" ) ) {
370 content->contentDisposition()->setDisposition( KMime::Headers::CDinline );
371 }
else if ( disposition.contains(
"ATTACHMENT" ) ) {
372 content->contentDisposition()->setDisposition( KMime::Headers::CDattachment );
375 parseSentence( structure, pos );
379 while ( pos < structure.size() && structure[pos] !=
')' ) {
380 skipLeadingSpaces( structure, pos );
381 parseSentence( structure, pos );
382 skipLeadingSpaces( structure, pos );
388 void FetchJobPrivate::parsePart(
const QByteArray &structure,
int &pos, KMime::Content *content )
390 if ( structure[pos] !=
'(' ) {
396 QByteArray mainType = parseString( structure, pos );
397 QByteArray subType = parseString( structure, pos );
399 content->contentType()->setMimeType( mainType +
'/' + subType );
401 parseSentence( structure, pos );
402 parseString( structure, pos );
404 content->contentDescription()->from7BitString( parseString( structure, pos ) );
406 parseString( structure, pos );
407 parseString( structure, pos );
408 parseString( structure, pos );
410 QByteArray disposition = parseSentence( structure, pos );
411 if ( disposition.contains(
"INLINE" ) ) {
412 content->contentDisposition()->setDisposition( KMime::Headers::CDinline );
413 }
else if ( disposition.contains(
"ATTACHMENT" ) ) {
414 content->contentDisposition()->setDisposition( KMime::Headers::CDattachment );
416 if ( ( content->contentDisposition()->disposition() == KMime::Headers::CDattachment ||
417 content->contentDisposition()->disposition() == KMime::Headers::CDinline ) &&
418 disposition.contains(
"FILENAME" ) ) {
419 QByteArray filename = disposition.remove( 0, disposition.indexOf(
"FILENAME" ) + 11 ).split(
'\"' )[0];
420 content->contentDisposition()->setFilename( filename );
424 while ( pos < structure.size() && structure[pos] !=
')' ) {
425 skipLeadingSpaces( structure, pos );
426 parseSentence( structure, pos );
427 skipLeadingSpaces( structure, pos );
431 QByteArray FetchJobPrivate::parseSentence(
const QByteArray &structure,
int &pos )
436 skipLeadingSpaces( structure, pos );
438 if ( structure[pos] !=
'(' ) {
439 return parseString( structure, pos );
445 switch ( structure[pos] ) {
463 skipLeadingSpaces( structure, pos );
464 parseString( structure, pos );
465 skipLeadingSpaces( structure, pos );
468 }
while ( pos < structure.size() && stack != 0 );
470 result = structure.mid( start, pos - start );
475 QByteArray FetchJobPrivate::parseString(
const QByteArray &structure,
int &pos )
479 skipLeadingSpaces( structure, pos );
482 bool foundSlash =
false;
485 if ( structure[pos] ==
'"' ) {
488 if ( structure[pos] ==
'\\' ) {
493 if ( structure[pos] ==
'"' ) {
494 result = structure.mid( start + 1, pos - start - 1 );
502 if ( structure[pos] ==
' ' ||
503 structure[pos] ==
'(' ||
504 structure[pos] ==
')' ||
505 structure[pos] ==
'[' ||
506 structure[pos] ==
']' ||
507 structure[pos] ==
'\n' ||
508 structure[pos] ==
'\r' ||
509 structure[pos] ==
'"' ) {
512 if ( structure[pos] ==
'\\' ) {
518 result = structure.mid( start, pos - start );
521 if ( result ==
"NIL" ) {
528 while ( result.contains(
"\\\"" ) ) {
529 result.replace(
"\\\"",
"\"" );
531 while ( result.contains(
"\\\\" ) ) {
532 result.replace(
"\\\\",
"\\" );
539 void FetchJobPrivate::skipLeadingSpaces(
const QByteArray &structure,
int &pos )
541 while ( pos < structure.size() && structure[pos] ==
' ' ) {
546 #include "moc_fetchjob.cpp"