• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

KIMAP Library

fetchjob.cpp

00001 /*
00002     Copyright (c) 2009 Kevin Ottens <ervin@kde.org>
00003 
00004     This library is free software; you can redistribute it and/or modify it
00005     under the terms of the GNU Library General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or (at your
00007     option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful, but WITHOUT
00010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00012     License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to the
00016     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017     02110-1301, USA.
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(']') ) { // BODY[ ... ] might have been split, skip until we find the ]
00275             while ( !(*it).endsWith(']') ) ++it;
00276             ++it;
00277           }
00278 
00279           int index;
00280           if ( (index=str.indexOf("HEADER"))>0 || (index=str.indexOf("MIME"))>0 ) { // headers
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 { // full payload
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]!='(' ) { // simple part
00333     pos--;
00334     parsePart( structure, pos, content );
00335   } else { // multi part
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 ); // Ditch the parameters... FIXME: Read it to get charset and name
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 ); // Ditch the body language
00357   }
00358 
00359   // Consume what's left
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 ); // Ditch the parameters... FIXME: Read it to get charset and name
00383   parseString( structure, pos ); // ... and the id
00384 
00385   content->contentDescription()->from7BitString( parseString( structure, pos ) );
00386 
00387   parseString( structure, pos ); // Ditch the encoding too
00388   parseString( structure, pos ); // ... and the size
00389   if ( mainType=="TEXT" ) {
00390     parseString( structure, pos ); // ... and the line count
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   // Consume what's left
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   // quoted string
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 { // unquoted string
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     // transform unquoted NIL
00490     if ( result == "NIL" )
00491       result.clear();
00492   }
00493 
00494   // simplify slashes
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"

KIMAP Library

Skip menu "KIMAP Library"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.8
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal