kmail Library API Documentation

kmfolderimap.cpp

00001 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include "kmfolderimap.h"
00028 #include "kmfoldermbox.h"
00029 #include "kmfoldertree.h"
00030 #include "undostack.h"
00031 #include "kmfoldermgr.h"
00032 #include "imapjob.h"
00033 using KMail::ImapJob;
00034 #include "attachmentstrategy.h"
00035 using KMail::AttachmentStrategy;
00036 
00037 #include <kdebug.h>
00038 #include <kio/scheduler.h>
00039 #include <kconfig.h>
00040 
00041 #include <qbuffer.h>
00042 #include <qtextcodec.h>
00043 
00044 #include <assert.h>
00045 
00046 KMFolderImap::KMFolderImap(KMFolderDir* aParent, const QString& aName)
00047   : KMFolderMbox(aParent, aName)
00048 {
00049   mContentState = imapNoInformation;
00050   mSubfolderState = imapNoInformation;
00051   mAccount = 0;
00052   mIsSelected = FALSE;
00053   mLastUid = 0;
00054   mCheckFlags = TRUE;
00055   mCheckMail = TRUE;
00056   mCheckingValidity = FALSE;
00057 
00058   KConfig* config = KMKernel::config();
00059   KConfigGroupSaver saver(config, "Folder-" + idString());
00060   mUidValidity = config->readEntry("UidValidity");
00061   if (mImapPath.isEmpty()) mImapPath = config->readEntry("ImapPath");
00062   if (aName == "INBOX" && mImapPath == "/INBOX/")
00063   {
00064     mIsSystemFolder = TRUE;
00065     mLabel = i18n("inbox");
00066   }
00067   mNoContent = config->readBoolEntry("NoContent", FALSE);
00068   mReadOnly = config->readBoolEntry("ReadOnly", FALSE);
00069 
00070   readConfig();
00071 }
00072 
00073 KMFolderImap::~KMFolderImap()
00074 {
00075   if (mAccount) {
00076     mAccount->removeSlaveJobsForFolder( this );
00077     /* Now that we've removed ourselves from the accounts jobs map, kill all 
00078        ongoing operations and reset mailcheck if we were deleted during an
00079        ongoing mailcheck of our account. Not very gracefull, but safe, and the 
00080        only way I can see to reset the account state cleanly. */
00081     if ( mAccount->checkingMail() ) {
00082        mAccount->killAllJobs();
00083     }
00084   }
00085   writeConfig();
00086   if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed(this);
00087   mMetaDataMap.setAutoDelete( true );
00088   mMetaDataMap.clear();
00089 }
00090 
00091 
00092 //-----------------------------------------------------------------------------
00093 void KMFolderImap::close(bool aForced)
00094 {
00095   if (mOpenCount <= 0 ) return;
00096   if (mOpenCount > 0) mOpenCount--;
00097   if (mOpenCount > 0 && !aForced) return;
00098   // FIXME is this still needed?
00099   if (mAccount) 
00100     mAccount->ignoreJobsForFolder( this );
00101   int idx = count();
00102   while (--idx >= 0) {
00103     if ( mMsgList[idx]->isMessage() ) {
00104       KMMessage *msg = static_cast<KMMessage*>(mMsgList[idx]);
00105       if (msg->transferInProgress())
00106           msg->setTransferInProgress( false );
00107     }
00108   }
00109   // The inherited close will decrement again, so we have to adjust.
00110   mOpenCount++;
00111   KMFolderMbox::close(aForced);
00112 }
00113 
00114 //-----------------------------------------------------------------------------
00115 KMMessage* KMFolderImap::getMsg(int idx)
00116 {
00117   if(!(idx >= 0 && idx <= count()))
00118     return 0;
00119 
00120   KMMsgBase* mb = getMsgBase(idx);
00121   if (!mb) return 0;
00122   if (mb->isMessage()) 
00123   {
00124     return ((KMMessage*)mb);
00125   } else {
00126     KMMessage* msg = KMFolder::getMsg( idx );
00127     if ( msg ) // set it incomplete as the msg was not transferred from the server
00128       msg->setComplete( false );
00129     return msg;
00130   }
00131 }
00132 
00133 //-----------------------------------------------------------------------------
00134 void KMFolderImap::setAccount(KMAcctImap *aAccount)
00135 {
00136   mAccount = aAccount;
00137   if (!mChild) return;
00138   KMFolderNode* node;
00139   for (node = mChild->first(); node; node = mChild->next())
00140   {
00141     if (!node->isDir())
00142       static_cast<KMFolderImap*>(node)->setAccount(aAccount);
00143   }
00144 }
00145 
00146 //-----------------------------------------------------------------------------
00147 void KMFolderImap::readConfig()
00148 {
00149   KConfig* config = KMKernel::config();
00150   KConfigGroupSaver saver(config, "Folder-" + idString());
00151   mCheckMail = config->readBoolEntry("checkmail", true);
00152   KMFolderMbox::readConfig();
00153 }
00154 
00155 //-----------------------------------------------------------------------------
00156 void KMFolderImap::writeConfig()
00157 {
00158   KConfig* config = KMKernel::config();
00159   KConfigGroupSaver saver(config, "Folder-" + idString());
00160   config->writeEntry("checkmail", mCheckMail);
00161   config->writeEntry("UidValidity", mUidValidity);
00162   config->writeEntry("ImapPath", mImapPath);
00163   config->writeEntry("NoContent", mNoContent);
00164   config->writeEntry("ReadOnly", mReadOnly);
00165   KMFolderMbox::writeConfig();
00166 }
00167 
00168 //-----------------------------------------------------------------------------
00169 void KMFolderImap::removeOnServer()
00170 {
00171   KURL url = mAccount->getUrl();
00172   url.setPath(imapPath());
00173   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
00174     return;
00175   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
00176   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
00177   ImapAccountBase::jobData jd(url.url());
00178   mAccount->insertJob(job, jd);
00179   connect(job, SIGNAL(result(KIO::Job *)),
00180           this, SLOT(slotRemoveFolderResult(KIO::Job *)));
00181 }
00182 
00183 //-----------------------------------------------------------------------------
00184 void KMFolderImap::slotRemoveFolderResult(KIO::Job *job)
00185 {
00186   ImapAccountBase::JobIterator it = mAccount->findJob(job);
00187   if ( it == mAccount->jobsEnd() ) return;
00188   mAccount->removeJob(it);
00189   if (job->error())
00190   {
00191     mAccount->slotSlaveError( mAccount->slave(), job->error(),
00192         job->errorText() );
00193   } else {
00194     mAccount->displayProgress();
00195     kmkernel->imapFolderMgr()->remove(this);
00196   }
00197 }
00198 
00199 //-----------------------------------------------------------------------------
00200 void KMFolderImap::removeMsg(int idx, bool quiet)
00201 {
00202   if (idx < 0)
00203     return;
00204 
00205   if (!quiet)
00206   {
00207     KMMessage *msg = getMsg(idx);
00208     deleteMessage(msg);
00209   }
00210 
00211   mLastUid = 0;
00212   KMFolderMbox::removeMsg(idx);
00213 }
00214 
00215 void KMFolderImap::removeMsg(QPtrList<KMMessage> msgList, bool quiet)
00216 {
00217   if (!quiet)
00218     deleteMessage(msgList);
00219 
00220   mLastUid = 0;
00221 
00222   /* Remove the messages from the local store as well.
00223      We don't call KMFolderInherited::removeMsg(QPtrList<KMMessage>) but
00224      iterate ourselves, as that would call KMFolderImap::removeMsg(int)
00225      and not the one from the store we want to be used. */
00226   for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00227   {
00228     int idx = find(msg);
00229     assert( idx != -1);
00230     // ATTENTION port me to maildir
00231     KMFolderMbox::removeMsg(idx, quiet);
00232   }
00233 }
00234 
00235 //-----------------------------------------------------------------------------
00236 int KMFolderImap::rename( const QString& newName, KMFolderDir */*aParent*/ )
00237 {
00238   if ( newName == name() )
00239     return 0;
00240 
00241   QString path = imapPath();
00242   int i = path.findRev( '.' );
00243   path = path.left( i );
00244   path += "." + newName;
00245   KURL src( mAccount->getUrl() );
00246   src.setPath( imapPath() );
00247   KURL dst( mAccount->getUrl() );
00248   dst.setPath( path );
00249   KIO::SimpleJob *job = KIO::rename( src, dst, true );
00250   kdDebug(5006)<< "### Rename : " << src.prettyURL()
00251            << " |=> " << dst.prettyURL()
00252            << endl;
00253   KIO::Scheduler::assignJobToSlave( mAccount->slave(), job );
00254   connect( job, SIGNAL(result(KIO::Job*)),
00255            SLOT(slotRenameResult(KIO::Job*)) );
00256   setImapPath( path );
00257   return 0;
00258 }
00259 
00260 //-----------------------------------------------------------------------------
00261 void KMFolderImap::slotRenameResult( KIO::Job *job )
00262 {
00263    KIO::SimpleJob* sj = static_cast<KIO::SimpleJob*>(job);
00264   if ( job->error() ) {
00265     setImapPath( sj->url().path() );
00266     mAccount->slotSlaveError( mAccount->slave(), job->error(),
00267                               job->errorText() );
00268     return;
00269   }
00270   QString path = imapPath();
00271   int i = path.findRev( '.' );
00272   path = path.mid( ++i );
00273   path.remove( '/' );
00274   KMFolderMbox::rename( path );
00275   kmkernel->folderMgr()->contentsChanged();
00276 }
00277 
00278 //-----------------------------------------------------------------------------
00279 void KMFolderImap::addMsgQuiet(KMMessage* aMsg)
00280 {
00281   KMFolder *folder = aMsg->parent();
00282   Q_UINT32 serNum = 0;
00283   aMsg->setTransferInProgress( false );
00284   if (folder) {
00285     serNum = aMsg->getMsgSerNum();
00286     kmkernel->undoStack()->pushSingleAction( serNum, folder, this );
00287     int idx = folder->find( aMsg );
00288     assert( idx != -1 );
00289     folder->take( idx );
00290   }
00291   // Remember the status, so it can be transfered to the new message.
00292   mMetaDataMap.insert(aMsg->msgIdMD5(), new KMMsgMetaData(aMsg->status(), serNum));
00293 
00294   delete aMsg;
00295   aMsg = 0;
00296   getFolder();
00297 }
00298 
00299 //-----------------------------------------------------------------------------
00300 void KMFolderImap::addMsgQuiet(QPtrList<KMMessage> msgList)
00301 {
00302   KMFolder *folder = msgList.first()->parent();
00303   Q_UINT32 serNum = 0;
00304   if (folder) serNum = msgList.first()->getMsgSerNum();
00305   int undoId = -1;
00306   for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00307   {
00308     if ( undoId == -1 )
00309       undoId = kmkernel->undoStack()->newUndoAction( folder, this );
00310     kmkernel->undoStack()->addMsgToAction( undoId, msg->getMsgSerNum() );
00311     // Remember the status, so it can be transfered to the new message.
00312     mMetaDataMap.insert(msg->msgIdMD5(), new KMMsgMetaData(msg->status(), serNum));
00313     msg->setTransferInProgress( false );
00314   }
00315   if (folder) folder->take(msgList);
00316   msgList.setAutoDelete(true);
00317   msgList.clear();
00318   getFolder();
00319 }
00320 
00321 //-----------------------------------------------------------------------------
00322 int KMFolderImap::addMsg(KMMessage* aMsg, int* aIndex_ret)
00323 {
00324   QPtrList<KMMessage> list; list.append(aMsg);
00325   return addMsg(list, aIndex_ret);
00326 }
00327 
00328 int KMFolderImap::addMsg(QPtrList<KMMessage>& msgList, int* aIndex_ret)
00329 {
00330   KMMessage *aMsg = msgList.getFirst();
00331   KMFolder *msgParent = aMsg->parent();
00332 
00333   ImapJob *imapJob = 0;
00334   if (msgParent)
00335   {
00336     if (msgParent->folderType() == KMFolderTypeImap)
00337     {
00338       if (static_cast<KMFolderImap*>(msgParent)->account() == account())
00339       {
00340         // make sure the messages won't be deleted while we work with them
00341         for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00342           msg->setTransferInProgress(true);
00343 
00344         if (this == msgParent)
00345         {
00346           // transfer the whole message, e.g. a draft-message is canceled and re-added to the drafts-folder
00347           for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00348           {
00349             if (!msg->isComplete())
00350             {
00351               int idx = msgParent->find(msg);
00352               assert(idx != -1);
00353               msg = msgParent->getMsg(idx);
00354             }
00355             imapJob = new ImapJob(msg, ImapJob::tPutMessage, this);
00356             connect(imapJob, SIGNAL(messageStored(KMMessage*)),
00357                 SLOT(addMsgQuiet(KMMessage*)));
00358             imapJob->start();
00359           }
00360 
00361         } else {
00362 
00363           // get the messages and the uids
00364           QValueList<int> uids;
00365           getUids(msgList, uids);
00366 
00367           // get the sets (do not sort the uids)
00368           QStringList sets = makeSets(uids, false);
00369 
00370           for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
00371           {
00372             // we need the messages that belong to the current set to pass them to the ImapJob
00373             QPtrList<KMMessage> temp_msgs = splitMessageList(*it, msgList);
00374 
00375             imapJob = new ImapJob(temp_msgs, *it, ImapJob::tMoveMessage, this);
00376             connect(imapJob, SIGNAL(messageCopied(QPtrList<KMMessage>)),
00377                 SLOT(addMsgQuiet(QPtrList<KMMessage>)));
00378             imapJob->start();
00379           }
00380         }
00381         if (aIndex_ret) *aIndex_ret = -1;
00382         return 0;
00383       }
00384       else
00385       {
00386         // different account, check if messages can be added
00387         QPtrListIterator<KMMessage> it( msgList );
00388         KMMessage *msg;
00389         while ( (msg = it.current()) != 0 )
00390         {
00391           ++it;
00392           if (!canAddMsgNow(msg, aIndex_ret))
00393             msgList.remove(msg);
00394           else {
00395             if (!msg->transferInProgress())
00396               msg->setTransferInProgress(true);
00397           }
00398         }
00399       }
00400     } // if imap
00401   }
00402 
00403   for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00404   {
00405     // transfer from local folders or other accounts
00406     if (msgParent && !msg->isMessage())
00407     {
00408       int idx = msgParent->find(msg);
00409       assert(idx != -1);
00410       msg = msgParent->getMsg(idx);
00411     }
00412     if (!msg->transferInProgress())
00413       msg->setTransferInProgress(true);
00414     imapJob = new ImapJob(msg, ImapJob::tPutMessage, this);
00415     connect(imapJob, SIGNAL(messageStored(KMMessage*)),
00416         SLOT(addMsgQuiet(KMMessage*)));
00417     imapJob->start();
00418   }
00419 
00420   if (aIndex_ret) *aIndex_ret = -1;
00421   return 0;
00422 }
00423 
00424 //-----------------------------------------------------------------------------
00425 void KMFolderImap::copyMsg(QPtrList<KMMessage>& msgList)
00426 {
00427   for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) {
00428     // Remember the status, so it can be transfered to the new message.
00429     mMetaDataMap.insert(msg->msgIdMD5(), new KMMsgMetaData(msg->status()));
00430   }
00431   QValueList<int> uids;
00432   getUids(msgList, uids);
00433   QStringList sets = makeSets(uids, false);
00434   for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
00435   {
00436     // we need the messages that belong to the current set to pass them to the ImapJob
00437     QPtrList<KMMessage> temp_msgs = splitMessageList(*it, msgList);
00438 
00439     ImapJob *job = new ImapJob(temp_msgs, *it, ImapJob::tCopyMessage, this);
00440     job->start();
00441   }
00442 }
00443 
00444 //-----------------------------------------------------------------------------
00445 QPtrList<KMMessage> KMFolderImap::splitMessageList(QString set, QPtrList<KMMessage>& msgList)
00446 {
00447   int lastcomma = set.findRev(",");
00448   int lastdub = set.findRev(":");
00449   int last = 0;
00450   if (lastdub > lastcomma) last = lastdub;
00451   else last = lastcomma;
00452   last++;
00453   if (last < 0) last = set.length();
00454   // the last uid of the current set
00455   QString last_uid = set.right(set.length() - last);
00456   QPtrList<KMMessage> temp_msgs;
00457   QString uid;
00458   if (!last_uid.isEmpty())
00459   {
00460     QPtrListIterator<KMMessage> it( msgList );
00461     KMMessage* msg = 0;
00462     while ( (msg = it.current()) != 0 )
00463     {
00464       // append the msg to the new list and delete it from the old
00465       temp_msgs.append(msg);
00466       uid = msg->headerField("X-UID");
00467       // remove modifies the current
00468       msgList.remove(msg);
00469       if (uid == last_uid) break;
00470     }
00471   }
00472   else
00473   {
00474     // probably only one element
00475     temp_msgs = msgList;
00476   }
00477 
00478   return temp_msgs;
00479 }
00480 
00481 //-----------------------------------------------------------------------------
00482 KMMessage* KMFolderImap::take(int idx)
00483 {
00484   KMMsgBase* mb(mMsgList[idx]);
00485   if (!mb) return 0;
00486   if (!mb->isMessage()) readMsg(idx);
00487 
00488   KMMessage *msg = static_cast<KMMessage*>(mb);
00489   deleteMessage(msg);
00490 
00491   mLastUid = 0;
00492   return KMFolderMbox::take(idx);
00493 }
00494 
00495 void KMFolderImap::take(QPtrList<KMMessage> msgList)
00496 {
00497   deleteMessage(msgList);
00498 
00499   mLastUid = 0;
00500   KMFolderMbox::take(msgList);
00501 }
00502 
00503 //-----------------------------------------------------------------------------
00504 bool KMFolderImap::listDirectory(bool secondStep)
00505 {
00506   mSubfolderState = imapInProgress;
00507   if ( mAccount->makeConnection() == ImapAccountBase::Error )
00508     return false;
00509 
00510   // connect to folderlisting
00511   connect(mAccount, SIGNAL(receivedFolders(QStringList, QStringList,
00512           QStringList, const ImapAccountBase::jobData &)),
00513       this, SLOT(slotListResult(QStringList, QStringList,
00514           QStringList, const ImapAccountBase::jobData &)));
00515 
00516   // start a new listing for the root-folder
00517   bool reset = (mImapPath == mAccount->prefix() && 
00518                 !secondStep && !mIsSystemFolder) ? true : false;
00519 
00520   // get the folders
00521   mAccount->listDirectory(mImapPath, mAccount->onlySubscribedFolders(),
00522       secondStep, this, reset);
00523 
00524   return true;
00525 }
00526 
00527 
00528 //-----------------------------------------------------------------------------
00529 void KMFolderImap::slotListResult( QStringList mSubfolderNames,
00530                                    QStringList mSubfolderPaths,
00531                                    QStringList mSubfolderMimeTypes,
00532                                    const ImapAccountBase::jobData & jobData )
00533 {
00534   if (jobData.parent) {
00535     // the account is connected to several folders, so we
00536     // have to sort out if this result is for us
00537     if (jobData.parent != this) return;
00538   }
00539   // disconnect to avoid recursions
00540   disconnect(mAccount, SIGNAL(receivedFolders(QStringList, QStringList,
00541           QStringList, const ImapAccountBase::jobData &)),
00542       this, SLOT(slotListResult(QStringList, QStringList,
00543           QStringList, const ImapAccountBase::jobData &)));
00544 
00545   mSubfolderState = imapFinished;
00546   bool it_inboxOnly = jobData.inboxOnly;
00547 //  kdDebug(5006) << name() << ": " << mSubfolderNames.join(",") << "; inboxOnly:" << it_inboxOnly
00548 //    << ", createinbox:" << mAccount->createInbox() << ", hasinbox:" << mAccount->hasInbox() << endl;
00549   // don't react on changes
00550   kmkernel->imapFolderMgr()->quiet(TRUE);
00551   if (it_inboxOnly) {
00552     // list again only for the INBOX
00553     listDirectory(TRUE);
00554   } else {
00555     if (mIsSystemFolder && mImapPath == "/INBOX/"
00556         && mAccount->prefix() == "/INBOX/")
00557     {
00558       // do not create folders under INBOX
00559       mAccount->setCreateInbox(FALSE);
00560       mSubfolderNames.clear();
00561     }
00562     createChildFolder();
00563     KMFolderImap *folder;
00564     KMFolderNode *node = mChild->first();
00565     while (node)
00566     {
00567       // check if the folders still exist on the server
00568       if (!node->isDir() && (node->name().upper() != "INBOX" || !mAccount->createInbox())
00569           && mSubfolderNames.findIndex(node->name()) == -1)
00570       {
00571         kdDebug(5006) << node->name() << " disappeared." << endl;
00572         kmkernel->imapFolderMgr()->remove(static_cast<KMFolder*>(node));
00573         node = mChild->first();
00574       }
00575       else node = mChild->next();
00576     }
00577     if (mAccount->createInbox())
00578     {
00579       // create the INBOX
00580       for (node = mChild->first(); node; node = mChild->next())
00581         if (!node->isDir() && node->name() == "INBOX") break;
00582       if (node) folder = static_cast<KMFolderImap*>(node);
00583       else folder = static_cast<KMFolderImap*>
00584         (mChild->createFolder("INBOX", TRUE));
00585       folder->setAccount(mAccount);
00586       folder->setImapPath("/INBOX/");
00587       folder->setLabel(i18n("inbox"));
00588       if (!node) folder->close();
00589       // so we have an INBOX
00590       mAccount->setCreateInbox( false );
00591       mAccount->setHasInbox( true );
00592       folder->listDirectory();
00593       kmkernel->imapFolderMgr()->contentsChanged();
00594     }
00595     for (uint i = 0; i < mSubfolderNames.count(); i++)
00596     {
00597       // create folders if necessary
00598       if (mSubfolderNames[i].upper() == "INBOX" &&
00599           mSubfolderPaths[i].upper() == "/INBOX/" &&
00600           mAccount->hasInbox()) // do not create an additional inbox
00601         continue;
00602       for (node = mChild->first(); node; node = mChild->next())
00603         if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
00604       if (node) folder = static_cast<KMFolderImap*>(node);
00605       else {
00606         folder = static_cast<KMFolderImap*>
00607           (mChild->createFolder(mSubfolderNames[i]));
00608         if (folder)
00609         {
00610           folder->close();
00611           kmkernel->imapFolderMgr()->contentsChanged();
00612         } else {
00613           kdWarning(5006) << "can't create folder " << mSubfolderNames[i] << endl;
00614         }
00615       }
00616       if (folder)
00617       {
00618         folder->setAccount(mAccount);
00619         folder->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
00620         folder->setImapPath(mSubfolderPaths[i]);
00621         if (mSubfolderMimeTypes[i] == "message/directory" ||
00622             mSubfolderMimeTypes[i] == "inode/directory")
00623           folder->listDirectory();
00624       }
00625     }
00626   }
00627   // now others should react on the changes
00628   kmkernel->imapFolderMgr()->quiet(FALSE);
00629 }
00630 
00631 //-----------------------------------------------------------------------------
00632 void KMFolderImap::checkValidity()
00633 {
00634   if (!mAccount) {
00635     emit folderComplete(this, false);
00636     return;
00637   }
00638   KURL url = mAccount->getUrl();
00639   url.setPath(imapPath() + ";UID=0:0");
00640   kdDebug(5006) << "KMFolderImap::checkValidity of: " << imapPath() << endl;
00641   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
00642   {
00643     kdWarning(5006) << "KMFolderImap::checkValidity - got no connection" << endl;
00644     emit folderComplete(this, FALSE);
00645     return;
00646   }
00647   // Only check once at a time.
00648   if (mCheckingValidity) {
00649     kdDebug(5006) << "KMFolderImap::checkValidity - already checking" << endl;
00650     return;
00651   }
00652   ImapAccountBase::jobData jd( url.url(), this );
00653   KIO::SimpleJob *job = KIO::get(url, FALSE, FALSE);
00654   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
00655   mAccount->insertJob(job, jd);
00656   connect(job, SIGNAL(result(KIO::Job *)),
00657           SLOT(slotCheckValidityResult(KIO::Job *)));
00658   connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
00659           SLOT(slotSimpleData(KIO::Job *, const QByteArray &)));
00660   // Only check once at a time.
00661   mCheckingValidity = true;
00662   
00663 }
00664 
00665 
00666 //-----------------------------------------------------------------------------
00667 ulong KMFolderImap::lastUid()
00668 {
00669   if (mLastUid) return mLastUid;
00670   open();
00671   if (count() > 0)
00672   {
00673     bool unget = !isMessage(count() - 1);
00674     KMMessage *msg = getMsg(count() - 1);
00675     mLastUid = msg->headerField("X-UID").toULong();
00676     if (unget) unGetMsg(count() - 1);
00677   }
00678   close();
00679   return mLastUid;
00680 }
00681 
00682 
00683 //-----------------------------------------------------------------------------
00684 void KMFolderImap::slotCheckValidityResult(KIO::Job * job)
00685 {
00686   kdDebug(5006) << "KMFolderImap::slotCheckValidityResult of: " << fileName() << endl;
00687   mCheckingValidity = false;
00688   ImapAccountBase::JobIterator it = mAccount->findJob(job);
00689   if ( it == mAccount->jobsEnd() ) return;
00690   if (job->error()) {
00691     mAccount->slotSlaveError(mAccount->slave(), job->error(), job->errorText());
00692     emit folderComplete(this, FALSE);
00693     mAccount->displayProgress();
00694   } else {
00695     QCString cstr((*it).data.data(), (*it).data.size() + 1);
00696     int a = cstr.find("X-uidValidity: ");
00697     int b = cstr.find("\r\n", a);
00698     QString uidv;
00699     if ( (b - a - 15) >= 0 ) uidv = cstr.mid(a + 15, b - a - 15);
00700     a = cstr.find("X-Access: ");
00701     b = cstr.find("\r\n", a);
00702     QString access;
00703     if ( (b - a - 10) >= 0 ) access = cstr.mid(a + 10, b - a - 10);
00704     mReadOnly = access == "Read only";
00705     QString startUid;
00706     if (uidValidity() != uidv)
00707     {
00708       // uidValidity changed
00709       kdDebug(5006) << "KMFolderImap::slotCheckValidityResult uidValidty changed." << endl;
00710       mAccount->ignoreJobsForFolder(this);
00711       expunge();
00712       mLastUid = 0;
00713       uidmap.clear();
00714       setUidValidity(uidv);
00715     } else {
00716       if (!mCheckFlags)
00717         startUid = QString::number(lastUid() + 1);
00718     }
00719     mAccount->removeJob(it);
00720     reallyGetFolder(startUid);
00721   }
00722 }
00723 
00724 //-----------------------------------------------------------------------------
00725 void KMFolderImap::getAndCheckFolder(bool force)
00726 {
00727   if (mNoContent)
00728     return getFolder(force);
00729 
00730   if ( mAccount )
00731     mAccount->processNewMailSingleFolder(this);
00732   if (force) {
00733     // force an update
00734     mCheckFlags = TRUE;
00735   }
00736 }
00737 
00738 //-----------------------------------------------------------------------------
00739 void KMFolderImap::getFolder(bool force)
00740 {
00741   mGuessedUnreadMsgs = -1;
00742   if (mNoContent)
00743   {
00744     mContentState = imapFinished;
00745     emit folderComplete(this, true);
00746     return;
00747   }
00748   mContentState = imapInProgress;
00749   if (force) {
00750     // force an update
00751     mCheckFlags = TRUE;
00752   }
00753   checkValidity();
00754 }
00755 
00756 
00757 //-----------------------------------------------------------------------------
00758 void KMFolderImap::reallyGetFolder(const QString &startUid)
00759 {
00760   KURL url = mAccount->getUrl();
00761   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
00762   {
00763     emit folderComplete(this, FALSE);
00764     mAccount->displayProgress();
00765     return;
00766   }
00767   quiet(true);
00768   if (startUid.isEmpty())
00769   {
00770     url.setPath(imapPath() + ";SECTION=UID FLAGS");
00771     KIO::SimpleJob *job = KIO::listDir(url, FALSE);
00772     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
00773     ImapAccountBase::jobData jd( url.url(), this );
00774     mAccount->insertJob(job, jd);
00775     connect(job, SIGNAL(result(KIO::Job *)),
00776             this, SLOT(slotListFolderResult(KIO::Job *)));
00777     connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
00778             this, SLOT(slotListFolderEntries(KIO::Job *,
00779             const KIO::UDSEntryList &)));
00780   } else {
00781     url.setPath(imapPath() + ";UID=" + startUid
00782       + ":*;SECTION=ENVELOPE");
00783     KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
00784     KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
00785     ImapAccountBase::jobData jd( url.url(), this );
00786     mAccount->insertJob(newJob, jd);
00787     connect(newJob, SIGNAL(result(KIO::Job *)),
00788             this, SLOT(slotGetLastMessagesResult(KIO::Job *)));
00789     connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
00790             this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
00791   }
00792 }
00793 
00794 
00795 //-----------------------------------------------------------------------------
00796 void KMFolderImap::slotListFolderResult(KIO::Job * job)
00797 {
00798   ImapAccountBase::JobIterator it = mAccount->findJob(job);
00799   if ( it == mAccount->jobsEnd() ) return;
00800   QString uids;
00801   if (job->error())
00802   {
00803     mAccount->slotSlaveError( mAccount->slave(), job->error(),
00804         job->errorText() );
00805     quiet( false );
00806     emit folderComplete(this, FALSE);
00807     mAccount->removeJob(it);
00808     mAccount->displayProgress();
00809     return;
00810   }
00811   mCheckFlags = FALSE;
00812   QStringList::Iterator uid;
00813   // Check for already retrieved headers
00814   if (count())
00815   {
00816     QCString cstr;
00817     int idx = 0, a, b, c, serverFlags;
00818     long int mailUid, serverUid;
00819     uid = (*it).items.begin();
00820     while (idx < count() && uid != (*it).items.end())
00821     {
00822       getMsgString(idx, cstr);
00823       a = cstr.find("X-UID: ");
00824       b = cstr.find("\n", a);
00825       if (a == -1 || b == -1) mailUid = -1;
00826       else mailUid = cstr.mid(a + 7, b - a - 7).toLong();
00827       c = (*uid).find(",");
00828       serverUid = (*uid).left(c).toLong();
00829       serverFlags = (*uid).mid(c+1).toInt();
00830       if (mailUid < serverUid) removeMsg(idx, TRUE);
00831       else if (mailUid == serverUid)
00832       {
00833         if (!mReadOnly)
00834           flagsToStatus(getMsgBase(idx), serverFlags, false);
00835         idx++;
00836         uid = (*it).items.remove(uid);
00837       }
00838       else break;  // happens only, if deleted mails reappear on the server
00839     }
00840     while (idx < count()) removeMsg(idx, TRUE);
00841   }
00842   for (uid = (*it).items.begin(); uid != (*it).items.end(); uid++)
00843     (*uid).truncate((*uid).find(","));
00844   ImapAccountBase::jobData jd( QString::null, (*it).parent );
00845 //jd.items = (*it).items;
00846   jd.total = (*it).items.count();
00847   if (jd.total == 0)
00848   {
00849     quiet(false);
00850     mContentState = imapFinished;
00851     emit folderComplete(this, TRUE);
00852     mAccount->removeJob(it);
00853     mAccount->displayProgress();
00854     return;
00855   }
00856 
00857   QStringList sets;
00858   uid = (*it).items.begin();
00859   if (jd.total == 1) sets.append(*uid + ":" + *uid);
00860   else sets = makeSets( (*it).items );
00861   mAccount->removeJob(it); // don't use *it below
00862 
00863   for (QStringList::Iterator i = sets.begin(); i != sets.end(); ++i)
00864   {
00865     KURL url = mAccount->getUrl();
00866     url.setPath(imapPath() + ";UID=" + *i + ";SECTION=ENVELOPE");
00867     if ( mAccount->makeConnection() != ImapAccountBase::Connected )
00868     {
00869       quiet(false);
00870       emit folderComplete(this, FALSE);
00871       return;
00872     }
00873     KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
00874     jd.url = url.url();
00875     KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
00876     mAccount->insertJob(newJob, jd);
00877     connect(newJob, SIGNAL(result(KIO::Job *)),
00878         this, (i == sets.at(sets.count() - 1))
00879         ? SLOT(slotGetLastMessagesResult(KIO::Job *))
00880         : SLOT(slotGetMessagesResult(KIO::Job *)));
00881     connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
00882         this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
00883   }
00884 }
00885 
00886 
00887 //-----------------------------------------------------------------------------
00888 void KMFolderImap::slotListFolderEntries(KIO::Job * job,
00889   const KIO::UDSEntryList & uds)
00890 {
00891   ImapAccountBase::JobIterator it = mAccount->findJob(job);
00892   if ( it == mAccount->jobsEnd() ) return;
00893   QString mimeType, name;
00894   long int flags = 0;
00895   for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
00896     udsIt != uds.end(); udsIt++)
00897   {
00898     for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
00899       eIt != (*udsIt).end(); eIt++)
00900     {
00901       if ((*eIt).m_uds == KIO::UDS_NAME)
00902         name = (*eIt).m_str;
00903       else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
00904         mimeType = (*eIt).m_str;
00905       else if ((*eIt).m_uds == KIO::UDS_ACCESS)
00906         flags = (*eIt).m_long;
00907     }
00908     if (mimeType == "message/rfc822-imap" && !(flags & 8))
00909       (*it).items.append(name + "," + QString::number(flags));
00910   }
00911 }
00912 
00913 
00914 //-----------------------------------------------------------------------------
00915 void KMFolderImap::flagsToStatus(KMMsgBase *msg, int flags, bool newMsg)
00916 {
00917   if (flags & 4) 
00918     msg->setStatus( KMMsgStatusFlag );
00919   if (flags & 2)
00920     msg->setStatus( KMMsgStatusReplied );
00921   if (flags & 1)
00922     msg->setStatus( KMMsgStatusOld );
00923 
00924   if (msg->isOfUnknownStatus()) {
00925     if (newMsg)
00926       msg->setStatus( KMMsgStatusNew );
00927     else
00928       msg->setStatus( KMMsgStatusUnread );
00929   }
00930 }
00931 
00932 
00933 //-----------------------------------------------------------------------------
00934 QString KMFolderImap::statusToFlags(KMMsgStatus status)
00935 {
00936   QString flags;
00937   if (status & KMMsgStatusDeleted) 
00938     flags = "\\DELETED";
00939   else {
00940     if (status & KMMsgStatusOld || status & KMMsgStatusRead) 
00941       flags = "\\SEEN ";
00942     if (status & KMMsgStatusReplied) 
00943       flags += "\\ANSWERED ";
00944     if (status & KMMsgStatusFlag) 
00945       flags += "\\FLAGGED";
00946   }
00947   
00948   return flags.simplifyWhiteSpace();
00949 }
00950 
00951 //-------------------------------------------------------------
00952 void
00953 KMFolderImap::ignoreJobsForMessage( KMMessage* msg )
00954 {
00955   if ( !msg || msg->transferInProgress() || 
00956        !msg->parent() || msg->parent()->folderType() != KMFolderTypeImap )
00957     return;
00958   KMAcctImap *account;
00959   if ( !(account = static_cast<KMFolderImap*>(msg->parent())->account()) )
00960     return;
00961 
00962   account->ignoreJobsForMessage( msg );
00963 }
00964 
00965 //-----------------------------------------------------------------------------
00966 void KMFolderImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
00967 {
00968   if ( data.isEmpty() ) return; // optimization
00969   ImapAccountBase::JobIterator it = mAccount->findJob(job);
00970   if ( it == mAccount->jobsEnd() ) return;
00971   (*it).cdata += QCString(data, data.size() + 1);
00972   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
00973   if (pos > 0)
00974   {
00975     int p = (*it).cdata.find("\r\nX-uidValidity:");
00976     if (p != -1) setUidValidity((*it).cdata
00977       .mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
00978     int c = (*it).cdata.find("\r\nX-Count:");
00979     if ( c != -1 )
00980     {
00981       bool ok;
00982       int exists = (*it).cdata.mid( c+10, 
00983           (*it).cdata.find("\r\n", c+1) - c-10 ).toInt(&ok);
00984       if ( ok && exists < count() )
00985       {
00986         kdDebug(5006) << "KMFolderImap::slotGetMessagesData - server has less messages (" << 
00987           exists << ") then folder (" << count() << "), so reload" << endl;
00988         mAccount->displayProgress();
00989         reallyGetFolder( QString::null );
00990         (*it).cdata.remove(0, pos);
00991         return;
00992       }
00993     }
00994     (*it).cdata.remove(0, pos);
00995   }
00996   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
00997   int flags;
00998   while (pos >= 0)
00999   {
01000     KMMessage *msg = new KMMessage;
01001     msg->fromString((*it).cdata.mid(16, pos - 16));
01002     flags = msg->headerField("X-Flags").toInt();
01003     ulong uid = msg->headerField("X-UID").toULong();
01004     if (flags & 8 || uid <= lastUid()) {
01005       delete msg;
01006       msg = 0;
01007     }
01008     else {
01009       if (uidmap.find(uid))
01010       {
01011         // assign the sernum from the cache
01012         const ulong sernum = (ulong) uidmap[uid];
01013         msg->setMsgSerNum(sernum);
01014         // delete the entry
01015         uidmap.remove(uid);
01016       }
01017       open();
01018       KMFolderMbox::addMsg(msg, 0);
01019       // Transfer the status, if it is cached.
01020       QString id = msg->msgIdMD5();
01021       if ( mMetaDataMap.find( id ) ) {
01022         KMMsgMetaData *md =  mMetaDataMap[id];
01023         msg->setStatus( md->status() );
01024         if ( md->serNum() != 0 )
01025           msg->setMsgSerNum( md->serNum() );
01026         mMetaDataMap.remove( id );
01027         delete md;
01028       }
01029       // Merge with the flags from the server.
01030       flagsToStatus((KMMsgBase*)msg, flags);
01031       // set the correct size
01032       msg->setMsgLength( msg->headerField("X-Length").toUInt() );
01033       close();
01034 
01035       if (count() > 1) unGetMsg(count() - 1);
01036       mLastUid = uid;
01037     }
01038     (*it).cdata.remove(0, pos);
01039     (*it).done++;
01040     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01041     mAccount->displayProgress();
01042   }
01043 }
01044 
01045 //-------------------------------------------------------------
01046 FolderJob*
01047 KMFolderImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
01048                            KMFolder *folder, QString partSpecifier,
01049                            const AttachmentStrategy *as ) const
01050 {
01051   KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder);
01052   if ( jt == FolderJob::tGetMessage && partSpecifier == "STRUCTURE" &&
01053        mAccount && mAccount->loadOnDemand() &&
01054        ( msg->signatureState() == KMMsgNotSigned || 
01055          msg->signatureState() == KMMsgSignatureStateUnknown ) )
01056   {
01057     // load-on-demand: retrieve the BODYSTRUCTURE and to speed things up also the headers
01058     // this is not activated for small or signed messages
01059     ImapJob *job = new ImapJob( msg, jt, kmfi, "HEADER" );
01060     job->start();
01061     ImapJob *job2 = new ImapJob( msg, jt, kmfi, "STRUCTURE", as );
01062     job2->start();
01063     return job;
01064   } else {
01065     // download complete message or part (attachment)
01066     if ( partSpecifier == "STRUCTURE" ) // hide from outside
01067       partSpecifier = QString::null;
01068 
01069     ImapJob *job = new ImapJob( msg, jt, kmfi, partSpecifier );
01070     return job;
01071   }
01072 }
01073 
01074 //-------------------------------------------------------------
01075 FolderJob*
01076 KMFolderImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
01077                            FolderJob::JobType jt, KMFolder *folder ) const
01078 {
01079   KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder);
01080   ImapJob *job = new ImapJob( msgList, sets, jt, kmfi );
01081   return job;
01082 }
01083 
01084 //-----------------------------------------------------------------------------
01085 void KMFolderImap::getMessagesResult(KIO::Job * job, bool lastSet)
01086 {
01087   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01088   if ( it == mAccount->jobsEnd() ) return;
01089   if (job->error())
01090   {
01091     mAccount->slotSlaveError( mAccount->slave(), job->error(),
01092         job->errorText() );
01093     mContentState = imapNoInformation;
01094     quiet( false );
01095     emit folderComplete(this, false);
01096   }
01097   else
01098   {
01099     if (lastSet)
01100     {
01101       mContentState = imapFinished;
01102       quiet(false);
01103       emit folderComplete(this, true);
01104     }
01105     mAccount->removeJob(it);
01106   }
01107 
01108   mAccount->displayProgress();
01109 }
01110 
01111 
01112 //-----------------------------------------------------------------------------
01113 void KMFolderImap::slotGetLastMessagesResult(KIO::Job * job)
01114 {
01115   getMessagesResult(job, true);
01116 }
01117 
01118 
01119 //-----------------------------------------------------------------------------
01120 void KMFolderImap::slotGetMessagesResult(KIO::Job * job)
01121 {
01122   getMessagesResult(job, false);
01123 }
01124 
01125 
01126 //-----------------------------------------------------------------------------
01127 void KMFolderImap::createFolder(const QString &name)
01128 {
01129   KURL url = mAccount->getUrl();
01130   url.setPath(imapPath() + name);
01131   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01132     return;
01133   KIO::SimpleJob *job = KIO::mkdir(url);
01134   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01135   ImapAccountBase::jobData jd( url.url(), this );
01136   jd.items = name;
01137   mAccount->insertJob(job, jd);
01138   connect(job, SIGNAL(result(KIO::Job *)),
01139           this, SLOT(slotCreateFolderResult(KIO::Job *)));
01140 }
01141 
01142 
01143 //-----------------------------------------------------------------------------
01144 void KMFolderImap::slotCreateFolderResult(KIO::Job * job)
01145 {
01146   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01147   if ( it == mAccount->jobsEnd() ) return;
01148   if (job->error())
01149   {
01150     mAccount->slotSlaveError( mAccount->slave(), job->error(),
01151         job->errorText() );
01152   } else {
01153     listDirectory();
01154   }
01155   mAccount->removeJob(job);
01156 }
01157 
01158 
01159 //-----------------------------------------------------------------------------
01160 static QTextCodec *sUtf7Codec = 0;
01161 
01162 QTextCodec * KMFolderImap::utf7Codec()
01163 {
01164   if (!sUtf7Codec) sUtf7Codec = QTextCodec::codecForName("utf-7");
01165   return sUtf7Codec;
01166 }
01167 
01168 
01169 //-----------------------------------------------------------------------------
01170 QString KMFolderImap::encodeFileName(const QString &name)
01171 {
01172   QString result = utf7Codec()->fromUnicode(name);
01173   return KURL::encode_string_no_slash(result);
01174 }
01175 
01176 
01177 //-----------------------------------------------------------------------------
01178 QString KMFolderImap::decodeFileName(const QString &name)
01179 {
01180   QString result = KURL::decode_string(name);
01181   return utf7Codec()->toUnicode(result.latin1());
01182 }
01183 
01184 //-----------------------------------------------------------------------------
01185 bool KMFolderImap::autoExpunge()
01186 {
01187   if (mAccount)
01188     return mAccount->autoExpunge();
01189 
01190   return false;
01191 }
01192 
01193 
01194 //-----------------------------------------------------------------------------
01195 void KMFolderImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
01196 {
01197   if ( data.isEmpty() ) return; // optimization
01198   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01199   if ( it == mAccount->jobsEnd() ) return;
01200   QBuffer buff((*it).data);
01201   buff.open(IO_WriteOnly | IO_Append);
01202   buff.writeBlock(data.data(), data.size());
01203   buff.close();
01204 }
01205 
01206 //-----------------------------------------------------------------------------
01207 void KMFolderImap::deleteMessage(KMMessage * msg)
01208 {
01209   KURL url = mAccount->getUrl();
01210   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msg->parent());
01211   const QString uid = msg->headerField("X-UID");
01212   /* If the uid is empty the delete job below will nuke all mail in the 
01213      folder, so we better safeguard against that. See ::expungeFolder, as
01214      to why. :( */
01215   if ( uid.isEmpty() ) {
01216      kdDebug( 5006 ) << "KMFolderImap::deleteMessage: Attempt to delete "
01217                         "an empty UID. Aborting."  << endl;
01218      return;
01219   }
01220   url.setPath(msg_parent->imapPath() + ";UID=" + uid );
01221   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01222     return;
01223   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01224   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01225   ImapAccountBase::jobData jd( url.url(), 0 );
01226   mAccount->insertJob(job, jd);
01227   connect(job, SIGNAL(result(KIO::Job *)),
01228           mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01229 }
01230 
01231 void KMFolderImap::deleteMessage(QPtrList<KMMessage> msgList)
01232 {
01233   QValueList<int> uids;
01234   getUids(msgList, uids);
01235   QStringList sets = makeSets(uids);
01236 
01237   KURL url = mAccount->getUrl();
01238   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msgList.first()->parent());
01239   for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
01240   {
01241     const QString uid = *it;
01242     // Don't delete with no uid, that nukes the folder. Should not happen, but
01243     // better safe than sorry.
01244     if ( uid.isEmpty() ) continue;
01245     url.setPath(msg_parent->imapPath() + ";UID=" + uid);
01246     if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01247       return;
01248     KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01249     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01250     ImapAccountBase::jobData jd( url.url(), 0 );
01251     mAccount->insertJob(job, jd);
01252     connect(job, SIGNAL(result(KIO::Job *)),
01253         mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01254   }
01255 }
01256 
01257 //-----------------------------------------------------------------------------
01258 void KMFolderImap::setStatus(int idx, KMMsgStatus status, bool toggle)
01259 {
01260   QValueList<int> ids; ids.append(idx);
01261   setStatus(ids, status, toggle);
01262 }
01263 
01264 void KMFolderImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01265 {
01266   KMFolder::setStatus(ids, status, toggle);
01267   if (mReadOnly) return;
01268 
01269   /* The status has been already set in the local index. Update the flags on
01270    * the server. To avoid doing that for each message individually, group them
01271    * by the status string they will be assigned and make sets for each of those
01272    * groups of mails. This is necessary because the imap kio_slave status job
01273    * does not append flags but overwrites them. Example:
01274    * 
01275    * 2 important mails and 3 unimportant mail, all unread. Mark all as read calls
01276    * this method with a list of uids. The 2 important mails need to get the string
01277    * \SEEN \FLAGGED while the others need to get just \SEEN. Build sets for each
01278    * of those and sort them, so the server can handle them efficiently. */
01279   QMap< QString, QStringList > groups;
01280   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) {
01281     KMMessage *msg = 0;
01282     bool unget = !isMessage(*it);
01283     msg = getMsg(*it);
01284     if (!msg) continue;
01285     QString flags = statusToFlags(msg->status());
01286     // Collect uids for each type of flags.
01287     groups[flags].append(msg->headerField("X-UID"));
01288     if (unget) unGetMsg(*it);
01289   }
01290   QMapIterator< QString, QStringList > dit;
01291   for ( dit = groups.begin(); dit != groups.end(); ++dit ) {
01292      QCString flags = dit.key().latin1();
01293      QStringList sets = makeSets( (*dit), true );
01294      // Send off a status setting job for each set.
01295      for (  QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
01296        QString imappath = imapPath() + ";UID=" + ( *slit );
01297        setImapStatus(imappath, flags);
01298      }
01299   }
01300   mAccount->displayProgress();
01301 }
01302 
01303 //-----------------------------------------------------------------------------
01304 QStringList KMFolderImap::makeSets(QStringList& uids, bool sort)
01305 {
01306   QValueList<int> tmp;
01307   for ( QStringList::Iterator it = uids.begin(); it != uids.end(); ++it )
01308     tmp.append( (*it).toInt() );
01309   return makeSets(tmp, sort);
01310 }
01311 
01312 QStringList KMFolderImap::makeSets(QValueList<int>& uids, bool sort)
01313 {
01314   QStringList sets;
01315   QString set;
01316 
01317   if (uids.size() == 1)
01318   {
01319     sets.append(QString::number(uids.first()));
01320     return sets;
01321   }
01322 
01323   if (sort) qHeapSort(uids);
01324 
01325   int last = 0;
01326   // needed to make a uid like 124 instead of 124:124
01327   bool inserted = false;
01328   /* iterate over uids and build sets like 120:122,124,126:150 */
01329   for ( QValueList<int>::Iterator it = uids.begin(); it != uids.end(); ++it )
01330   {
01331     if (it == uids.begin() || set.isEmpty()) {
01332       set = QString::number(*it);
01333       inserted = true;
01334     } else
01335     {
01336       if (last+1 != *it)
01337       {
01338         // end this range
01339         if (inserted)
01340           set += ',' + QString::number(*it);
01341         else
01342           set += ':' + QString::number(last) + ',' + QString::number(*it);
01343         inserted = true;
01344         if (set.length() > 100)
01345         {
01346           // just in case the server has a problem with longer lines..
01347           sets.append(set);
01348           set = "";
01349         }
01350       } else {
01351         inserted = false;
01352       }
01353     }
01354     last = *it;
01355   }
01356   // last element
01357   if (!inserted)
01358     set += ':' + QString::number(uids.last());
01359 
01360   if (!set.isEmpty()) sets.append(set);
01361 
01362   return sets;
01363 }
01364 
01365 //-----------------------------------------------------------------------------
01366 void KMFolderImap::getUids(QValueList<int>& ids, QValueList<int>& uids)
01367 {
01368   KMMessage *msg = 0;
01369   // get the uids
01370   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
01371   {
01372     bool unget = !isMessage(*it);
01373     msg = getMsg(*it);
01374     if (!msg) continue;
01375     uids.append(msg->headerField("X-UID").toInt());
01376     if (unget) unGetMsg(*it);
01377   }
01378 }
01379 
01380 void KMFolderImap::getUids(QPtrList<KMMessage>& msgList, QValueList<int>& uids, KMFolder* msgParent)
01381 {
01382   KMMessage *msg = 0;
01383 
01384   if (!msgParent) msgParent = msgList.first()->parent();
01385   if (!msgParent) return;
01386 
01387   for ( msg = msgList.first(); msg; msg = msgList.next() )
01388   {
01389     if ( !msg->headerField("X-UID").isEmpty() )
01390       uids.append(msg->headerField("X-UID").toInt());
01391   }
01392 }
01393 
01394 //-----------------------------------------------------------------------------
01395 void KMFolderImap::setImapStatus(QString path, QCString flags)
01396 {
01397   // set the status on the server, the uids are integrated in the path
01398   kdDebug(5006) << "setImapStatus path=" << path << endl;
01399   KURL url = mAccount->getUrl();
01400   url.setPath(path);
01401 
01402   QByteArray packedArgs;
01403   QDataStream stream( packedArgs, IO_WriteOnly);
01404 
01405   stream << (int) 'S' << url << flags;
01406 
01407   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01408     return;
01409   KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE);
01410   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01411   ImapAccountBase::jobData jd( url.url(), 0 );
01412   mAccount->insertJob(job, jd);
01413   connect(job, SIGNAL(result(KIO::Job *)),
01414           SLOT(slotSetStatusResult(KIO::Job *)));
01415 }
01416 
01417 
01418 //-----------------------------------------------------------------------------
01419 void KMFolderImap::expungeFolder(KMFolderImap * aFolder, bool quiet)
01420 {
01421   aFolder->setNeedsCompacting(FALSE);
01422   KURL url = mAccount->getUrl();
01423   url.setPath(aFolder->imapPath() + ";UID=*");
01424   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01425     return;
01426   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01427   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01428   ImapAccountBase::jobData jd( url.url(), 0 );
01429   jd.quiet = quiet;
01430   mAccount->insertJob(job, jd);
01431   connect(job, SIGNAL(result(KIO::Job *)),
01432           mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01433 }
01434 
01435 
01436 //-----------------------------------------------------------------------------
01437 void KMFolderImap::slotSetStatusResult(KIO::Job * job)
01438 {
01439   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01440   if ( it == mAccount->jobsEnd() ) return;
01441   mAccount->removeJob(it);
01442   if (job->error() && job->error() != KIO::ERR_CANNOT_OPEN_FOR_WRITING)
01443   {
01444     mAccount->slotSlaveError( mAccount->slave(), job->error(),
01445         job->errorText() );
01446   }
01447   mAccount->displayProgress();
01448 }
01449 
01450 
01451 //-----------------------------------------------------------------------------
01452 bool KMFolderImap::processNewMail(bool)
01453 {
01454    // a little safety
01455   if ( !mAccount ) {
01456     kdWarning(5006) << "KMFolderImap::processNewMail - account is null!" << endl;
01457     return false;
01458   }
01459   if (imapPath().isEmpty()) {
01460     kdWarning(5006) << "KMFolderImap::processNewMail - imapPath of " << name() << " is empty!" << endl;
01461     kmkernel->imapFolderMgr()->remove(this);
01462     return false;
01463   }
01464   KURL url = mAccount->getUrl();
01465   if (mReadOnly)
01466     url.setPath(imapPath() + ";SECTION=UIDNEXT");
01467   else
01468     url.setPath(imapPath() + ";SECTION=UNSEEN");
01469   if ( mAccount->makeConnection() != ImapAccountBase::Connected ) {
01470     kdWarning(5006) << "KMFolderImap::processNewMail - got no connection!" << endl;
01471     return false;
01472   }
01473   KIO::SimpleJob *job = KIO::stat(url, FALSE);
01474   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01475   ImapAccountBase::jobData jd(url.url());
01476   mAccount->insertJob(job, jd);
01477   connect(job, SIGNAL(result(KIO::Job *)),
01478           SLOT(slotStatResult(KIO::Job *)));
01479   return true;
01480 }
01481 
01482 
01483 //-----------------------------------------------------------------------------
01484 void KMFolderImap::slotStatResult(KIO::Job * job)
01485 {
01486   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01487   if ( it == mAccount->jobsEnd() ) return;
01488   mAccount->removeJob(it);
01489   if (job->error())
01490   {
01491     mAccount->slotSlaveError( mAccount->slave(), job->error(),
01492         job->errorText() );
01493   } else {
01494     KIO::UDSEntry uds = static_cast<KIO::StatJob*>(job)->statResult();
01495     for (KIO::UDSEntry::ConstIterator it = uds.begin(); it != uds.end(); it++)
01496     {
01497       if ((*it).m_uds == KIO::UDS_SIZE)
01498       {
01499         if (mReadOnly)
01500         {
01501           mGuessedUnreadMsgs = -1;
01502           mGuessedUnreadMsgs = countUnread() + (*it).m_long - lastUid() - 1;
01503           if (mGuessedUnreadMsgs < 0) mGuessedUnreadMsgs = 0;
01504         } else {
01505           mGuessedUnreadMsgs = (*it).m_long;
01506         }
01507       }
01508     }
01509   }
01510   emit numUnreadMsgsChanged( this );
01511   mAccount->displayProgress();
01512 }
01513 
01514 //-----------------------------------------------------------------------------
01515 int KMFolderImap::create(bool imap)
01516 {
01517   readConfig();
01518   mUnreadMsgs = -1;
01519   return KMFolderMbox::create(imap);
01520 }
01521 
01522 QValueList<int> KMFolderImap::splitSets(QString uids)
01523 {
01524   QValueList<int> uidlist;
01525 
01526   // ex: 1205,1204,1203,1202,1236:1238
01527   QString buffer = QString::null;
01528   int setstart = -1;
01529   // iterate over the uids
01530   for (uint i = 0; i < uids.length(); i++)
01531   {
01532     QChar chr = uids[i];
01533     if (chr == ',')
01534     {
01535       if (setstart > -1)
01536       {
01537         // a range (uid:uid) was before
01538         for (int j = setstart; j <= buffer.toInt(); j++)
01539         {
01540           uidlist.append(j);
01541         }
01542         setstart = -1;
01543       } else {
01544         // single uid
01545         uidlist.append(buffer.toInt());
01546       }
01547       buffer = "";
01548     } else if (chr == ':') {
01549       // remember the start of the range
01550       setstart = buffer.toInt();
01551       buffer = "";
01552     } else if (chr.category() == QChar::Number_DecimalDigit) {
01553       // digit
01554       buffer += chr;
01555     } else {
01556       // ignore
01557     }
01558   }
01559   // process the last data
01560   if (setstart > -1)
01561   {
01562     for (int j = setstart; j <= buffer.toInt(); j++)
01563     {
01564       uidlist.append(j);
01565     }
01566   } else {
01567     uidlist.append(buffer.toInt());
01568   }
01569 
01570   return uidlist;
01571 }
01572 
01573 #include "kmfolderimap.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:27 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003