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           mAccount->hasInbox()) // do not create an additional inbox
00600         continue;
00601       for (node = mChild->first(); node; node = mChild->next())
00602         if (!node->isDir() && node->name() == mSubfolderNames[i]) break;
00603       if (node) folder = static_cast<KMFolderImap*>(node);
00604       else {
00605         folder = static_cast<KMFolderImap*>
00606           (mChild->createFolder(mSubfolderNames[i]));
00607         if (folder)
00608         {
00609           folder->close();
00610           kmkernel->imapFolderMgr()->contentsChanged();
00611         } else {
00612           kdWarning(5006) << "can't create folder " << mSubfolderNames[i] << endl;
00613         }
00614       }
00615       if (folder)
00616       {
00617         folder->setAccount(mAccount);
00618         folder->setNoContent(mSubfolderMimeTypes[i] == "inode/directory");
00619         folder->setImapPath(mSubfolderPaths[i]);
00620         if (mSubfolderMimeTypes[i] == "message/directory" ||
00621             mSubfolderMimeTypes[i] == "inode/directory")
00622           folder->listDirectory();
00623       }
00624     }
00625   }
00626   // now others should react on the changes
00627   kmkernel->imapFolderMgr()->quiet(FALSE);
00628 }
00629 
00630 //-----------------------------------------------------------------------------
00631 void KMFolderImap::checkValidity()
00632 {
00633   if (!mAccount) {
00634     emit folderComplete(this, false);
00635     return;
00636   }
00637   KURL url = mAccount->getUrl();
00638   url.setPath(imapPath() + ";UID=0:0");
00639   kdDebug(5006) << "KMFolderImap::checkValidity of: " << imapPath() << endl;
00640   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
00641   {
00642     kdWarning(5006) << "KMFolderImap::checkValidity - got no connection" << endl;
00643     emit folderComplete(this, FALSE);
00644     return;
00645   }
00646   // Only check once at a time.
00647   if (mCheckingValidity) {
00648     kdDebug(5006) << "KMFolderImap::checkValidity - already checking" << endl;
00649     return;
00650   }
00651   ImapAccountBase::jobData jd( url.url(), this );
00652   KIO::SimpleJob *job = KIO::get(url, FALSE, FALSE);
00653   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
00654   mAccount->insertJob(job, jd);
00655   connect(job, SIGNAL(result(KIO::Job *)),
00656           SLOT(slotCheckValidityResult(KIO::Job *)));
00657   connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
00658           SLOT(slotSimpleData(KIO::Job *, const QByteArray &)));
00659   // Only check once at a time.
00660   mCheckingValidity = true;
00661   
00662 }
00663 
00664 
00665 //-----------------------------------------------------------------------------
00666 ulong KMFolderImap::lastUid()
00667 {
00668   if (mLastUid) return mLastUid;
00669   open();
00670   if (count() > 0)
00671   {
00672     bool unget = !isMessage(count() - 1);
00673     KMMessage *msg = getMsg(count() - 1);
00674     mLastUid = msg->headerField("X-UID").toULong();
00675     if (unget) unGetMsg(count() - 1);
00676   }
00677   close();
00678   return mLastUid;
00679 }
00680 
00681 
00682 //-----------------------------------------------------------------------------
00683 void KMFolderImap::slotCheckValidityResult(KIO::Job * job)
00684 {
00685   kdDebug(5006) << "KMFolderImap::slotCheckValidityResult of: " << fileName() << endl;
00686   mCheckingValidity = false;
00687   ImapAccountBase::JobIterator it = mAccount->findJob(job);
00688   if ( it == mAccount->jobsEnd() ) return;
00689   if (job->error()) {
00690     mAccount->slotSlaveError(mAccount->slave(), job->error(), job->errorText());
00691     emit folderComplete(this, FALSE);
00692     mAccount->displayProgress();
00693   } else {
00694     QCString cstr((*it).data.data(), (*it).data.size() + 1);
00695     int a = cstr.find("X-uidValidity: ");
00696     int b = cstr.find("\r\n", a);
00697     QString uidv;
00698     if ( (b - a - 15) >= 0 ) uidv = cstr.mid(a + 15, b - a - 15);
00699     a = cstr.find("X-Access: ");
00700     b = cstr.find("\r\n", a);
00701     QString access;
00702     if ( (b - a - 10) >= 0 ) access = cstr.mid(a + 10, b - a - 10);
00703     mReadOnly = access == "Read only";
00704     QString startUid;
00705     if (uidValidity() != uidv)
00706     {
00707       // uidValidity changed
00708       kdDebug(5006) << "KMFolderImap::slotCheckValidityResult uidValidty changed." << endl;
00709       mAccount->ignoreJobsForFolder(this);
00710       expunge();
00711       mLastUid = 0;
00712       uidmap.clear();
00713       setUidValidity(uidv);
00714     } else {
00715       if (!mCheckFlags)
00716         startUid = QString::number(lastUid() + 1);
00717     }
00718     mAccount->removeJob(it);
00719     reallyGetFolder(startUid);
00720   }
00721 }
00722 
00723 //-----------------------------------------------------------------------------
00724 void KMFolderImap::getAndCheckFolder(bool force)
00725 {
00726   if (mNoContent)
00727     return getFolder(force);
00728 
00729   if ( mAccount )
00730     mAccount->processNewMailSingleFolder(this);
00731   if (force) {
00732     // force an update
00733     mCheckFlags = TRUE;
00734   }
00735 }
00736 
00737 //-----------------------------------------------------------------------------
00738 void KMFolderImap::getFolder(bool force)
00739 {
00740   mGuessedUnreadMsgs = -1;
00741   if (mNoContent)
00742   {
00743     mContentState = imapFinished;
00744     emit folderComplete(this, true);
00745     return;
00746   }
00747   mContentState = imapInProgress;
00748   if (force) {
00749     // force an update
00750     mCheckFlags = TRUE;
00751   }
00752   checkValidity();
00753 }
00754 
00755 
00756 //-----------------------------------------------------------------------------
00757 void KMFolderImap::reallyGetFolder(const QString &startUid)
00758 {
00759   KURL url = mAccount->getUrl();
00760   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
00761   {
00762     emit folderComplete(this, FALSE);
00763     mAccount->displayProgress();
00764     return;
00765   }
00766   quiet(true);
00767   if (startUid.isEmpty())
00768   {
00769     url.setPath(imapPath() + ";SECTION=UID FLAGS");
00770     KIO::SimpleJob *job = KIO::listDir(url, FALSE);
00771     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
00772     ImapAccountBase::jobData jd( url.url(), this );
00773     mAccount->insertJob(job, jd);
00774     connect(job, SIGNAL(result(KIO::Job *)),
00775             this, SLOT(slotListFolderResult(KIO::Job *)));
00776     connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
00777             this, SLOT(slotListFolderEntries(KIO::Job *,
00778             const KIO::UDSEntryList &)));
00779   } else {
00780     url.setPath(imapPath() + ";UID=" + startUid
00781       + ":*;SECTION=ENVELOPE");
00782     KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
00783     KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
00784     ImapAccountBase::jobData jd( url.url(), this );
00785     mAccount->insertJob(newJob, jd);
00786     connect(newJob, SIGNAL(result(KIO::Job *)),
00787             this, SLOT(slotGetLastMessagesResult(KIO::Job *)));
00788     connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
00789             this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
00790   }
00791 }
00792 
00793 
00794 //-----------------------------------------------------------------------------
00795 void KMFolderImap::slotListFolderResult(KIO::Job * job)
00796 {
00797   ImapAccountBase::JobIterator it = mAccount->findJob(job);
00798   if ( it == mAccount->jobsEnd() ) return;
00799   QString uids;
00800   if (job->error())
00801   {
00802     mAccount->slotSlaveError( mAccount->slave(), job->error(),
00803         job->errorText() );
00804     quiet( false );
00805     emit folderComplete(this, FALSE);
00806     mAccount->removeJob(it);
00807     mAccount->displayProgress();
00808     return;
00809   }
00810   mCheckFlags = FALSE;
00811   QStringList::Iterator uid;
00812   // Check for already retrieved headers
00813   if (count())
00814   {
00815     QCString cstr;
00816     int idx = 0, a, b, c, serverFlags;
00817     long int mailUid, serverUid;
00818     uid = (*it).items.begin();
00819     while (idx < count() && uid != (*it).items.end())
00820     {
00821       getMsgString(idx, cstr);
00822       a = cstr.find("X-UID: ");
00823       b = cstr.find("\n", a);
00824       if (a == -1 || b == -1) mailUid = -1;
00825       else mailUid = cstr.mid(a + 7, b - a - 7).toLong();
00826       c = (*uid).find(",");
00827       serverUid = (*uid).left(c).toLong();
00828       serverFlags = (*uid).mid(c+1).toInt();
00829       if (mailUid < serverUid) removeMsg(idx, TRUE);
00830       else if (mailUid == serverUid)
00831       {
00832         if (!mReadOnly)
00833           flagsToStatus(getMsgBase(idx), serverFlags, false);
00834         idx++;
00835         uid = (*it).items.remove(uid);
00836       }
00837       else break;  // happens only, if deleted mails reappear on the server
00838     }
00839     while (idx < count()) removeMsg(idx, TRUE);
00840   }
00841   for (uid = (*it).items.begin(); uid != (*it).items.end(); uid++)
00842     (*uid).truncate((*uid).find(","));
00843   ImapAccountBase::jobData jd( QString::null, (*it).parent );
00844 //jd.items = (*it).items;
00845   jd.total = (*it).items.count();
00846   if (jd.total == 0)
00847   {
00848     quiet(false);
00849     mContentState = imapFinished;
00850     emit folderComplete(this, TRUE);
00851     mAccount->removeJob(it);
00852     mAccount->displayProgress();
00853     return;
00854   }
00855 
00856   QStringList sets;
00857   uid = (*it).items.begin();
00858   if (jd.total == 1) sets.append(*uid + ":" + *uid);
00859   else sets = makeSets( (*it).items );
00860   mAccount->removeJob(it); // don't use *it below
00861 
00862   for (QStringList::Iterator i = sets.begin(); i != sets.end(); ++i)
00863   {
00864     KURL url = mAccount->getUrl();
00865     url.setPath(imapPath() + ";UID=" + *i + ";SECTION=ENVELOPE");
00866     if ( mAccount->makeConnection() != ImapAccountBase::Connected )
00867     {
00868       quiet(false);
00869       emit folderComplete(this, FALSE);
00870       return;
00871     }
00872     KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
00873     jd.url = url.url();
00874     KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
00875     mAccount->insertJob(newJob, jd);
00876     connect(newJob, SIGNAL(result(KIO::Job *)),
00877         this, (i == sets.at(sets.count() - 1))
00878         ? SLOT(slotGetLastMessagesResult(KIO::Job *))
00879         : SLOT(slotGetMessagesResult(KIO::Job *)));
00880     connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
00881         this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
00882   }
00883 }
00884 
00885 
00886 //-----------------------------------------------------------------------------
00887 void KMFolderImap::slotListFolderEntries(KIO::Job * job,
00888   const KIO::UDSEntryList & uds)
00889 {
00890   ImapAccountBase::JobIterator it = mAccount->findJob(job);
00891   if ( it == mAccount->jobsEnd() ) return;
00892   QString mimeType, name;
00893   long int flags = 0;
00894   for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
00895     udsIt != uds.end(); udsIt++)
00896   {
00897     for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
00898       eIt != (*udsIt).end(); eIt++)
00899     {
00900       if ((*eIt).m_uds == KIO::UDS_NAME)
00901         name = (*eIt).m_str;
00902       else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
00903         mimeType = (*eIt).m_str;
00904       else if ((*eIt).m_uds == KIO::UDS_ACCESS)
00905         flags = (*eIt).m_long;
00906     }
00907     if (mimeType == "message/rfc822-imap" && !(flags & 8))
00908       (*it).items.append(name + "," + QString::number(flags));
00909   }
00910 }
00911 
00912 
00913 //-----------------------------------------------------------------------------
00914 void KMFolderImap::flagsToStatus(KMMsgBase *msg, int flags, bool newMsg)
00915 {
00916   if (flags & 4) 
00917     msg->setStatus( KMMsgStatusFlag );
00918   if (flags & 2)
00919     msg->setStatus( KMMsgStatusReplied );
00920   if (flags & 1)
00921     msg->setStatus( KMMsgStatusOld );
00922 
00923   if (msg->isOfUnknownStatus()) {
00924     if (newMsg)
00925       msg->setStatus( KMMsgStatusNew );
00926     else
00927       msg->setStatus( KMMsgStatusUnread );
00928   }
00929 }
00930 
00931 
00932 //-----------------------------------------------------------------------------
00933 QString KMFolderImap::statusToFlags(KMMsgStatus status)
00934 {
00935   QString flags;
00936   if (status & KMMsgStatusDeleted) 
00937     flags = "\\DELETED";
00938   else {
00939     if (status & KMMsgStatusOld || status & KMMsgStatusRead) 
00940       flags = "\\SEEN ";
00941     if (status & KMMsgStatusReplied) 
00942       flags += "\\ANSWERED ";
00943     if (status & KMMsgStatusFlag) 
00944       flags += "\\FLAGGED";
00945   }
00946   
00947   return flags.simplifyWhiteSpace();
00948 }
00949 
00950 //-------------------------------------------------------------
00951 void
00952 KMFolderImap::ignoreJobsForMessage( KMMessage* msg )
00953 {
00954   if ( !msg || msg->transferInProgress() || 
00955        !msg->parent() || msg->parent()->folderType() != KMFolderTypeImap )
00956     return;
00957   KMAcctImap *account;
00958   if ( !(account = static_cast<KMFolderImap*>(msg->parent())->account()) )
00959     return;
00960 
00961   account->ignoreJobsForMessage( msg );
00962 }
00963 
00964 //-----------------------------------------------------------------------------
00965 void KMFolderImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
00966 {
00967   if ( data.isEmpty() ) return; // optimization
00968   ImapAccountBase::JobIterator it = mAccount->findJob(job);
00969   if ( it == mAccount->jobsEnd() ) return;
00970   (*it).cdata += QCString(data, data.size() + 1);
00971   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
00972   if (pos > 0)
00973   {
00974     int p = (*it).cdata.find("\r\nX-uidValidity:");
00975     if (p != -1) setUidValidity((*it).cdata
00976       .mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
00977     int c = (*it).cdata.find("\r\nX-Count:");
00978     if ( c != -1 )
00979     {
00980       bool ok;
00981       int exists = (*it).cdata.mid( c+10, 
00982           (*it).cdata.find("\r\n", c+1) - c-10 ).toInt(&ok);
00983       if ( ok && exists < count() )
00984       {
00985         kdDebug(5006) << "KMFolderImap::slotGetMessagesData - server has less messages (" << 
00986           exists << ") then folder (" << count() << "), so reload" << endl;
00987         mAccount->displayProgress();
00988         reallyGetFolder( QString::null );
00989         (*it).cdata.remove(0, pos);
00990         return;
00991       }
00992     }
00993     (*it).cdata.remove(0, pos);
00994   }
00995   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
00996   int flags;
00997   while (pos >= 0)
00998   {
00999     KMMessage *msg = new KMMessage;
01000     msg->fromString((*it).cdata.mid(16, pos - 16));
01001     flags = msg->headerField("X-Flags").toInt();
01002     ulong uid = msg->headerField("X-UID").toULong();
01003     if (flags & 8 || uid <= lastUid()) {
01004       delete msg;
01005       msg = 0;
01006     }
01007     else {
01008       if (uidmap.find(uid))
01009       {
01010         // assign the sernum from the cache
01011         const ulong sernum = (ulong) uidmap[uid];
01012         msg->setMsgSerNum(sernum);
01013         // delete the entry
01014         uidmap.remove(uid);
01015       }
01016       open();
01017       KMFolderMbox::addMsg(msg, 0);
01018       // Transfer the status, if it is cached.
01019       QString id = msg->msgIdMD5();
01020       if ( mMetaDataMap.find( id ) ) {
01021         KMMsgMetaData *md =  mMetaDataMap[id];
01022         msg->setStatus( md->status() );
01023         if ( md->serNum() != 0 )
01024           msg->setMsgSerNum( md->serNum() );
01025         mMetaDataMap.remove( id );
01026         delete md;
01027       }
01028       // Merge with the flags from the server.
01029       flagsToStatus((KMMsgBase*)msg, flags);
01030       // set the correct size
01031       msg->setMsgLength( msg->headerField("X-Length").toUInt() );
01032       close();
01033 
01034       if (count() > 1) unGetMsg(count() - 1);
01035       mLastUid = uid;
01036     }
01037     (*it).cdata.remove(0, pos);
01038     (*it).done++;
01039     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01040     mAccount->displayProgress();
01041   }
01042 }
01043 
01044 //-------------------------------------------------------------
01045 FolderJob*
01046 KMFolderImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
01047                            KMFolder *folder, QString partSpecifier,
01048                            const AttachmentStrategy *as ) const
01049 {
01050   KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder);
01051   if ( jt == FolderJob::tGetMessage && partSpecifier == "STRUCTURE" &&
01052        mAccount && mAccount->loadOnDemand() &&
01053        ( msg->signatureState() == KMMsgNotSigned || 
01054          msg->signatureState() == KMMsgSignatureStateUnknown ) )
01055   {
01056     // load-on-demand: retrieve the BODYSTRUCTURE and to speed things up also the headers
01057     // this is not activated for small or signed messages
01058     ImapJob *job = new ImapJob( msg, jt, kmfi, "HEADER" );
01059     job->start();
01060     ImapJob *job2 = new ImapJob( msg, jt, kmfi, "STRUCTURE", as );
01061     job2->start();
01062     return job;
01063   } else {
01064     // download complete message or part (attachment)
01065     if ( partSpecifier == "STRUCTURE" ) // hide from outside
01066       partSpecifier = QString::null;
01067 
01068     ImapJob *job = new ImapJob( msg, jt, kmfi, partSpecifier );
01069     return job;
01070   }
01071 }
01072 
01073 //-------------------------------------------------------------
01074 FolderJob*
01075 KMFolderImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
01076                            FolderJob::JobType jt, KMFolder *folder ) const
01077 {
01078   KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder);
01079   ImapJob *job = new ImapJob( msgList, sets, jt, kmfi );
01080   return job;
01081 }
01082 
01083 //-----------------------------------------------------------------------------
01084 void KMFolderImap::getMessagesResult(KIO::Job * job, bool lastSet)
01085 {
01086   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01087   if ( it == mAccount->jobsEnd() ) return;
01088   if (job->error())
01089   {
01090     mAccount->slotSlaveError( mAccount->slave(), job->error(),
01091         job->errorText() );
01092     mContentState = imapNoInformation;
01093     quiet( false );
01094     emit folderComplete(this, false);
01095   }
01096   else
01097   {
01098     if (lastSet)
01099     {
01100       mContentState = imapFinished;
01101       quiet(false);
01102       emit folderComplete(this, true);
01103     }
01104     mAccount->removeJob(it);
01105   }
01106 
01107   mAccount->displayProgress();
01108 }
01109 
01110 
01111 //-----------------------------------------------------------------------------
01112 void KMFolderImap::slotGetLastMessagesResult(KIO::Job * job)
01113 {
01114   getMessagesResult(job, true);
01115 }
01116 
01117 
01118 //-----------------------------------------------------------------------------
01119 void KMFolderImap::slotGetMessagesResult(KIO::Job * job)
01120 {
01121   getMessagesResult(job, false);
01122 }
01123 
01124 
01125 //-----------------------------------------------------------------------------
01126 void KMFolderImap::createFolder(const QString &name)
01127 {
01128   KURL url = mAccount->getUrl();
01129   url.setPath(imapPath() + name);
01130   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01131     return;
01132   KIO::SimpleJob *job = KIO::mkdir(url);
01133   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01134   ImapAccountBase::jobData jd( url.url(), this );
01135   jd.items = name;
01136   mAccount->insertJob(job, jd);
01137   connect(job, SIGNAL(result(KIO::Job *)),
01138           this, SLOT(slotCreateFolderResult(KIO::Job *)));
01139 }
01140 
01141 
01142 //-----------------------------------------------------------------------------
01143 void KMFolderImap::slotCreateFolderResult(KIO::Job * job)
01144 {
01145   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01146   if ( it == mAccount->jobsEnd() ) return;
01147   if (job->error())
01148   {
01149     mAccount->slotSlaveError( mAccount->slave(), job->error(),
01150         job->errorText() );
01151   } else {
01152     listDirectory();
01153   }
01154   mAccount->removeJob(job);
01155 }
01156 
01157 
01158 //-----------------------------------------------------------------------------
01159 static QTextCodec *sUtf7Codec = 0;
01160 
01161 QTextCodec * KMFolderImap::utf7Codec()
01162 {
01163   if (!sUtf7Codec) sUtf7Codec = QTextCodec::codecForName("utf-7");
01164   return sUtf7Codec;
01165 }
01166 
01167 
01168 //-----------------------------------------------------------------------------
01169 QString KMFolderImap::encodeFileName(const QString &name)
01170 {
01171   QString result = utf7Codec()->fromUnicode(name);
01172   return KURL::encode_string_no_slash(result);
01173 }
01174 
01175 
01176 //-----------------------------------------------------------------------------
01177 QString KMFolderImap::decodeFileName(const QString &name)
01178 {
01179   QString result = KURL::decode_string(name);
01180   return utf7Codec()->toUnicode(result.latin1());
01181 }
01182 
01183 //-----------------------------------------------------------------------------
01184 bool KMFolderImap::autoExpunge()
01185 {
01186   if (mAccount)
01187     return mAccount->autoExpunge();
01188 
01189   return false;
01190 }
01191 
01192 
01193 //-----------------------------------------------------------------------------
01194 void KMFolderImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
01195 {
01196   if ( data.isEmpty() ) return; // optimization
01197   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01198   if ( it == mAccount->jobsEnd() ) return;
01199   QBuffer buff((*it).data);
01200   buff.open(IO_WriteOnly | IO_Append);
01201   buff.writeBlock(data.data(), data.size());
01202   buff.close();
01203 }
01204 
01205 //-----------------------------------------------------------------------------
01206 void KMFolderImap::deleteMessage(KMMessage * msg)
01207 {
01208   KURL url = mAccount->getUrl();
01209   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msg->parent());
01210   const QString uid = msg->headerField("X-UID");
01211   /* If the uid is empty the delete job below will nuke all mail in the 
01212      folder, so we better safeguard against that. See ::expungeFolder, as
01213      to why. :( */
01214   if ( uid.isEmpty() ) {
01215      kdDebug( 5006 ) << "KMFolderImap::deleteMessage: Attempt to delete "
01216                         "an empty UID. Aborting."  << endl;
01217      return;
01218   }
01219   url.setPath(msg_parent->imapPath() + ";UID=" + uid );
01220   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01221     return;
01222   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01223   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01224   ImapAccountBase::jobData jd( url.url(), 0 );
01225   mAccount->insertJob(job, jd);
01226   connect(job, SIGNAL(result(KIO::Job *)),
01227           mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01228 }
01229 
01230 void KMFolderImap::deleteMessage(QPtrList<KMMessage> msgList)
01231 {
01232   QValueList<int> uids;
01233   getUids(msgList, uids);
01234   QStringList sets = makeSets(uids);
01235 
01236   KURL url = mAccount->getUrl();
01237   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msgList.first()->parent());
01238   for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
01239   {
01240     const QString uid = *it;
01241     // Don't delete with no uid, that nukes the folder. Should not happen, but
01242     // better safe than sorry.
01243     if ( uid.isEmpty() ) continue;
01244     url.setPath(msg_parent->imapPath() + ";UID=" + uid);
01245     if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01246       return;
01247     KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01248     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01249     ImapAccountBase::jobData jd( url.url(), 0 );
01250     mAccount->insertJob(job, jd);
01251     connect(job, SIGNAL(result(KIO::Job *)),
01252         mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01253   }
01254 }
01255 
01256 //-----------------------------------------------------------------------------
01257 void KMFolderImap::setStatus(int idx, KMMsgStatus status, bool toggle)
01258 {
01259   QValueList<int> ids; ids.append(idx);
01260   setStatus(ids, status, toggle);
01261 }
01262 
01263 void KMFolderImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01264 {
01265   KMFolder::setStatus(ids, status, toggle);
01266   if (mReadOnly) return;
01267 
01268   /* The status has been already set in the local index. Update the flags on
01269    * the server. To avoid doing that for each message individually, group them
01270    * by the status string they will be assigned and make sets for each of those
01271    * groups of mails. This is necessary because the imap kio_slave status job
01272    * does not append flags but overwrites them. Example:
01273    * 
01274    * 2 important mails and 3 unimportant mail, all unread. Mark all as read calls
01275    * this method with a list of uids. The 2 important mails need to get the string
01276    * \SEEN \FLAGGED while the others need to get just \SEEN. Build sets for each
01277    * of those and sort them, so the server can handle them efficiently. */
01278   QMap< QString, QStringList > groups;
01279   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) {
01280     KMMessage *msg = 0;
01281     bool unget = !isMessage(*it);
01282     msg = getMsg(*it);
01283     if (!msg) continue;
01284     QString flags = statusToFlags(msg->status());
01285     // Collect uids for each type of flags.
01286     groups[flags].append(msg->headerField("X-UID"));
01287     if (unget) unGetMsg(*it);
01288   }
01289   QMapIterator< QString, QStringList > dit;
01290   for ( dit = groups.begin(); dit != groups.end(); ++dit ) {
01291      QCString flags = dit.key().latin1();
01292      QStringList sets = makeSets( (*dit), true );
01293      // Send off a status setting job for each set.
01294      for (  QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
01295        QString imappath = imapPath() + ";UID=" + ( *slit );
01296        setImapStatus(imappath, flags);
01297      }
01298   }
01299   mAccount->displayProgress();
01300 }
01301 
01302 //-----------------------------------------------------------------------------
01303 QStringList KMFolderImap::makeSets(QStringList& uids, bool sort)
01304 {
01305   QValueList<int> tmp;
01306   for ( QStringList::Iterator it = uids.begin(); it != uids.end(); ++it )
01307     tmp.append( (*it).toInt() );
01308   return makeSets(tmp, sort);
01309 }
01310 
01311 QStringList KMFolderImap::makeSets(QValueList<int>& uids, bool sort)
01312 {
01313   QStringList sets;
01314   QString set;
01315 
01316   if (uids.size() == 1)
01317   {
01318     sets.append(QString::number(uids.first()));
01319     return sets;
01320   }
01321 
01322   if (sort) qHeapSort(uids);
01323 
01324   int last = 0;
01325   // needed to make a uid like 124 instead of 124:124
01326   bool inserted = false;
01327   /* iterate over uids and build sets like 120:122,124,126:150 */
01328   for ( QValueList<int>::Iterator it = uids.begin(); it != uids.end(); ++it )
01329   {
01330     if (it == uids.begin() || set.isEmpty()) {
01331       set = QString::number(*it);
01332       inserted = true;
01333     } else
01334     {
01335       if (last+1 != *it)
01336       {
01337         // end this range
01338         if (inserted)
01339           set += ',' + QString::number(*it);
01340         else
01341           set += ':' + QString::number(last) + ',' + QString::number(*it);
01342         inserted = true;
01343         if (set.length() > 100)
01344         {
01345           // just in case the server has a problem with longer lines..
01346           sets.append(set);
01347           set = "";
01348         }
01349       } else {
01350         inserted = false;
01351       }
01352     }
01353     last = *it;
01354   }
01355   // last element
01356   if (!inserted)
01357     set += ':' + QString::number(uids.last());
01358 
01359   if (!set.isEmpty()) sets.append(set);
01360 
01361   return sets;
01362 }
01363 
01364 //-----------------------------------------------------------------------------
01365 void KMFolderImap::getUids(QValueList<int>& ids, QValueList<int>& uids)
01366 {
01367   KMMessage *msg = 0;
01368   // get the uids
01369   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
01370   {
01371     bool unget = !isMessage(*it);
01372     msg = getMsg(*it);
01373     if (!msg) continue;
01374     uids.append(msg->headerField("X-UID").toInt());
01375     if (unget) unGetMsg(*it);
01376   }
01377 }
01378 
01379 void KMFolderImap::getUids(QPtrList<KMMessage>& msgList, QValueList<int>& uids, KMFolder* msgParent)
01380 {
01381   KMMessage *msg = 0;
01382 
01383   if (!msgParent) msgParent = msgList.first()->parent();
01384   if (!msgParent) return;
01385 
01386   for ( msg = msgList.first(); msg; msg = msgList.next() )
01387   {
01388     if ( !msg->headerField("X-UID").isEmpty() )
01389       uids.append(msg->headerField("X-UID").toInt());
01390   }
01391 }
01392 
01393 //-----------------------------------------------------------------------------
01394 void KMFolderImap::setImapStatus(QString path, QCString flags)
01395 {
01396   // set the status on the server, the uids are integrated in the path
01397   kdDebug(5006) << "setImapStatus path=" << path << endl;
01398   KURL url = mAccount->getUrl();
01399   url.setPath(path);
01400 
01401   QByteArray packedArgs;
01402   QDataStream stream( packedArgs, IO_WriteOnly);
01403 
01404   stream << (int) 'S' << url << flags;
01405 
01406   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01407     return;
01408   KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE);
01409   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01410   ImapAccountBase::jobData jd( url.url(), 0 );
01411   mAccount->insertJob(job, jd);
01412   connect(job, SIGNAL(result(KIO::Job *)),
01413           SLOT(slotSetStatusResult(KIO::Job *)));
01414 }
01415 
01416 
01417 //-----------------------------------------------------------------------------
01418 void KMFolderImap::expungeFolder(KMFolderImap * aFolder, bool quiet)
01419 {
01420   aFolder->setNeedsCompacting(FALSE);
01421   KURL url = mAccount->getUrl();
01422   url.setPath(aFolder->imapPath() + ";UID=*");
01423   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01424     return;
01425   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01426   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01427   ImapAccountBase::jobData jd( url.url(), 0 );
01428   jd.quiet = quiet;
01429   mAccount->insertJob(job, jd);
01430   connect(job, SIGNAL(result(KIO::Job *)),
01431           mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01432 }
01433 
01434 
01435 //-----------------------------------------------------------------------------
01436 void KMFolderImap::slotSetStatusResult(KIO::Job * job)
01437 {
01438   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01439   if ( it == mAccount->jobsEnd() ) return;
01440   mAccount->removeJob(it);
01441   if (job->error() && job->error() != KIO::ERR_CANNOT_OPEN_FOR_WRITING)
01442   {
01443     mAccount->slotSlaveError( mAccount->slave(), job->error(),
01444         job->errorText() );
01445   }
01446   mAccount->displayProgress();
01447 }
01448 
01449 
01450 //-----------------------------------------------------------------------------
01451 bool KMFolderImap::processNewMail(bool)
01452 {
01453    // a little safety
01454   if ( !mAccount ) {
01455     kdWarning(5006) << "KMFolderImap::processNewMail - account is null!" << endl;
01456     return false;
01457   }
01458   if (imapPath().isEmpty()) {
01459     kdWarning(5006) << "KMFolderImap::processNewMail - imapPath of " << name() << " is empty!" << endl;
01460     kmkernel->imapFolderMgr()->remove(this);
01461     return false;
01462   }
01463   KURL url = mAccount->getUrl();
01464   if (mReadOnly)
01465     url.setPath(imapPath() + ";SECTION=UIDNEXT");
01466   else
01467     url.setPath(imapPath() + ";SECTION=UNSEEN");
01468   if ( mAccount->makeConnection() != ImapAccountBase::Connected ) {
01469     kdWarning(5006) << "KMFolderImap::processNewMail - got no connection!" << endl;
01470     return false;
01471   }
01472   KIO::SimpleJob *job = KIO::stat(url, FALSE);
01473   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01474   ImapAccountBase::jobData jd(url.url());
01475   mAccount->insertJob(job, jd);
01476   connect(job, SIGNAL(result(KIO::Job *)),
01477           SLOT(slotStatResult(KIO::Job *)));
01478   return true;
01479 }
01480 
01481 
01482 //-----------------------------------------------------------------------------
01483 void KMFolderImap::slotStatResult(KIO::Job * job)
01484 {
01485   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01486   if ( it == mAccount->jobsEnd() ) return;
01487   mAccount->removeJob(it);
01488   if (job->error())
01489   {
01490     mAccount->slotSlaveError( mAccount->slave(), job->error(),
01491         job->errorText() );
01492   } else {
01493     KIO::UDSEntry uds = static_cast<KIO::StatJob*>(job)->statResult();
01494     for (KIO::UDSEntry::ConstIterator it = uds.begin(); it != uds.end(); it++)
01495     {
01496       if ((*it).m_uds == KIO::UDS_SIZE)
01497       {
01498         if (mReadOnly)
01499         {
01500           mGuessedUnreadMsgs = -1;
01501           mGuessedUnreadMsgs = countUnread() + (*it).m_long - lastUid() - 1;
01502           if (mGuessedUnreadMsgs < 0) mGuessedUnreadMsgs = 0;
01503         } else {
01504           mGuessedUnreadMsgs = (*it).m_long;
01505         }
01506       }
01507     }
01508   }
01509   emit numUnreadMsgsChanged( this );
01510   mAccount->displayProgress();
01511 }
01512 
01513 //-----------------------------------------------------------------------------
01514 int KMFolderImap::create(bool imap)
01515 {
01516   readConfig();
01517   mUnreadMsgs = -1;
01518   return KMFolderMbox::create(imap);
01519 }
01520 
01521 QValueList<int> KMFolderImap::splitSets(QString uids)
01522 {
01523   QValueList<int> uidlist;
01524 
01525   // ex: 1205,1204,1203,1202,1236:1238
01526   QString buffer = QString::null;
01527   int setstart = -1;
01528   // iterate over the uids
01529   for (uint i = 0; i < uids.length(); i++)
01530   {
01531     QChar chr = uids[i];
01532     if (chr == ',')
01533     {
01534       if (setstart > -1)
01535       {
01536         // a range (uid:uid) was before
01537         for (int j = setstart; j <= buffer.toInt(); j++)
01538         {
01539           uidlist.append(j);
01540         }
01541         setstart = -1;
01542       } else {
01543         // single uid
01544         uidlist.append(buffer.toInt());
01545       }
01546       buffer = "";
01547     } else if (chr == ':') {
01548       // remember the start of the range
01549       setstart = buffer.toInt();
01550       buffer = "";
01551     } else if (chr.category() == QChar::Number_DecimalDigit) {
01552       // digit
01553       buffer += chr;
01554     } else {
01555       // ignore
01556     }
01557   }
01558   // process the last data
01559   if (setstart > -1)
01560   {
01561     for (int j = setstart; j <= buffer.toInt(); j++)
01562     {
01563       uidlist.append(j);
01564     }
01565   } else {
01566     uidlist.append(buffer.toInt());
01567   }
01568 
01569   return uidlist;
01570 }
01571 
01572 #include "kmfolderimap.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Mar 6 17:18:19 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003