00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "fetchjob.h"
00021
00022 #include <QtCore/QTimer>
00023 #include <KDE/KLocale>
00024
00025 #include "job_p.h"
00026 #include "message_p.h"
00027 #include "session_p.h"
00028
00029 namespace KIMAP
00030 {
00031 class FetchJobPrivate : public JobPrivate
00032 {
00033 public:
00034 FetchJobPrivate( FetchJob *job, Session *session, const QString& name ) : JobPrivate( session, name ), q(job), uidBased(false) { }
00035 ~FetchJobPrivate() { }
00036
00037 void parseBodyStructure( const QByteArray &structure, int &pos, KMime::Content *content );
00038 void parsePart( const QByteArray &structure, int &pos, KMime::Content *content );
00039 QByteArray parseString( const QByteArray &structure, int &pos );
00040 QByteArray parseSentence( const QByteArray &structure, int &pos );
00041 void skipLeadingSpaces( const QByteArray &structure, int &pos );
00042
00043 MessagePtr message(int id)
00044 {
00045 if ( !messages.contains(id) ) {
00046 messages[id] = MessagePtr(new KMime::Message);
00047 }
00048
00049 return messages[id];
00050 }
00051
00052 ContentPtr part(int id, QByteArray partName)
00053 {
00054 if ( !parts[id].contains(partName) ) {
00055 parts[id][partName] = ContentPtr(new KMime::Content);
00056 }
00057
00058 return parts[id][partName];
00059 }
00060
00061 void emitPendings()
00062 {
00063 if ( pendingUids.isEmpty() ) {
00064 return;
00065 }
00066
00067 if ( !pendingParts.isEmpty() ) {
00068 emit q->partsReceived( selectedMailBox,
00069 pendingUids, pendingParts );
00070
00071 } else if ( !pendingSizes.isEmpty() || !pendingFlags.isEmpty() ) {
00072 emit q->headersReceived( selectedMailBox,
00073 pendingUids, pendingSizes,
00074 pendingFlags, pendingMessages );
00075 } else {
00076 emit q->messagesReceived( selectedMailBox,
00077 pendingUids, pendingMessages );
00078 }
00079
00080 pendingUids.clear();
00081 pendingMessages.clear();
00082 pendingParts.clear();
00083 pendingSizes.clear();
00084 pendingFlags.clear();
00085 }
00086
00087 FetchJob * const q;
00088
00089 ImapSet set;
00090 bool uidBased;
00091 FetchJob::FetchScope scope;
00092 QString selectedMailBox;
00093
00094 QMap<qint64, MessagePtr> messages;
00095 QMap<qint64, MessageParts> parts;
00096 QMap<qint64, MessageFlags> flags;
00097 QMap<qint64, qint64> sizes;
00098 QMap<qint64, qint64> uids;
00099
00100 QTimer emitPendingsTimer;
00101 QMap<qint64, MessagePtr> pendingMessages;
00102 QMap<qint64, MessageParts> pendingParts;
00103 QMap<qint64, MessageFlags> pendingFlags;
00104 QMap<qint64, qint64> pendingSizes;
00105 QMap<qint64, qint64> pendingUids;
00106 };
00107 }
00108
00109 using namespace KIMAP;
00110
00111 FetchJob::FetchJob( Session *session )
00112 : Job( *new FetchJobPrivate(this, session, i18n("Fetch")) )
00113 {
00114 Q_D(FetchJob);
00115 d->scope.mode = FetchScope::Content;
00116 connect( &d->emitPendingsTimer, SIGNAL( timeout() ),
00117 this, SLOT( emitPendings() ) );
00118 }
00119
00120 FetchJob::~FetchJob()
00121 {
00122 }
00123
00124 void FetchJob::setSequenceSet( const ImapSet &set )
00125 {
00126 Q_D(FetchJob);
00127 d->set = set;
00128 }
00129
00130 ImapSet FetchJob::sequenceSet() const
00131 {
00132 Q_D(const FetchJob);
00133 return d->set;
00134 }
00135
00136 void FetchJob::setUidBased(bool uidBased)
00137 {
00138 Q_D(FetchJob);
00139 d->uidBased = uidBased;
00140 }
00141
00142 bool FetchJob::isUidBased() const
00143 {
00144 Q_D(const FetchJob);
00145 return d->uidBased;
00146 }
00147
00148 void FetchJob::setScope( const FetchScope &scope )
00149 {
00150 Q_D(FetchJob);
00151 d->scope = scope;
00152 }
00153
00154 FetchJob::FetchScope FetchJob::scope() const
00155 {
00156 Q_D(const FetchJob);
00157 return d->scope;
00158 }
00159
00160 QMap<qint64, MessagePtr> FetchJob::messages() const
00161 {
00162 Q_D(const FetchJob);
00163 return d->messages;
00164 }
00165
00166 QMap<qint64, MessageParts> FetchJob::parts() const
00167 {
00168 Q_D(const FetchJob);
00169 return d->parts;
00170 }
00171
00172 QMap<qint64, MessageFlags> FetchJob::flags() const
00173 {
00174 Q_D(const FetchJob);
00175 return d->flags;
00176 }
00177
00178 QMap<qint64, qint64> FetchJob::sizes() const
00179 {
00180 Q_D(const FetchJob);
00181 return d->sizes;
00182 }
00183
00184 QMap<qint64, qint64> FetchJob::uids() const
00185 {
00186 Q_D(const FetchJob);
00187 return d->uids;
00188 }
00189
00190 void FetchJob::doStart()
00191 {
00192 Q_D(FetchJob);
00193
00194 QByteArray parameters = d->set.toImapSequenceSet()+' ';
00195
00196 switch ( d->scope.mode ) {
00197 case FetchScope::Headers:
00198 if ( d->scope.parts.isEmpty() ) {
00199 parameters+="(RFC822.SIZE INTERNALDATE BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT)] FLAGS UID)";
00200 } else {
00201 parameters+='(';
00202 foreach ( const QByteArray &part, d->scope.parts ) {
00203 parameters+="BODY["+part+".MIME] ";
00204 }
00205 parameters+="UID)";
00206 }
00207 break;
00208 case FetchScope::Flags:
00209 parameters+="(FLAGS UID)";
00210 break;
00211 case FetchScope::Structure:
00212 parameters+="(BODYSTRUCTURE UID)";
00213 break;
00214 case FetchScope::Content:
00215 if ( d->scope.parts.isEmpty() ) {
00216 parameters+="(BODY[] UID)";
00217 } else {
00218 parameters+='(';
00219 foreach ( const QByteArray &part, d->scope.parts ) {
00220 parameters+="BODY["+part+"] ";
00221 }
00222 parameters+="UID)";
00223 }
00224 break;
00225 }
00226
00227 QByteArray command = "FETCH";
00228 if ( d->uidBased ) {
00229 command = "UID "+command;
00230 }
00231
00232 d->emitPendingsTimer.start( 100 );
00233 d->selectedMailBox = d->sessionInternal()->selectedMailBox();
00234 d->tag = d->sessionInternal()->sendCommand( command, parameters );
00235 }
00236
00237 void FetchJob::handleResponse( const Message &response )
00238 {
00239 Q_D(FetchJob);
00240
00241 if (handleErrorReplies(response) == NotHandled ) {
00242 if ( response.content.size() == 4
00243 && response.content[2].toString()=="FETCH"
00244 && response.content[3].type()==Message::Part::List ) {
00245
00246 qint64 id = response.content[1].toString().toLongLong();
00247 QList<QByteArray> content = response.content[3].toList();
00248
00249 for ( QList<QByteArray>::ConstIterator it = content.constBegin();
00250 it!=content.constEnd(); ++it ) {
00251 QByteArray str = *it;
00252 ++it;
00253
00254 if ( str=="UID" ) {
00255 d->uids[id] = it->toLongLong();
00256 } else if ( str=="RFC822.SIZE" ) {
00257 d->sizes[id] = it->toLongLong();
00258 } else if ( str=="INTERNALDATE" ) {
00259 d->message(id)->date()->setDateTime( KDateTime::fromString( *it, KDateTime::RFCDate ) );
00260 } else if ( str=="FLAGS" ) {
00261 if ( (*it).startsWith('(') && (*it).endsWith(')') ) {
00262 QByteArray str = *it;
00263 str.chop(1);
00264 str.remove(0, 1);
00265 d->flags[id] = str.split(' ');
00266 } else {
00267 d->flags[id] << *it;
00268 }
00269 } else if ( str=="BODYSTRUCTURE" ) {
00270 int pos = 0;
00271 d->parseBodyStructure(*it, pos, d->message(id).get());
00272 d->message(id)->assemble();
00273 } else if ( str.startsWith("BODY[") ) {
00274 if ( !str.endsWith(']') ) {
00275 while ( !(*it).endsWith(']') ) ++it;
00276 ++it;
00277 }
00278
00279 int index;
00280 if ( (index=str.indexOf("HEADER"))>0 || (index=str.indexOf("MIME"))>0 ) {
00281 if ( str[index-1]=='.' ) {
00282 QByteArray partId = str.mid( 5, index-6 );
00283 d->part( id, partId )->setHead(*it);
00284 d->part( id, partId )->parse();
00285 } else {
00286 d->message(id)->setHead(*it);
00287 d->message(id)->parse();
00288 }
00289 } else {
00290 if ( str=="BODY[]" ) {
00291 d->message(id)->setContent( KMime::CRLFtoLF(*it) );
00292 d->message(id)->parse();
00293
00294 d->pendingUids[id] = d->uids[id];
00295 d->pendingMessages[id] = d->message(id);
00296 } else {
00297 QByteArray partId = str.mid( 5, str.size()-6 );
00298 d->part( id, partId )->setBody(*it);
00299 d->part( id, partId )->parse();
00300
00301 d->pendingUids[id] = d->uids[id];
00302 d->pendingParts[id] = d->parts[id];
00303 }
00304 }
00305 }
00306 }
00307
00308 if ( d->scope.mode == FetchScope::Headers ) {
00309 d->pendingUids[id] = d->uids[id];
00310 d->pendingSizes[id] = d->sizes[id];
00311 d->pendingFlags[id] = d->flags[id];
00312 d->pendingMessages[id] = d->message(id);
00313 }
00314 }
00315 } else {
00316 d->emitPendingsTimer.stop();
00317 d->emitPendings();
00318 }
00319 }
00320
00321 void FetchJobPrivate::parseBodyStructure(const QByteArray &structure, int &pos, KMime::Content *content)
00322 {
00323 skipLeadingSpaces(structure, pos);
00324
00325 if ( structure[pos]!='(' ) {
00326 return;
00327 }
00328
00329 pos++;
00330
00331
00332 if ( structure[pos]!='(' ) {
00333 pos--;
00334 parsePart( structure, pos, content );
00335 } else {
00336 content->contentType()->setMimeType("MULTIPART/MIXED");
00337 while ( pos<structure.size() && structure[pos]=='(' ) {
00338 KMime::Content *child = new KMime::Content;
00339 content->addContent( child );
00340 parseBodyStructure( structure, pos, child );
00341 child->assemble();
00342 }
00343
00344 QByteArray subType = parseString( structure, pos );
00345 content->contentType()->setMimeType( "MULTIPART/"+subType );
00346
00347 parseSentence( structure, pos );
00348
00349 QByteArray disposition = parseSentence( structure, pos );
00350 if ( disposition.contains("INLINE") ) {
00351 content->contentDisposition()->setDisposition( KMime::Headers::CDinline );
00352 } else if ( disposition.contains("ATTACHMENT") ) {
00353 content->contentDisposition()->setDisposition( KMime::Headers::CDattachment );
00354 }
00355
00356 parseSentence( structure, pos );
00357 }
00358
00359
00360 while ( pos<structure.size() && structure[pos]!=')' ) {
00361 skipLeadingSpaces( structure, pos );
00362 parseSentence( structure, pos );
00363 skipLeadingSpaces( structure, pos );
00364 }
00365
00366 pos++;
00367 }
00368
00369 void FetchJobPrivate::parsePart( const QByteArray &structure, int &pos, KMime::Content *content )
00370 {
00371 if ( structure[pos]!='(' ) {
00372 return;
00373 }
00374
00375 pos++;
00376
00377 QByteArray mainType = parseString( structure, pos );
00378 QByteArray subType = parseString( structure, pos );
00379
00380 content->contentType()->setMimeType( mainType+'/'+subType );
00381
00382 parseSentence( structure, pos );
00383 parseString( structure, pos );
00384
00385 content->contentDescription()->from7BitString( parseString( structure, pos ) );
00386
00387 parseString( structure, pos );
00388 parseString( structure, pos );
00389 if ( mainType=="TEXT" ) {
00390 parseString( structure, pos );
00391 }
00392
00393 QByteArray disposition = parseSentence( structure, pos );
00394 if ( disposition.contains("INLINE") ) {
00395 content->contentDisposition()->setDisposition( KMime::Headers::CDinline );
00396 } else if ( disposition.contains("ATTACHMENT") ) {
00397 content->contentDisposition()->setDisposition( KMime::Headers::CDattachment );
00398 }
00399
00400
00401 while ( pos<structure.size() && structure[pos]!=')' ) {
00402 skipLeadingSpaces( structure, pos );
00403 parseSentence( structure, pos );
00404 skipLeadingSpaces( structure, pos );
00405 }
00406 }
00407
00408 QByteArray FetchJobPrivate::parseSentence( const QByteArray &structure, int &pos )
00409 {
00410 QByteArray result;
00411 int stack = 0;
00412
00413 skipLeadingSpaces( structure, pos );
00414
00415 if ( structure[pos]!='(' ) {
00416 return parseString( structure, pos );
00417 }
00418
00419 int start = pos;
00420
00421 do {
00422 switch ( structure[pos] ) {
00423 case '(':
00424 pos++;
00425 stack++;
00426 break;
00427 case ')':
00428 pos++;
00429 stack--;
00430 break;
00431 case '[':
00432 pos++;
00433 stack++;
00434 break;
00435 case ']':
00436 pos++;
00437 stack--;
00438 break;
00439 default:
00440 skipLeadingSpaces(structure, pos);
00441 parseString(structure, pos);
00442 skipLeadingSpaces(structure, pos);
00443 break;
00444 }
00445 } while ( pos<structure.size() && stack!=0 );
00446
00447 result = structure.mid( start, pos - start );
00448
00449 return result;
00450 }
00451
00452 QByteArray FetchJobPrivate::parseString( const QByteArray &structure, int &pos )
00453 {
00454 QByteArray result;
00455
00456 skipLeadingSpaces( structure, pos );
00457
00458 int start = pos;
00459 bool foundSlash = false;
00460
00461
00462 if ( structure[pos] == '"' ) {
00463 pos++;
00464 Q_FOREVER {
00465 if ( structure[pos] == '\\' ) {
00466 pos+= 2;
00467 foundSlash = true;
00468 continue;
00469 }
00470 if ( structure[pos] == '"' ) {
00471 result = structure.mid( start+1, pos - start );
00472 pos++;
00473 break;
00474 }
00475 pos++;
00476 }
00477 } else {
00478 Q_FOREVER {
00479 if ( structure[pos] == ' ' || structure[pos] == '(' || structure[pos] == ')' || structure[pos] == '[' || structure[pos] == ']' || structure[pos] == '\n' || structure[pos] == '\r' || structure[pos] == '"') {
00480 break;
00481 }
00482 if (structure[pos] == '\\')
00483 foundSlash = true;
00484 pos++;
00485 }
00486
00487 result = structure.mid( start, pos - start );
00488
00489
00490 if ( result == "NIL" )
00491 result.clear();
00492 }
00493
00494
00495 if ( foundSlash ) {
00496 while ( result.contains( "\\\"" ) )
00497 result.replace( "\\\"", "\"" );
00498 while ( result.contains( "\\\\" ) )
00499 result.replace( "\\\\", "\\" );
00500 }
00501
00502 return result;
00503 }
00504
00505 void FetchJobPrivate::skipLeadingSpaces( const QByteArray &structure, int &pos )
00506 {
00507 while ( structure[pos]==' ' && pos<structure.size() ) pos++;
00508 }
00509
00510 #include "fetchjob.moc"