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] ";
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+
"] ";
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.";
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(
']') ) ++it;
288 if ( (index=str.indexOf(
"HEADER"))>0 || (index=str.indexOf(
"MIME"))>0 ) {
289 if ( str[index-1]==
'.' ) {
290 QByteArray partId = str.mid( 5, index-6 );
291 if ( !parts.contains( partId ) ) {
292 parts[partId] = ContentPtr(
new KMime::Content );
294 parts[partId]->setHead(*it);
295 parts[partId]->parse();
296 d->pendingParts[id] =
parts;
298 message->setHead(*it);
299 shouldParseMessage =
true;
302 if ( str==
"BODY[]" ) {
303 message->setContent( KMime::CRLFtoLF(*it) );
304 shouldParseMessage =
true;
306 d->pendingMessages[id] = message;
308 QByteArray partId = str.mid( 5, str.size()-6 );
309 if ( !parts.contains( partId ) ) {
310 parts[partId] = ContentPtr(
new KMime::Content );
312 parts[partId]->setBody(*it);
313 parts[partId]->parse();
315 d->pendingParts[id] =
parts;
321 if ( shouldParseMessage ) {
329 d->pendingMessages[id] = message;
335 void FetchJobPrivate::parseBodyStructure(
const QByteArray &structure,
int &pos, KMime::Content *content)
337 skipLeadingSpaces(structure, pos);
339 if ( structure[pos]!=
'(' ) {
346 if ( structure[pos]!=
'(' ) {
348 parsePart( structure, pos, content );
350 content->contentType()->setMimeType(
"MULTIPART/MIXED");
351 while ( pos<structure.size() && structure[pos]==
'(' ) {
352 KMime::Content *child =
new KMime::Content;
353 content->addContent( child );
354 parseBodyStructure( structure, pos, child );
358 QByteArray subType = parseString( structure, pos );
359 content->contentType()->setMimeType(
"MULTIPART/"+subType );
361 QByteArray parameters = parseSentence( structure, pos );
362 if (parameters.contains(
"BOUNDARY") ) {
363 content->contentType()->setBoundary(parameters.remove(0, parameters.indexOf(
"BOUNDARY") + 11).split(
'\"')[0]);
366 QByteArray disposition = parseSentence( structure, pos );
367 if ( disposition.contains(
"INLINE") ) {
368 content->contentDisposition()->setDisposition( KMime::Headers::CDinline );
369 }
else if ( disposition.contains(
"ATTACHMENT") ) {
370 content->contentDisposition()->setDisposition( KMime::Headers::CDattachment );
373 parseSentence( structure, pos );
377 while ( pos<structure.size() && structure[pos]!=
')' ) {
378 skipLeadingSpaces( structure, pos );
379 parseSentence( structure, pos );
380 skipLeadingSpaces( structure, pos );
386 void FetchJobPrivate::parsePart(
const QByteArray &structure,
int &pos, KMime::Content *content )
388 if ( structure[pos]!=
'(' ) {
394 QByteArray mainType = parseString( structure, pos );
395 QByteArray subType = parseString( structure, pos );
397 content->contentType()->setMimeType( mainType+
'/'+subType );
399 parseSentence( structure, pos );
400 parseString( structure, pos );
402 content->contentDescription()->from7BitString( parseString( structure, pos ) );
404 parseString( structure, pos );
405 parseString( structure, pos );
406 parseString( structure, pos );
408 QByteArray disposition = parseSentence( structure, pos );
409 if ( disposition.contains(
"INLINE") ) {
410 content->contentDisposition()->setDisposition( KMime::Headers::CDinline );
411 }
else if ( disposition.contains(
"ATTACHMENT") ) {
412 content->contentDisposition()->setDisposition( KMime::Headers::CDattachment );
414 if ( (content->contentDisposition()->disposition() == KMime::Headers::CDattachment
415 || content->contentDisposition()->disposition() == KMime::Headers::CDinline)
416 && disposition.contains(
"FILENAME") ) {
417 QByteArray filename = disposition.remove(0, disposition.indexOf(
"FILENAME") + 11).split(
'\"')[0];
418 content->contentDisposition()->setFilename( filename );
422 while ( pos<structure.size() && structure[pos]!=
')' ) {
423 skipLeadingSpaces( structure, pos );
424 parseSentence( structure, pos );
425 skipLeadingSpaces( structure, pos );
429 QByteArray FetchJobPrivate::parseSentence(
const QByteArray &structure,
int &pos )
434 skipLeadingSpaces( structure, pos );
436 if ( structure[pos]!=
'(' ) {
437 return parseString( structure, pos );
443 switch ( structure[pos] ) {
461 skipLeadingSpaces(structure, pos);
462 parseString(structure, pos);
463 skipLeadingSpaces(structure, pos);
466 }
while ( pos<structure.size() && stack!=0 );
468 result = structure.mid( start, pos - start );
473 QByteArray FetchJobPrivate::parseString(
const QByteArray &structure,
int &pos )
477 skipLeadingSpaces( structure, pos );
480 bool foundSlash =
false;
483 if ( structure[pos] ==
'"' ) {
486 if ( structure[pos] ==
'\\' ) {
491 if ( structure[pos] ==
'"' ) {
492 result = structure.mid( start+1, pos - start - 1);
500 if ( structure[pos] ==
' ' || structure[pos] ==
'(' || structure[pos] ==
')' || structure[pos] ==
'[' || structure[pos] ==
']' || structure[pos] ==
'\n' || structure[pos] ==
'\r' || structure[pos] ==
'"') {
503 if (structure[pos] ==
'\\')
508 result = structure.mid( start, pos - start );
511 if ( result ==
"NIL" )
517 while ( result.contains(
"\\\"" ) )
518 result.replace(
"\\\"",
"\"" );
519 while ( result.contains(
"\\\\" ) )
520 result.replace(
"\\\\",
"\\" );
526 void FetchJobPrivate::skipLeadingSpaces(
const QByteArray &structure,
int &pos )
528 while ( structure[pos]==
' ' && pos<structure.size() ) pos++;
531 #include "fetchjob.moc"