kmail

kmfolderimap.cpp

00001 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include "kmfolder.h"
00028 #include "kmfolderimap.h"
00029 #include "kmfoldermbox.h"
00030 #include "kmfoldertree.h"
00031 #include "kmmsgdict.h"
00032 #include "undostack.h"
00033 #include "kmfoldermgr.h"
00034 #include "kmfiltermgr.h"
00035 #include "kmmsgdict.h"
00036 #include "imapaccountbase.h"
00037 using KMail::ImapAccountBase;
00038 #include "imapjob.h"
00039 using KMail::ImapJob;
00040 #include "attachmentstrategy.h"
00041 using KMail::AttachmentStrategy;
00042 #include "progressmanager.h"
00043 using KPIM::ProgressItem;
00044 using KPIM::ProgressManager;
00045 #include "listjob.h"
00046 using KMail::ListJob;
00047 #include "kmsearchpattern.h"
00048 #include "searchjob.h"
00049 using KMail::SearchJob;
00050 #include "renamejob.h"
00051 using KMail::RenameJob;
00052 
00053 #include <kdebug.h>
00054 #include <kio/scheduler.h>
00055 #include <kconfig.h>
00056 #include <kmessagebox.h>
00057 
00058 #include <qbuffer.h>
00059 #include <qtextcodec.h>
00060 #include <qstylesheet.h>
00061 
00062 #include <assert.h>
00063 
00064 KMFolderImap::KMFolderImap(KMFolder* folder, const char* aName)
00065   : KMFolderMbox(folder, aName)
00066 {
00067   mContentState = imapNoInformation;
00068   mSubfolderState = imapNoInformation;
00069   mAccount = 0;
00070   mIsSelected = FALSE;
00071   mLastUid = 0;
00072   mCheckFlags = TRUE;
00073   mCheckMail = TRUE;
00074   mCheckingValidity = FALSE;
00075   mUserRights = 0;
00076   mAlreadyRemoved = false;
00077   mHasChildren = ChildrenUnknown;
00078   mMailCheckProgressItem = 0;
00079   mListDirProgressItem = 0;
00080   mAddMessageProgressItem = 0;
00081   mReadOnly = false;
00082 
00083   connect (this, SIGNAL( folderComplete( KMFolderImap*, bool ) ),
00084            this, SLOT( slotCompleteMailCheckProgress()) );
00085 }
00086 
00087 KMFolderImap::~KMFolderImap()
00088 {
00089   if (mAccount) {
00090     mAccount->removeSlaveJobsForFolder( folder() );
00091     /* Now that we've removed ourselves from the accounts jobs map, kill all
00092        ongoing operations and reset mailcheck if we were deleted during an
00093        ongoing mailcheck of our account. Not very gracefull, but safe, and the
00094        only way I can see to reset the account state cleanly. */
00095     if ( mAccount->checkingMail( folder() ) ) {
00096        mAccount->killAllJobs();
00097     }
00098   }
00099   writeConfig();
00100   if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed( folder() );
00101   mMetaDataMap.setAutoDelete( true );
00102   mMetaDataMap.clear();
00103   mUidMetaDataMap.setAutoDelete( true );
00104   mUidMetaDataMap.clear();
00105 }
00106 
00107 
00108 //-----------------------------------------------------------------------------
00109 void KMFolderImap::close(bool aForced)
00110 {
00111   if (mOpenCount <= 0 ) return;
00112   if (mOpenCount > 0) mOpenCount--;
00113   if (mOpenCount > 0 && !aForced) return;
00114   if (isSelected() && !aForced) {
00115       kdWarning(5006) << "Trying to close the selected folder " << label() <<
00116           " - ignoring!" << endl;
00117       return;
00118   }
00119   // FIXME is this still needed?
00120   if (mAccount)
00121     mAccount->ignoreJobsForFolder( folder() );
00122   int idx = count();
00123   while (--idx >= 0) {
00124     if ( mMsgList[idx]->isMessage() ) {
00125       KMMessage *msg = static_cast<KMMessage*>(mMsgList[idx]);
00126       if (msg->transferInProgress())
00127           msg->setTransferInProgress( false );
00128     }
00129   }
00130   // The inherited close will decrement again, so we have to adjust.
00131   mOpenCount++;
00132   KMFolderMbox::close(aForced);
00133 }
00134 
00135 KMFolder* KMFolderImap::trashFolder() const
00136 {
00137   QString trashStr = account()->trash();
00138   return kmkernel->imapFolderMgr()->findIdString( trashStr );
00139 }
00140 
00141 //-----------------------------------------------------------------------------
00142 KMMessage* KMFolderImap::getMsg(int idx)
00143 {
00144   if(!(idx >= 0 && idx <= count()))
00145     return 0;
00146 
00147   KMMsgBase* mb = getMsgBase(idx);
00148   if (!mb) return 0;
00149   if (mb->isMessage())
00150   {
00151     return ((KMMessage*)mb);
00152   } else {
00153     KMMessage* msg = FolderStorage::getMsg( idx );
00154     if ( msg ) // set it incomplete as the msg was not transferred from the server
00155       msg->setComplete( false );
00156     return msg;
00157   }
00158 }
00159 
00160 //-----------------------------------------------------------------------------
00161 void KMFolderImap::setAccount(KMAcctImap *aAccount)
00162 {
00163   mAccount = aAccount;
00164   if( !folder() || !folder()->child() ) return;
00165   KMFolderNode* node;
00166   for (node = folder()->child()->first(); node;
00167        node = folder()->child()->next())
00168   {
00169     if (!node->isDir())
00170       static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage())->setAccount(aAccount);
00171   }
00172 }
00173 
00174 //-----------------------------------------------------------------------------
00175 void KMFolderImap::readConfig()
00176 {
00177   KConfig* config = KMKernel::config();
00178   KConfigGroupSaver saver(config, "Folder-" + folder()->idString());
00179   mCheckMail = config->readBoolEntry("checkmail", true);
00180 
00181   mUidValidity = config->readEntry("UidValidity");
00182   if ( mImapPath.isEmpty() ) {
00183     setImapPath( config->readEntry("ImapPath") );
00184   }
00185   if (QString(name()).upper() == "INBOX" && mImapPath == "/INBOX/")
00186   {
00187     folder()->setSystemFolder( true );
00188     folder()->setLabel( i18n("inbox") );
00189   }
00190   mNoContent = config->readBoolEntry("NoContent", FALSE);
00191   mReadOnly = config->readBoolEntry("ReadOnly", FALSE);
00192 
00193   KMFolderMbox::readConfig();
00194 }
00195 
00196 //-----------------------------------------------------------------------------
00197 void KMFolderImap::writeConfig()
00198 {
00199   KConfig* config = KMKernel::config();
00200   KConfigGroupSaver saver(config, "Folder-" + folder()->idString());
00201   config->writeEntry("checkmail", mCheckMail);
00202   config->writeEntry("UidValidity", mUidValidity);
00203   config->writeEntry("ImapPath", mImapPath);
00204   config->writeEntry("NoContent", mNoContent);
00205   config->writeEntry("ReadOnly", mReadOnly);
00206   KMFolderMbox::writeConfig();
00207 }
00208 
00209 //-----------------------------------------------------------------------------
00210 void KMFolderImap::remove()
00211 {
00212   if ( mAlreadyRemoved || !mAccount )
00213   {
00214     // override
00215     FolderStorage::remove();
00216     return;
00217   }
00218   KURL url = mAccount->getUrl();
00219   url.setPath(imapPath());
00220   if ( mAccount->makeConnection() == ImapAccountBase::Error ||
00221        imapPath().isEmpty() )
00222   {
00223     emit removed(folder(), false);
00224     return;
00225   }
00226   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
00227   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
00228   ImapAccountBase::jobData jd(url.url());
00229   jd.progressItem = ProgressManager::createProgressItem(
00230                       "ImapFolderRemove" + ProgressManager::getUniqueID(),
00231                       i18n("Removing folder"),
00232                       i18n( "URL: %1" ).arg( QStyleSheet::escape( folder()->prettyURL() ) ),
00233                       false,
00234                       mAccount->useSSL() || mAccount->useTLS() );
00235   mAccount->insertJob(job, jd);
00236   connect(job, SIGNAL(result(KIO::Job *)),
00237           this, SLOT(slotRemoveFolderResult(KIO::Job *)));
00238 }
00239 
00240 //-----------------------------------------------------------------------------
00241 void KMFolderImap::slotRemoveFolderResult(KIO::Job *job)
00242 {
00243   ImapAccountBase::JobIterator it = mAccount->findJob(job);
00244   if ( it == mAccount->jobsEnd() ) return;
00245   if (job->error())
00246   {
00247     mAccount->handleJobError( job, i18n("Error while removing a folder.") );
00248     emit removed(folder(), false);
00249   } else {
00250     mAccount->removeJob(it);
00251     FolderStorage::remove();
00252   }
00253 
00254 }
00255 
00256 //-----------------------------------------------------------------------------
00257 void KMFolderImap::removeMsg(int idx, bool quiet)
00258 {
00259   if (idx < 0)
00260     return;
00261 
00262   if (!quiet)
00263   {
00264     KMMessage *msg = getMsg(idx);
00265     deleteMessage(msg);
00266   }
00267 
00268   mLastUid = 0;
00269   KMFolderMbox::removeMsg(idx);
00270 }
00271 
00272 void KMFolderImap::removeMsg( const QPtrList<KMMessage>& msgList, bool quiet )
00273 {
00274   if ( msgList.isEmpty() ) return;
00275   if (!quiet)
00276     deleteMessage(msgList);
00277 
00278   mLastUid = 0;
00279 
00280   /* Remove the messages from the local store as well.
00281      We don't call KMFolderInherited::removeMsg(QPtrList<KMMessage>) but
00282      iterate ourselves, as that would call KMFolderImap::removeMsg(int)
00283      and not the one from the store we want to be used. */
00284 
00285   QPtrListIterator<KMMessage> it( msgList );
00286   KMMessage *msg;
00287   while ( (msg = it.current()) != 0 ) {
00288     ++it;
00289     int idx = find(msg);
00290     assert( idx != -1);
00291     // ATTENTION port me to maildir
00292     KMFolderMbox::removeMsg(idx, quiet);
00293   }
00294 }
00295 
00296 //-----------------------------------------------------------------------------
00297 int KMFolderImap::rename( const QString& newName, KMFolderDir *aParent )
00298 {
00299   if ( !aParent )
00300     KMFolderMbox::rename( newName );
00301   kmkernel->folderMgr()->contentsChanged();
00302   return 0;
00303 }
00304 
00305 //-----------------------------------------------------------------------------
00306 void KMFolderImap::addMsgQuiet(KMMessage* aMsg)
00307 {
00308   KMFolder *aFolder = aMsg->parent();
00309   Q_UINT32 serNum = 0;
00310   aMsg->setTransferInProgress( false );
00311   if (aFolder) {
00312     serNum = aMsg->getMsgSerNum();
00313     kmkernel->undoStack()->pushSingleAction( serNum, aFolder, folder() );
00314     int idx = aFolder->find( aMsg );
00315     assert( idx != -1 );
00316     aFolder->take( idx );
00317   } else {
00318     kdDebug(5006) << k_funcinfo << "no parent" << endl;
00319   }
00320   if ( !mAccount->hasCapability("uidplus") ) {
00321     // Remember the status with the MD5 as key
00322     // so it can be transfered to the new message
00323     mMetaDataMap.insert( aMsg->msgIdMD5(),
00324         new KMMsgMetaData(aMsg->status(), serNum) );
00325   }
00326 
00327   delete aMsg;
00328   aMsg = 0;
00329   getFolder();
00330 }
00331 
00332 //-----------------------------------------------------------------------------
00333 void KMFolderImap::addMsgQuiet(QPtrList<KMMessage> msgList)
00334 {
00335   if ( mAddMessageProgressItem )
00336   {
00337     mAddMessageProgressItem->setComplete();
00338     mAddMessageProgressItem = 0;
00339   }
00340   KMFolder *aFolder = msgList.first()->parent();
00341   int undoId = -1;
00342   bool uidplus = mAccount->hasCapability("uidplus");
00343   for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00344   {
00345     if ( undoId == -1 )
00346       undoId = kmkernel->undoStack()->newUndoAction( aFolder, folder() );
00347     if ( msg->getMsgSerNum() > 0 )
00348       kmkernel->undoStack()->addMsgToAction( undoId, msg->getMsgSerNum() );
00349     if ( !uidplus ) {
00350       // Remember the status with the MD5 as key
00351       // so it can be transfered to the new message
00352       mMetaDataMap.insert( msg->msgIdMD5(),
00353           new KMMsgMetaData(msg->status(), msg->getMsgSerNum()) );
00354     }
00355     msg->setTransferInProgress( false );
00356   }
00357   if ( aFolder ) {
00358     aFolder->take( msgList );
00359   } else {
00360     kdDebug(5006) << k_funcinfo << "no parent" << endl;
00361   }
00362   msgList.setAutoDelete(true);
00363   msgList.clear();
00364   getFolder();
00365 }
00366 
00367 //-----------------------------------------------------------------------------
00368 int KMFolderImap::addMsg(KMMessage* aMsg, int* aIndex_ret)
00369 {
00370   QPtrList<KMMessage> list;
00371   list.append(aMsg);
00372   QValueList<int> index;
00373   int ret = addMsg(list, index);
00374   aIndex_ret = &index.first();
00375   return ret;
00376 }
00377 
00378 int KMFolderImap::addMsg(QPtrList<KMMessage>& msgList, QValueList<int>& aIndex_ret)
00379 {
00380   KMMessage *aMsg = msgList.getFirst();
00381   KMFolder *msgParent = aMsg->parent();
00382 
00383   ImapJob *imapJob = 0;
00384   if (msgParent)
00385   {
00386     if (msgParent->folderType() == KMFolderTypeImap)
00387     {
00388       if (static_cast<KMFolderImap*>(msgParent->storage())->account() == account())
00389       {
00390         // make sure the messages won't be deleted while we work with them
00391         for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00392           msg->setTransferInProgress(true);
00393 
00394         if (folder() == msgParent)
00395         {
00396           // transfer the whole message, e.g. a draft-message is canceled and re-added to the drafts-folder
00397           for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() )
00398           {
00399             if (!msg->isComplete())
00400             {
00401               int idx = msgParent->find(msg);
00402               assert(idx != -1);
00403               msg = msgParent->getMsg(idx);
00404             }
00405             imapJob = new ImapJob(msg, ImapJob::tPutMessage, this);
00406             connect(imapJob, SIGNAL(messageStored(KMMessage*)),
00407                      SLOT(addMsgQuiet(KMMessage*)));
00408             imapJob->start();
00409           }
00410 
00411         } else {
00412 
00413           // get the messages and the uids
00414           QValueList<ulong> uids;
00415           getUids(msgList, uids);
00416 
00417           // get the sets (do not sort the uids)
00418           QStringList sets = makeSets(uids, false);
00419 
00420           for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
00421           {
00422             // we need the messages that belong to the current set to pass them to the ImapJob
00423             QPtrList<KMMessage> temp_msgs = splitMessageList(*it, msgList);
00424             if ( temp_msgs.isEmpty() ) kdDebug(5006) << "Wow! KMFolderImap::splitMessageList() returned an empty list!" << endl;
00425             imapJob = new ImapJob(temp_msgs, *it, ImapJob::tMoveMessage, this);
00426             connect(imapJob, SIGNAL(messageCopied(QPtrList<KMMessage>)),
00427                 SLOT(addMsgQuiet(QPtrList<KMMessage>)));
00428             connect(imapJob, SIGNAL(result(KMail::FolderJob*)),
00429                 SLOT(slotCopyMsgResult(KMail::FolderJob*)));
00430             imapJob->start();
00431           }
00432         }
00433         return 0;
00434       }
00435       else
00436       {
00437         // different account, check if messages can be added
00438         QPtrListIterator<KMMessage> it( msgList );
00439         KMMessage *msg;
00440         while ( (msg = it.current()) != 0 )
00441         {
00442           ++it;
00443           int index;
00444           if (!canAddMsgNow(msg, &index)) {
00445             aIndex_ret << index;
00446             msgList.remove(msg);
00447           } else {
00448             if (!msg->transferInProgress())
00449               msg->setTransferInProgress(true);
00450           }
00451         }
00452       }
00453     } // if imap
00454   }
00455 
00456   if ( !msgList.isEmpty() )
00457   {
00458     // transfer from local folders or other accounts
00459     QPtrListIterator<KMMessage> it( msgList );
00460     KMMessage* msg;
00461     while ( ( msg = it.current() ) != 0 )
00462     {
00463       ++it;
00464       if ( !msg->transferInProgress() )
00465         msg->setTransferInProgress( true );
00466     }
00467     imapJob = new ImapJob( msgList, QString::null, ImapJob::tPutMessage, this );
00468     if ( !mAddMessageProgressItem && msgList.count() > 1 )
00469     {
00470       // use a parent progress if we have more than 1 message
00471       // otherwise the normal progress is more accurate
00472       mAddMessageProgressItem = ProgressManager::createProgressItem(
00473           "Uploading"+ProgressManager::getUniqueID(),
00474           i18n("Uploading message data"),
00475           i18n("Destination folder: %1").arg( QStyleSheet::escape( folder()->prettyURL() ) ),
00476           true,
00477           mAccount->useSSL() || mAccount->useTLS() );
00478       mAddMessageProgressItem->setTotalItems( msgList.count() );
00479       connect ( mAddMessageProgressItem, SIGNAL( progressItemCanceled( KPIM::ProgressItem*)),
00480           mAccount, SLOT( slotAbortRequested( KPIM::ProgressItem* ) ) );
00481       imapJob->setParentProgressItem( mAddMessageProgressItem );
00482     }
00483     connect( imapJob, SIGNAL( messageCopied(QPtrList<KMMessage>) ),
00484         SLOT( addMsgQuiet(QPtrList<KMMessage>) ) );
00485     imapJob->start();
00486   }
00487 
00488   return 0;
00489 }
00490 
00491 //-----------------------------------------------------------------------------
00492 void KMFolderImap::slotCopyMsgResult( KMail::FolderJob* job )
00493 {
00494   if ( job->error() ) // getFolder() will not be called in this case
00495     emit folderComplete( this, false );
00496 }
00497 
00498 //-----------------------------------------------------------------------------
00499 void KMFolderImap::copyMsg(QPtrList<KMMessage>& msgList)
00500 {
00501   if ( !mAccount->hasCapability("uidplus") ) {
00502     for ( KMMessage *msg = msgList.first(); msg; msg = msgList.next() ) {
00503       // Remember the status with the MD5 as key
00504       // so it can be transfered to the new message
00505       mMetaDataMap.insert( msg->msgIdMD5(), new KMMsgMetaData(msg->status()) );
00506     }
00507   }
00508 
00509   QValueList<ulong> uids;
00510   getUids(msgList, uids);
00511   QStringList sets = makeSets(uids, false);
00512   for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
00513   {
00514     // we need the messages that belong to the current set to pass them to the ImapJob
00515     QPtrList<KMMessage> temp_msgs = splitMessageList(*it, msgList);
00516 
00517     ImapJob *job = new ImapJob(temp_msgs, *it, ImapJob::tCopyMessage, this);
00518     job->start();
00519   }
00520 }
00521 
00522 //-----------------------------------------------------------------------------
00523 QPtrList<KMMessage> KMFolderImap::splitMessageList(const QString& set,
00524                                                    QPtrList<KMMessage>& msgList)
00525 {
00526   int lastcomma = set.findRev(",");
00527   int lastdub = set.findRev(":");
00528   int last = 0;
00529   if (lastdub > lastcomma) last = lastdub;
00530   else last = lastcomma;
00531   last++;
00532   if (last < 0) last = set.length();
00533   // the last uid of the current set
00534   const QString last_uid = set.right(set.length() - last);
00535   QPtrList<KMMessage> temp_msgs;
00536   QString uid;
00537   if (!last_uid.isEmpty())
00538   {
00539     QPtrListIterator<KMMessage> it( msgList );
00540     KMMessage* msg = 0;
00541     while ( (msg = it.current()) != 0 )
00542     {
00543       // append the msg to the new list and delete it from the old
00544       temp_msgs.append(msg);
00545       uid.setNum( msg->UID() );
00546       // remove modifies the current
00547       msgList.remove(msg);
00548       if (uid == last_uid) break;
00549     }
00550   }
00551   else
00552   {
00553     // probably only one element
00554     temp_msgs = msgList;
00555   }
00556 
00557   return temp_msgs;
00558 }
00559 
00560 //-----------------------------------------------------------------------------
00561 KMMessage* KMFolderImap::take(int idx)
00562 {
00563   KMMsgBase* mb(mMsgList[idx]);
00564   if (!mb) return 0;
00565   if (!mb->isMessage()) readMsg(idx);
00566 
00567   KMMessage *msg = static_cast<KMMessage*>(mb);
00568   deleteMessage(msg);
00569 
00570   mLastUid = 0;
00571   return KMFolderMbox::take(idx);
00572 }
00573 
00574 void KMFolderImap::take(QPtrList<KMMessage> msgList)
00575 {
00576   deleteMessage(msgList);
00577 
00578   mLastUid = 0;
00579   KMFolderMbox::take(msgList);
00580 }
00581 
00582 //-----------------------------------------------------------------------------
00583 void KMFolderImap::slotListNamespaces()
00584 {
00585   disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
00586       this, SLOT( slotListNamespaces() ) );
00587   if ( mAccount->makeConnection() == ImapAccountBase::Error )
00588   {
00589     kdWarning(5006) << "slotListNamespaces - got no connection" << endl;
00590     return;
00591   } else if ( mAccount->makeConnection() == ImapAccountBase::Connecting )
00592   {
00593     // wait for the connectionResult
00594     kdDebug(5006) << "slotListNamespaces - waiting for connection" << endl;
00595     connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
00596         this, SLOT( slotListNamespaces() ) );
00597     return;
00598   }
00599   kdDebug(5006) << "slotListNamespaces" << endl;
00600   // reset subfolder states recursively
00601   setSubfolderState( imapNoInformation );
00602   mSubfolderState = imapListingInProgress;
00603   mAccount->setHasInbox( false );
00604 
00605   ImapAccountBase::ListType type = ImapAccountBase::List;
00606   if ( mAccount->onlySubscribedFolders() )
00607     type = ImapAccountBase::ListSubscribed;
00608 
00609   ImapAccountBase::nsMap map = mAccount->namespaces();
00610   QStringList personal = map[ImapAccountBase::PersonalNS];
00611   // start personal namespace listing and send it directly to slotListResult
00612   for ( QStringList::Iterator it = personal.begin(); it != personal.end(); ++it )
00613   {
00614     KMail::ListJob* job = new KMail::ListJob( mAccount, type, this,
00615     mAccount->addPathToNamespace( *it ) );
00616     job->setNamespace( *it );
00617     connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
00618             const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
00619         this, SLOT(slotListResult(const QStringList&, const QStringList&,
00620             const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
00621     job->start();
00622   }
00623 
00624   // and now we list all other namespaces and check them ourself
00625   QStringList ns = map[ImapAccountBase::OtherUsersNS];
00626   ns += map[ImapAccountBase::SharedNS];
00627   for ( QStringList::Iterator it = ns.begin(); it != ns.end(); ++it )
00628   {
00629     KMail::ListJob* job = new  KMail::ListJob( mAccount, type, this, mAccount->addPathToNamespace( *it ) );
00630     connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
00631             const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
00632         this, SLOT(slotCheckNamespace(const QStringList&, const QStringList&,
00633             const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
00634     job->start();
00635   }
00636 }
00637 
00638 //-----------------------------------------------------------------------------
00639 void KMFolderImap::slotCheckNamespace( const QStringList& subfolderNames,
00640                                        const QStringList& subfolderPaths,
00641                                        const QStringList& subfolderMimeTypes,
00642                                        const QStringList& subfolderAttributes,
00643                                        const ImapAccountBase::jobData& jobData )
00644 {
00645   kdDebug(5006) << "slotCheckNamespace - " << subfolderNames.join(",") << endl;
00646 
00647   // get a correct foldername:
00648   // strip / and make sure it does not contain the delimiter
00649   QString name = jobData.path.mid( 1, jobData.path.length()-2 );
00650   name.remove( mAccount->delimiterForNamespace( name ) );
00651   if ( name.isEmpty() ) {
00652     // happens when an empty namespace is defined
00653     slotListResult( subfolderNames, subfolderPaths,
00654         subfolderMimeTypes, subfolderAttributes, jobData );
00655     return;
00656   }
00657 
00658   folder()->createChildFolder();
00659   KMFolderNode *node = 0;
00660   for ( node = folder()->child()->first(); node;
00661         node = folder()->child()->next())
00662   {
00663     if ( !node->isDir() && node->name() == name )
00664       break;
00665   }
00666   if ( subfolderNames.isEmpty() )
00667   {
00668     if ( node )
00669     {
00670       kdDebug(5006) << "delete namespace folder " << name << endl;
00671       KMFolder *fld = static_cast<KMFolder*>(node);
00672       KMFolderImap* nsFolder = static_cast<KMFolderImap*>(fld->storage());
00673       nsFolder->setAlreadyRemoved( true );
00674       kmkernel->imapFolderMgr()->remove( fld );
00675     }
00676   } else {
00677     if ( node )
00678     {
00679       // folder exists so pass on the attributes
00680       kdDebug(5006) << "found namespace folder " << name << endl;
00681       if ( !mAccount->listOnlyOpenFolders() )
00682       {
00683         KMFolderImap* nsFolder =
00684           static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage());
00685         nsFolder->slotListResult( subfolderNames, subfolderPaths,
00686             subfolderMimeTypes, subfolderAttributes, jobData );
00687       }
00688     } else
00689     {
00690       // create folder
00691       kdDebug(5006) << "create namespace folder " << name << endl;
00692       KMFolder *fld = folder()->child()->createFolder( name );
00693       if ( fld ) {
00694         KMFolderImap* f = static_cast<KMFolderImap*> ( fld->storage() );
00695         f->initializeFrom( this, mAccount->addPathToNamespace( name ),
00696             "inode/directory" );
00697         f->close();
00698         if ( !mAccount->listOnlyOpenFolders() )
00699         {
00700           f->slotListResult( subfolderNames, subfolderPaths,
00701               subfolderMimeTypes, subfolderAttributes, jobData );
00702         }
00703       }
00704       kmkernel->imapFolderMgr()->contentsChanged();
00705     }
00706   }
00707 }
00708 
00709 //-----------------------------------------------------------------------------
00710 bool KMFolderImap::listDirectory()
00711 {
00712   if ( !mAccount ||
00713        ( mAccount && mAccount->makeConnection() == ImapAccountBase::Error ) )
00714   {
00715     kdDebug(5006) << "KMFolderImap::listDirectory - got no connection" << endl;
00716     return false;
00717   }
00718 
00719   if ( this == mAccount->rootFolder() )
00720   {
00721     // a new listing started
00722     slotListNamespaces();
00723     return true;
00724   }
00725   mSubfolderState = imapListingInProgress;
00726 
00727   // get the folders
00728   ImapAccountBase::ListType type = ImapAccountBase::List;
00729   if ( mAccount->onlySubscribedFolders() )
00730     type = ImapAccountBase::ListSubscribed;
00731   KMail::ListJob* job = new  KMail::ListJob( mAccount, type, this );
00732   job->setParentProgressItem( account()->listDirProgressItem() );
00733   connect( job, SIGNAL(receivedFolders(const QStringList&, const QStringList&,
00734           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)),
00735       this, SLOT(slotListResult(const QStringList&, const QStringList&,
00736           const QStringList&, const QStringList&, const ImapAccountBase::jobData&)));
00737   job->start();
00738 
00739   return true;
00740 }
00741 
00742 
00743 //-----------------------------------------------------------------------------
00744 void KMFolderImap::slotListResult( const QStringList& subfolderNames,
00745                                    const QStringList& subfolderPaths,
00746                                    const QStringList& subfolderMimeTypes,
00747                                    const QStringList& subfolderAttributes,
00748                                    const ImapAccountBase::jobData& jobData )
00749 {
00750   mSubfolderState = imapFinished;
00751   //kdDebug(5006) << label() << ": folderNames=" << subfolderNames << " folderPaths="
00752   //<< subfolderPaths << " mimeTypes=" << subfolderMimeTypes << endl;
00753 
00754   // don't react on changes
00755   kmkernel->imapFolderMgr()->quiet(true);
00756 
00757   bool root = ( this == mAccount->rootFolder() );
00758   folder()->createChildFolder();
00759   if ( root && !mAccount->hasInbox() )
00760   {
00761     // create the INBOX
00762     initInbox();
00763   }
00764 
00765   // see if we have a better parent
00766   // if you have a prefix that contains a folder (e.g "INBOX.") the folders
00767   // need to be created underneath it
00768   if ( root && !subfolderNames.empty() )
00769   {
00770     KMFolderImap* parent = findParent( subfolderPaths.first(), subfolderNames.first() );
00771     if ( parent )
00772     {
00773       kdDebug(5006) << "KMFolderImap::slotListResult - pass listing to "
00774         << parent->label() << endl;
00775       parent->slotListResult( subfolderNames, subfolderPaths,
00776           subfolderMimeTypes, subfolderAttributes, jobData );
00777       // cleanup
00778       QStringList list;
00779       checkFolders( list, jobData.curNamespace );
00780       // finish
00781       emit directoryListingFinished( this );
00782       kmkernel->imapFolderMgr()->quiet( false );
00783       return;
00784     }
00785   }
00786 
00787   bool emptyList = ( root && subfolderNames.empty() );
00788   if ( !emptyList )
00789   {
00790     checkFolders( subfolderNames, jobData.curNamespace );
00791   }
00792 
00793   KMFolderImap *f = 0;
00794   KMFolderNode *node = 0;
00795   for ( uint i = 0; i < subfolderNames.count(); i++ )
00796   {
00797     bool settingsChanged = false;
00798     // create folders if necessary
00799     for ( node = folder()->child()->first(); node;
00800           node = folder()->child()->next() ) {
00801       if ( !node->isDir() && node->name() == subfolderNames[i] )
00802         break;
00803     }
00804     if ( node ) {
00805       f = static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage());
00806     }
00807     else if ( subfolderPaths[i].upper() != "/INBOX/" )
00808     {
00809       kdDebug(5006) << "create folder " << subfolderNames[i] << endl;
00810       KMFolder *fld = folder()->child()->createFolder(subfolderNames[i]);
00811       if ( fld ) {
00812         f = static_cast<KMFolderImap*> ( fld->storage() );
00813         f->close();
00814         settingsChanged = true;
00815       } else {
00816         kdWarning(5006) << "can't create folder " << subfolderNames[i] << endl;
00817       }
00818     }
00819     if ( f )
00820     {
00821       // sanity check
00822       if ( f->imapPath().isEmpty() ) {
00823         settingsChanged = true;
00824       }
00825       // update progress
00826       account()->listDirProgressItem()->incCompletedItems();
00827       account()->listDirProgressItem()->updateProgress();
00828       account()->listDirProgressItem()->setStatus( folder()->prettyURL() + i18n(" completed") );
00829 
00830       f->initializeFrom( this, subfolderPaths[i], subfolderMimeTypes[i] );
00831       f->setChildrenState( subfolderAttributes[i] );
00832       if ( mAccount->listOnlyOpenFolders() &&
00833            f->hasChildren() != FolderStorage::ChildrenUnknown )
00834       {
00835         settingsChanged = true;
00836       }
00837 
00838       if ( settingsChanged )
00839       {
00840         // tell the tree our information changed
00841         kmkernel->imapFolderMgr()->contentsChanged();
00842       }
00843       if ( ( subfolderMimeTypes[i] == "message/directory" ||
00844              subfolderMimeTypes[i] == "inode/directory" ) &&
00845            !mAccount->listOnlyOpenFolders() )
00846       {
00847         f->listDirectory();
00848       }
00849     } else {
00850       kdWarning(5006) << "can't find folder " << subfolderNames[i] << endl;
00851     }
00852   } // for subfolders
00853 
00854   // now others should react on the changes
00855   kmkernel->imapFolderMgr()->quiet( false );
00856   emit directoryListingFinished( this );
00857   account()->listDirProgressItem()->setComplete();
00858 }
00859 
00860 //-----------------------------------------------------------------------------
00861 void KMFolderImap::initInbox()
00862 {
00863   KMFolderImap *f = 0;
00864   KMFolderNode *node = 0;
00865 
00866   for (node = folder()->child()->first(); node;
00867       node = folder()->child()->next()) {
00868     if (!node->isDir() && node->name() == "INBOX") break;
00869   }
00870   if (node) {
00871     f = static_cast<KMFolderImap*>(static_cast<KMFolder*>(node)->storage());
00872   } else {
00873     f = static_cast<KMFolderImap*>
00874       (folder()->child()->createFolder("INBOX", true)->storage());
00875     if ( f )
00876     {
00877       f->folder()->setLabel( i18n("inbox") );
00878       f->close();
00879     }
00880     kmkernel->imapFolderMgr()->contentsChanged();
00881   }
00882   if ( f ) {
00883     f->initializeFrom( this, "/INBOX/", "message/directory" );
00884     f->setChildrenState( QString::null );
00885   }
00886   // so we have an INBOX
00887   mAccount->setHasInbox( true );
00888 }
00889 
00890 //-----------------------------------------------------------------------------
00891 KMFolderImap* KMFolderImap::findParent( const QString& path, const QString& name )
00892 {
00893   QString parent = path.left( path.length() - name.length() - 2 );
00894   if ( parent.length() > 1 )
00895   {
00896     // extract name of the parent
00897     parent = parent.right( parent.length() - 1 );
00898     if ( parent != label() )
00899     {
00900       KMFolderNode *node = folder()->child()->first();
00901       // look for a better parent
00902       while ( node )
00903       {
00904         if ( node->name() == parent )
00905         {
00906           KMFolder* fld = static_cast<KMFolder*>(node);
00907           KMFolderImap* imapFld = static_cast<KMFolderImap*>( fld->storage() );
00908           return imapFld;
00909         }
00910         node = folder()->child()->next();
00911       }
00912     }
00913   }
00914   return 0;
00915 }
00916 
00917 //-----------------------------------------------------------------------------
00918 void KMFolderImap::checkFolders( const QStringList& subfolderNames,
00919     const QString& myNamespace )
00920 {
00921   QPtrList<KMFolder> toRemove;
00922   KMFolderNode *node = folder()->child()->first();
00923   while ( node )
00924   {
00925     if ( !node->isDir() && subfolderNames.findIndex(node->name()) == -1 )
00926     {
00927       KMFolder* fld = static_cast<KMFolder*>(node);
00928       KMFolderImap* imapFld = static_cast<KMFolderImap*>( fld->storage() );
00929       // as more than one namespace can be listed in the root folder we need to make sure
00930       // that the folder is within the current namespace
00931       bool isInNamespace = ( myNamespace.isEmpty() ||
00932           myNamespace == mAccount->namespaceForFolder( imapFld ) );
00933       kdDebug(5006) << node->name() << " in namespace " << myNamespace << ":" <<
00934         isInNamespace << endl;
00935       // ignore some cases
00936       QString name = node->name();
00937       bool ignore = ( ( this == mAccount->rootFolder() ) &&
00938           ( imapFld->imapPath() == "/INBOX/" ||
00939             mAccount->isNamespaceFolder( name ) ||
00940         !isInNamespace ) );
00941       // additional sanity check for broken folders
00942       if ( imapFld->imapPath().isEmpty() ) {
00943         ignore = false;
00944       }
00945       if ( !ignore )
00946       {
00947         // remove the folder without server round trip
00948         kdDebug(5006) << "checkFolders - " << node->name() << " disappeared" << endl;
00949         imapFld->setAlreadyRemoved( true );
00950         toRemove.append( fld );
00951       } else {
00952         kdDebug(5006) << "checkFolders - " << node->name() << " ignored" << endl;
00953       }
00954     }
00955     node = folder()->child()->next();
00956   }
00957   // remove folders
00958   for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() )
00959     kmkernel->imapFolderMgr()->remove( doomed );
00960 }
00961 
00962 //-----------------------------------------------------------------------------
00963 void KMFolderImap::initializeFrom( KMFolderImap* parent, QString folderPath,
00964                                    QString mimeType )
00965 {
00966   setAccount( parent->account() );
00967   setImapPath( folderPath );
00968   setNoContent( mimeType == "inode/directory" );
00969   setNoChildren( mimeType == "message/digest" );
00970 }
00971 
00972 //-----------------------------------------------------------------------------
00973 void KMFolderImap::setChildrenState( QString attributes )
00974 {
00975   // update children state
00976   if ( attributes.find( "haschildren", 0, false ) != -1 )
00977   {
00978     setHasChildren( FolderStorage::HasChildren );
00979   } else if ( attributes.find( "hasnochildren", 0, false ) != -1 ||
00980               attributes.find( "noinferiors", 0, false ) != -1 )
00981   {
00982     setHasChildren( FolderStorage::HasNoChildren );
00983   } else
00984   {
00985     if ( mAccount->listOnlyOpenFolders() ) {
00986       setHasChildren( FolderStorage::HasChildren );
00987     } else {
00988       setHasChildren( FolderStorage::ChildrenUnknown );
00989     }
00990   }
00991 }
00992 
00993 //-----------------------------------------------------------------------------
00994 void KMFolderImap::checkValidity()
00995 {
00996   if (!mAccount) {
00997     emit folderComplete(this, false);
00998     close();
00999     return;
01000   }
01001   KURL url = mAccount->getUrl();
01002   url.setPath(imapPath() + ";UID=0:0");
01003   kdDebug(5006) << "KMFolderImap::checkValidity of: " << imapPath() << endl;
01004 
01005   // Start with a clean slate
01006   disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01007               this, SLOT( checkValidity() ) );
01008 
01009   KMAcctImap::ConnectionState connectionState = mAccount->makeConnection();
01010   if ( connectionState == ImapAccountBase::Error ) {
01011     kdDebug(5006) << "KMFolderImap::checkValidity - got no connection" << endl;
01012     emit folderComplete(this, FALSE);
01013     mContentState = imapNoInformation;
01014     close();
01015     return;
01016   } else if ( connectionState == ImapAccountBase::Connecting ) {
01017     // We'll wait for the connectionResult signal from the account. If it
01018     // errors, the above will catch it.
01019     kdDebug(5006) << "CheckValidity - waiting for connection" << endl;
01020     connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01021         this, SLOT( checkValidity() ) );
01022     return;
01023   }
01024   // Only check once at a time.
01025   if (mCheckingValidity) {
01026     kdDebug(5006) << "KMFolderImap::checkValidity - already checking" << endl;
01027     close();
01028     return;
01029   }
01030   // otherwise we already are inside a mailcheck
01031   if ( !mMailCheckProgressItem ) {
01032     ProgressItem* parent = ( account()->checkingSingleFolder() ? 0 :
01033         account()->mailCheckProgressItem() );
01034     mMailCheckProgressItem = ProgressManager::createProgressItem(
01035               parent,
01036               "MailCheck" + folder()->prettyURL(),
01037               QStyleSheet::escape( folder()->prettyURL() ),
01038               i18n("checking"),
01039               false,
01040               account()->useSSL() || account()->useTLS() );
01041   } else {
01042     mMailCheckProgressItem->setProgress(0);
01043   }
01044   if ( account()->mailCheckProgressItem() ) {
01045     account()->mailCheckProgressItem()->setStatus( folder()->prettyURL() );
01046   }
01047   ImapAccountBase::jobData jd( url.url() );
01048   KIO::SimpleJob *job = KIO::get(url, FALSE, FALSE);
01049   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01050   mAccount->insertJob(job, jd);
01051   connect(job, SIGNAL(result(KIO::Job *)),
01052           SLOT(slotCheckValidityResult(KIO::Job *)));
01053   connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
01054           SLOT(slotSimpleData(KIO::Job *, const QByteArray &)));
01055   // Only check once at a time.
01056   mCheckingValidity = true;
01057 }
01058 
01059 
01060 //-----------------------------------------------------------------------------
01061 ulong KMFolderImap::lastUid()
01062 {
01063   if ( mLastUid > 0 )
01064       return mLastUid;
01065   open();
01066   if (count() > 0)
01067   {
01068     KMMsgBase * base = getMsgBase(count()-1);
01069     mLastUid = base->UID();
01070   }
01071   close();
01072   return mLastUid;
01073 }
01074 
01075 
01076 //-----------------------------------------------------------------------------
01077 void KMFolderImap::slotCheckValidityResult(KIO::Job * job)
01078 {
01079   kdDebug(5006) << "KMFolderImap::slotCheckValidityResult of: " << fileName() << endl;
01080   mCheckingValidity = false;
01081   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01082   if ( it == mAccount->jobsEnd() ) return;
01083   if (job->error()) {
01084     if ( job->error() != KIO::ERR_ACCESS_DENIED ) {
01085       // we suppress access denied messages because they are normally a result of
01086       // explicitely set ACLs. Do not save this information (e.g. setNoContent) so that
01087       // we notice when this changes
01088       mAccount->handleJobError( job, i18n("Error while querying the server status.") );
01089     }
01090     mContentState = imapNoInformation;
01091     emit folderComplete(this, FALSE);
01092     close();
01093   } else {
01094     QCString cstr((*it).data.data(), (*it).data.size() + 1);
01095     int a = cstr.find("X-uidValidity: ");
01096     int b = cstr.find("\r\n", a);
01097     QString uidv;
01098     if ( (b - a - 15) >= 0 )
01099         uidv = cstr.mid(a + 15, b - a - 15);
01100     a = cstr.find("X-Access: ");
01101     b = cstr.find("\r\n", a);
01102     QString access;
01103     if ( (b - a - 10) >= 0 )
01104         access = cstr.mid(a + 10, b - a - 10);
01105     mReadOnly = access == "Read only";
01106     a = cstr.find("X-Count: ");
01107     b = cstr.find("\r\n", a);
01108     int exists = -1;
01109     bool ok = false;
01110     if ( (b - a - 9) >= 0 )
01111         exists = cstr.mid(a + 9, b - a - 9).toInt(&ok);
01112     if ( !ok ) exists = -1;
01113     QString startUid;
01114     if (uidValidity() != uidv)
01115     {
01116       // uidValidity changed
01117       kdDebug(5006) << k_funcinfo << "uidValidty changed from "
01118        << uidValidity() << " to " << uidv << endl;
01119       if ( !uidValidity().isEmpty() )
01120       {
01121         mAccount->ignoreJobsForFolder( folder() );
01122         mUidMetaDataMap.clear();
01123       }
01124       mLastUid = 0;
01125       setUidValidity(uidv);
01126       writeConfig();
01127     } else {
01128       if (!mCheckFlags)
01129         startUid = QString::number(lastUid() + 1);
01130     }
01131     mAccount->removeJob(it);
01132     if ( mMailCheckProgressItem )
01133     {
01134       if ( startUid.isEmpty() ) {
01135         // flags for all messages are loaded
01136         mMailCheckProgressItem->setTotalItems( exists );
01137       } else {
01138         // only an approximation but doesn't hurt
01139         int remain = exists - count();
01140         if ( remain < 0 ) remain = 1;
01141         mMailCheckProgressItem->setTotalItems( remain );
01142       }
01143       mMailCheckProgressItem->setCompletedItems( 0 );
01144     }
01145     reallyGetFolder(startUid);
01146   }
01147 }
01148 
01149 //-----------------------------------------------------------------------------
01150 void KMFolderImap::getAndCheckFolder(bool force)
01151 {
01152   if (mNoContent)
01153     return getFolder(force);
01154 
01155   if ( mAccount )
01156     mAccount->processNewMailSingleFolder( folder() );
01157   if (force) {
01158     // force an update
01159     mCheckFlags = TRUE;
01160   }
01161 }
01162 
01163 //-----------------------------------------------------------------------------
01164 void KMFolderImap::getFolder(bool force)
01165 {
01166   mGuessedUnreadMsgs = -1;
01167   if (mNoContent)
01168   {
01169     mContentState = imapFinished;
01170     emit folderComplete(this, true);
01171     return;
01172   }
01173   open();
01174   mContentState = imapListingInProgress;
01175   if (force) {
01176     // force an update
01177     mCheckFlags = TRUE;
01178   }
01179   checkValidity();
01180 }
01181 
01182 
01183 //-----------------------------------------------------------------------------
01184 void KMFolderImap::reallyGetFolder(const QString &startUid)
01185 {
01186   KURL url = mAccount->getUrl();
01187   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01188   {
01189     mContentState = imapNoInformation;
01190     emit folderComplete(this, FALSE);
01191     close();
01192     return;
01193   }
01194   quiet(true);
01195   if (startUid.isEmpty())
01196   {
01197     if ( mMailCheckProgressItem )
01198       mMailCheckProgressItem->setStatus( i18n("Retrieving message status") );
01199     url.setPath(imapPath() + ";SECTION=UID FLAGS");
01200     KIO::SimpleJob *job = KIO::listDir(url, FALSE);
01201     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01202     ImapAccountBase::jobData jd( url.url(), folder() );
01203     jd.cancellable = true;
01204     mAccount->insertJob(job, jd);
01205     connect(job, SIGNAL(result(KIO::Job *)),
01206             this, SLOT(slotListFolderResult(KIO::Job *)));
01207     connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
01208             this, SLOT(slotListFolderEntries(KIO::Job *,
01209             const KIO::UDSEntryList &)));
01210   } else {
01211     mContentState = imapDownloadInProgress;
01212     if ( mMailCheckProgressItem )
01213       mMailCheckProgressItem->setStatus( i18n("Retrieving messages") );
01214     url.setPath(imapPath() + ";UID=" + startUid
01215       + ":*;SECTION=ENVELOPE");
01216     KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
01217     KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
01218     ImapAccountBase::jobData jd( url.url(), folder() );
01219     jd.cancellable = true;
01220     mAccount->insertJob(newJob, jd);
01221     connect(newJob, SIGNAL(result(KIO::Job *)),
01222             this, SLOT(slotGetLastMessagesResult(KIO::Job *)));
01223     connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
01224             this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
01225   }
01226 }
01227 
01228 
01229 //-----------------------------------------------------------------------------
01230 void KMFolderImap::slotListFolderResult(KIO::Job * job)
01231 {
01232   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01233   if ( it == mAccount->jobsEnd() ) return;
01234   QString uids;
01235   if (job->error())
01236   {
01237     mAccount->handleJobError( job,
01238          i18n("Error while listing the contents of the folder %1.").arg( label() ) );
01239     mAccount->removeJob(it);
01240     finishMailCheck( imapNoInformation );
01241     return;
01242   }
01243   mCheckFlags = FALSE;
01244   QStringList::Iterator uid;
01245   /*
01246     The code below does the following:
01247     - for each mail in the local store and each entry we got from the server,
01248       compare the local uid with the one from the server and update the status
01249       flags of the mails
01250     - for all mails that are not already locally present, start a job which
01251       gets the envelope of each
01252     - remove all locally present mails if the server does not list them anymore
01253   */
01254   if ( count() ) {
01255     int idx = 0, c, serverFlags;
01256     ulong mailUid, serverUid;
01257     uid = (*it).items.begin();
01258     while ( idx < count() && uid != (*it).items.end() ) {
01259       KMMsgBase *msgBase = getMsgBase( idx );
01260       mailUid = msgBase->UID();
01261       // parse the uid from the server and the flags out of the list from
01262       // the server. Format: 1234, 1
01263       c = (*uid).find(",");
01264       serverUid = (*uid).left( c ).toLong();
01265       serverFlags = (*uid).mid( c+1 ).toInt();
01266       if ( mailUid < serverUid ) {
01267         removeMsg( idx, TRUE );
01268       } else if ( mailUid == serverUid ) {
01269         // if this is a read only folder, ignore status updates from the server
01270         // since we can't write our status back our local version is what has to
01271         // be considered correct.
01272         if (!mReadOnly)
01273           flagsToStatus( msgBase, serverFlags, false );
01274         idx++;
01275         uid = (*it).items.remove(uid);
01276         if ( msgBase->getMsgSerNum() > 0 ) {
01277           saveMsgMetaData( static_cast<KMMessage*>(msgBase) );
01278         }
01279       }
01280       else break;  // happens only, if deleted mails reappear on the server
01281     }
01282     // remove all remaining entries in the local cache, they are no longer
01283     // present on the server
01284     while (idx < count()) removeMsg(idx, TRUE);
01285   }
01286   // strip the flags from the list of uids, so it can be reused
01287   for (uid = (*it).items.begin(); uid != (*it).items.end(); ++uid)
01288     (*uid).truncate((*uid).find(","));
01289   ImapAccountBase::jobData jd( QString::null, (*it).parent );
01290   jd.total = (*it).items.count();
01291   if (jd.total == 0)
01292   {
01293     finishMailCheck( imapFinished );
01294     mAccount->removeJob(it);
01295     return;
01296   }
01297   if ( mMailCheckProgressItem )
01298   {
01299     // next step for the progressitem
01300     mMailCheckProgressItem->setCompletedItems( 0 );
01301     mMailCheckProgressItem->setTotalItems( jd.total );
01302     mMailCheckProgressItem->setProgress( 0 );
01303     mMailCheckProgressItem->setStatus( i18n("Retrieving messages") );
01304   }
01305 
01306   QStringList sets;
01307   uid = (*it).items.begin();
01308   if (jd.total == 1) sets.append(*uid + ":" + *uid);
01309   else sets = makeSets( (*it).items );
01310   mAccount->removeJob(it); // don't use *it below
01311 
01312   // Now kick off the getting of envelopes for the new mails in the folder
01313   for (QStringList::Iterator i = sets.begin(); i != sets.end(); ++i)
01314   {
01315     mContentState = imapDownloadInProgress;
01316     KURL url = mAccount->getUrl();
01317     url.setPath(imapPath() + ";UID=" + *i + ";SECTION=ENVELOPE");
01318     KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
01319     jd.url = url.url();
01320     KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
01321     mAccount->insertJob(newJob, jd);
01322     connect(newJob, SIGNAL(result(KIO::Job *)),
01323         this, (i == sets.at(sets.count() - 1))
01324         ? SLOT(slotGetLastMessagesResult(KIO::Job *))
01325         : SLOT(slotGetMessagesResult(KIO::Job *)));
01326     connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
01327         this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
01328   }
01329 }
01330 
01331 
01332 //-----------------------------------------------------------------------------
01333 void KMFolderImap::slotListFolderEntries(KIO::Job * job,
01334   const KIO::UDSEntryList & uds)
01335 {
01336   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01337   if ( it == mAccount->jobsEnd() ) return;
01338   QString mimeType, name;
01339   long int flags = 0;
01340   for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
01341     udsIt != uds.end(); udsIt++)
01342   {
01343     for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
01344       eIt != (*udsIt).end(); eIt++)
01345     {
01346       if ((*eIt).m_uds == KIO::UDS_NAME)
01347         name = (*eIt).m_str;
01348       else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
01349         mimeType = (*eIt).m_str;
01350       else if ((*eIt).m_uds == KIO::UDS_ACCESS)
01351         flags = (*eIt).m_long;
01352     }
01353     if ((mimeType == "message/rfc822-imap" || mimeType == "message/rfc822") &&
01354         !(flags & 8)) {
01355       (*it).items.append(name + "," + QString::number(flags));
01356       if ( mMailCheckProgressItem ) {
01357         mMailCheckProgressItem->incCompletedItems();
01358         mMailCheckProgressItem->updateProgress();
01359       }
01360     }
01361   }
01362 }
01363 
01364 
01365 // debugging helper
01366 //X static QString flagsToString( int flags )
01367 //X {
01368 //X     QString str("(");
01369 //X     if ( flags & 4 ) {
01370 //X         str += "\\Flagged ";
01371 //X     }
01372 //X     if ( flags & 2 ) {
01373 //X         str += "\\Answered ";
01374 //X     }
01375 //X     if ( flags & 1 ) {
01376 //X         str += "\\Seen";
01377 //X     }
01378 //X     str += ")";
01379 //X     return str;
01380 //X }
01381 
01382 //-----------------------------------------------------------------------------
01383 void KMFolderImap::flagsToStatus(KMMsgBase *msg, int flags, bool newMsg)
01384 {
01385   if ( !msg ) return;
01386 
01387   const KMMsgStatus oldStatus = msg->status();
01388   // Set flags if they are new
01389   if ( (flags & 4) && (oldStatus & KMMsgStatusFlag) == 0 )
01390     msg->setStatus( KMMsgStatusFlag );
01391   if ( (flags & 2) && (oldStatus & KMMsgStatusReplied) == 0 )
01392     msg->setStatus( KMMsgStatusReplied );
01393   if ( (flags & 1) && (oldStatus & KMMsgStatusOld) == 0 )
01394     msg->setStatus( KMMsgStatusOld );
01395 
01396   // Toggle flags if they changed
01397 //  if ( ( (flags & 4) > 0 ) != ( (oldStatus & KMMsgStatusFlag) > 0 ) )
01398 //    msg->toggleStatus( KMMsgStatusFlag );
01399 //  if ( ( (flags & 2) > 0 ) != ( (oldStatus & KMMsgStatusReplied) > 0 ) )
01400 //    msg->toggleStatus( KMMsgStatusReplied );
01401 //  if ( ( (flags & 1) > 0 ) != ( (oldStatus & KMMsgStatusOld) > 0 ) )
01402 //    msg->toggleStatus( KMMsgStatusOld );
01403 
01404   // In case the message does not have the seen flag set, override our local
01405   // notion that it is read. Otherwise the count of unread messages and the
01406   // number of messages which actually show up as read can go out of sync.
01407   if (msg->isOfUnknownStatus() || !(flags&1) ) {
01408     if (newMsg) {
01409       if ( (oldStatus & KMMsgStatusNew) == 0 )
01410         msg->setStatus( KMMsgStatusNew );
01411     } else {
01412       if ( (oldStatus & KMMsgStatusUnread) == 0 )
01413         msg->setStatus( KMMsgStatusUnread );
01414     }
01415   }
01416 }
01417 
01418 
01419 //-----------------------------------------------------------------------------
01420 QString KMFolderImap::statusToFlags(KMMsgStatus status)
01421 {
01422   QString flags;
01423   if (status & KMMsgStatusDeleted)
01424     flags = "\\DELETED";
01425   else {
01426     if (status & KMMsgStatusOld || status & KMMsgStatusRead)
01427       flags = "\\SEEN ";
01428     if (status & KMMsgStatusReplied)
01429       flags += "\\ANSWERED ";
01430     if (status & KMMsgStatusFlag)
01431       flags += "\\FLAGGED";
01432   }
01433 
01434   return flags.simplifyWhiteSpace();
01435 }
01436 
01437 //-------------------------------------------------------------
01438 void
01439 KMFolderImap::ignoreJobsForMessage( KMMessage* msg )
01440 {
01441   if ( !msg || msg->transferInProgress() ||
01442        !msg->parent() || msg->parent()->folderType() != KMFolderTypeImap )
01443     return;
01444   KMAcctImap *account;
01445   if ( !(account = static_cast<KMFolderImap*>(msg->storage())->account()) )
01446     return;
01447 
01448   account->ignoreJobsForMessage( msg );
01449 }
01450 
01451 //-----------------------------------------------------------------------------
01452 void KMFolderImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
01453 {
01454   if ( data.isEmpty() ) return; // optimization
01455   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01456   if ( it == mAccount->jobsEnd() ) return;
01457   (*it).cdata += QCString(data, data.size() + 1);
01458   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
01459   if ( pos == -1 ) {
01460     // if we do not find the pattern in the complete string we will not find
01461     // it in a substring.
01462     return;
01463   }
01464   if (pos > 0)
01465   {
01466     int p = (*it).cdata.find("\r\nX-uidValidity:");
01467     if (p != -1) setUidValidity((*it).cdata
01468       .mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
01469     int c = (*it).cdata.find("\r\nX-Count:");
01470     if ( c != -1 )
01471     {
01472       bool ok;
01473       int exists = (*it).cdata.mid( c+10,
01474           (*it).cdata.find("\r\n", c+1) - c-10 ).toInt(&ok);
01475       if ( ok && exists < count() ) {
01476         kdDebug(5006) << "KMFolderImap::slotGetMessagesData - server has less messages (" <<
01477           exists << ") then folder (" << count() << "), so reload" << endl;
01478         open();
01479         reallyGetFolder( QString::null );
01480         (*it).cdata.remove(0, pos);
01481         return;
01482       } else if ( ok ) {
01483         int delta = exists - count();
01484         if ( mMailCheckProgressItem ) {
01485           mMailCheckProgressItem->setTotalItems( delta );
01486         }
01487       }
01488     }
01489     (*it).cdata.remove(0, pos);
01490   }
01491   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01492   int flags;
01493   while (pos >= 0)
01494   {
01495     KMMessage *msg = new KMMessage;
01496     msg->setComplete( false );
01497     msg->setReadyToShow( false );
01498     // nothing between the boundaries, older UWs do that
01499     if ( pos != 14 ) {
01500       msg->fromString( (*it).cdata.mid(16, pos - 16) );
01501       flags = msg->headerField("X-Flags").toInt();
01502       ulong uid = msg->UID();
01503       KMMsgMetaData *md =  0;
01504       if ( mUidMetaDataMap.find( uid ) ) {
01505           md =  mUidMetaDataMap[uid];
01506       }
01507       ulong serNum = 0;
01508       if ( md ) {
01509         serNum = md->serNum();
01510       }
01511       bool ok = true;
01512       if ( uid <= lastUid() && serNum > 0 ) {
01513         // the UID is already known so no need to create it
01514         ok = false;
01515       }
01516       // deleted flag
01517       if ( flags & 8 )
01518         ok = false;
01519       if ( !ok ) {
01520         delete msg;
01521         msg = 0;
01522       } else {
01523         if ( serNum > 0 ) {
01524           // assign the sernum from the cache
01525           msg->setMsgSerNum( serNum );
01526         }
01527         // Transfer the status, if it is cached.
01528         if ( md ) {
01529           msg->setStatus( md->status() );
01530         } else if ( !mAccount->hasCapability("uidplus") ) {
01531           // see if we have cached the msgIdMD5 and get the status +
01532           // serial number from there
01533           QString id = msg->msgIdMD5();
01534           if ( mMetaDataMap.find( id ) ) {
01535             md =  mMetaDataMap[id];
01536             msg->setStatus( md->status() );
01537             if ( md->serNum() != 0 && serNum == 0 ) {
01538               msg->setMsgSerNum( md->serNum() );
01539             }
01540             mMetaDataMap.remove( id );
01541             delete md;
01542           }
01543         }
01544         KMFolderMbox::addMsg(msg, 0);
01545         // Merge with the flags from the server.
01546         flagsToStatus((KMMsgBase*)msg, flags);
01547         // set the correct size
01548         msg->setMsgSizeServer( msg->headerField("X-Length").toUInt() );
01549         msg->setUID(uid);
01550         if ( msg->getMsgSerNum() > 0 ) {
01551           saveMsgMetaData( msg );
01552         }
01553         // Filter messages that have arrived in the inbox folder
01554         if ( folder()->isSystemFolder() && imapPath() == "/INBOX/"
01555             && kmkernel->filterMgr()->atLeastOneIncomingFilterAppliesTo( mAccount->id() ) )
01556             mAccount->execFilters( msg->getMsgSerNum() );
01557 
01558         if ( count() > 1 ) {
01559           unGetMsg(count() - 1);
01560         }
01561         mLastUid = uid;
01562         if ( mMailCheckProgressItem ) {
01563           mMailCheckProgressItem->incCompletedItems();
01564           mMailCheckProgressItem->updateProgress();
01565         }
01566       }
01567     }
01568     (*it).cdata.remove(0, pos);
01569     (*it).done++;
01570     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01571   } // while
01572 }
01573 
01574 //-------------------------------------------------------------
01575 FolderJob*
01576 KMFolderImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
01577                            KMFolder *folder, QString partSpecifier,
01578                            const AttachmentStrategy *as ) const
01579 {
01580   KMFolderImap* kmfi = folder? dynamic_cast<KMFolderImap*>(folder->storage()) : 0;
01581   if ( jt == FolderJob::tGetMessage && partSpecifier == "STRUCTURE" &&
01582        mAccount && mAccount->loadOnDemand() &&
01583        ( msg->msgSizeServer() > 5000 || msg->msgSizeServer() == 0 ) &&
01584        ( msg->signatureState() == KMMsgNotSigned ||
01585          msg->signatureState() == KMMsgSignatureStateUnknown ) &&
01586        ( msg->encryptionState() == KMMsgNotEncrypted ||
01587          msg->encryptionState() == KMMsgEncryptionStateUnknown ) )
01588   {
01589     // load-on-demand: retrieve the BODYSTRUCTURE and to speed things up also the headers
01590     // this is not activated for small or signed messages
01591     ImapJob *job = new ImapJob( msg, jt, kmfi, "HEADER" );
01592     job->start();
01593     ImapJob *job2 = new ImapJob( msg, jt, kmfi, "STRUCTURE", as );
01594     job2->start();
01595     job->setParentFolder( this );
01596     return job;
01597   } else {
01598     // download complete message or part (attachment)
01599     if ( partSpecifier == "STRUCTURE" ) // hide from outside
01600       partSpecifier = QString::null;
01601 
01602     ImapJob *job = new ImapJob( msg, jt, kmfi, partSpecifier );
01603     job->setParentFolder( this );
01604     return job;
01605   }
01606 }
01607 
01608 //-------------------------------------------------------------
01609 FolderJob*
01610 KMFolderImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
01611                            FolderJob::JobType jt, KMFolder *folder ) const
01612 {
01613   KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder->storage());
01614   ImapJob *job = new ImapJob( msgList, sets, jt, kmfi );
01615   job->setParentFolder( this );
01616   return job;
01617 }
01618 
01619 //-----------------------------------------------------------------------------
01620 void KMFolderImap::getMessagesResult(KIO::Job * job, bool lastSet)
01621 {
01622   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01623   if ( it == mAccount->jobsEnd() ) return;
01624   if (job->error()) {
01625     mAccount->handleJobError( job, i18n("Error while retrieving messages.") );
01626     finishMailCheck( imapNoInformation );
01627     return;
01628   }
01629   if (lastSet) {
01630     finishMailCheck( imapFinished );
01631     mAccount->removeJob(it);
01632   }
01633 }
01634 
01635 
01636 //-----------------------------------------------------------------------------
01637 void KMFolderImap::slotGetLastMessagesResult(KIO::Job * job)
01638 {
01639   getMessagesResult(job, true);
01640 }
01641 
01642 
01643 //-----------------------------------------------------------------------------
01644 void KMFolderImap::slotGetMessagesResult(KIO::Job * job)
01645 {
01646   getMessagesResult(job, false);
01647 }
01648 
01649 
01650 //-----------------------------------------------------------------------------
01651 void KMFolderImap::createFolder(const QString &name, const QString& parentPath,
01652                                 bool askUser)
01653 {
01654   kdDebug(5006) << "KMFolderImap::createFolder - name=" << name << ",parent=" <<
01655     parentPath << ",askUser=" << askUser << endl;
01656   if ( mAccount->makeConnection() != ImapAccountBase::Connected ) {
01657     kdWarning(5006) << "KMFolderImap::createFolder - got no connection" << endl;
01658     return;
01659   }
01660   KURL url = mAccount->getUrl();
01661   QString parent = ( parentPath.isEmpty() ? imapPath() : parentPath );
01662   QString path = mAccount->createImapPath( parent, name );
01663   if ( askUser ) {
01664     path += "/;INFO=ASKUSER";
01665   }
01666   url.setPath( path );
01667 
01668   KIO::SimpleJob *job = KIO::mkdir(url);
01669   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01670   ImapAccountBase::jobData jd( url.url(), folder() );
01671   jd.items = name;
01672   mAccount->insertJob(job, jd);
01673   connect(job, SIGNAL(result(KIO::Job *)),
01674           this, SLOT(slotCreateFolderResult(KIO::Job *)));
01675 }
01676 
01677 
01678 //-----------------------------------------------------------------------------
01679 void KMFolderImap::slotCreateFolderResult(KIO::Job * job)
01680 {
01681   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01682   if ( it == mAccount->jobsEnd() ) return;
01683   if (job->error())
01684   {
01685     if ( job->error() == KIO::ERR_COULD_NOT_MKDIR ) {
01686       // Creating a folder failed, remove it from the tree.
01687       mAccount->listDirectory( );
01688     }
01689     mAccount->handleJobError( job, i18n("Error while creating a folder.") );
01690   } else {
01691     listDirectory();
01692     mAccount->removeJob(job);
01693   }
01694 }
01695 
01696 
01697 //-----------------------------------------------------------------------------
01698 static QTextCodec *sUtf7Codec = 0;
01699 
01700 QTextCodec * KMFolderImap::utf7Codec()
01701 {
01702   if (!sUtf7Codec) sUtf7Codec = QTextCodec::codecForName("utf-7");
01703   return sUtf7Codec;
01704 }
01705 
01706 
01707 //-----------------------------------------------------------------------------
01708 QString KMFolderImap::encodeFileName(const QString &name)
01709 {
01710   QString result = utf7Codec()->fromUnicode(name);
01711   return KURL::encode_string_no_slash(result);
01712 }
01713 
01714 
01715 //-----------------------------------------------------------------------------
01716 QString KMFolderImap::decodeFileName(const QString &name)
01717 {
01718   QString result = KURL::decode_string(name);
01719   return utf7Codec()->toUnicode(result.latin1());
01720 }
01721 
01722 //-----------------------------------------------------------------------------
01723 bool KMFolderImap::autoExpunge()
01724 {
01725   if (mAccount)
01726     return mAccount->autoExpunge();
01727 
01728   return false;
01729 }
01730 
01731 
01732 //-----------------------------------------------------------------------------
01733 void KMFolderImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
01734 {
01735   if ( data.isEmpty() ) return; // optimization
01736   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01737   if ( it == mAccount->jobsEnd() ) return;
01738   QBuffer buff((*it).data);
01739   buff.open(IO_WriteOnly | IO_Append);
01740   buff.writeBlock(data.data(), data.size());
01741   buff.close();
01742 }
01743 
01744 //-----------------------------------------------------------------------------
01745 void KMFolderImap::deleteMessage(KMMessage * msg)
01746 {
01747   mUidMetaDataMap.remove( msg->UID() );
01748   mMetaDataMap.remove( msg->msgIdMD5() );
01749   KURL url = mAccount->getUrl();
01750   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msg->storage());
01751   ulong uid = msg->UID();
01752   /* If the uid is empty the delete job below will nuke all mail in the
01753      folder, so we better safeguard against that. See ::expungeFolder, as
01754      to why. :( */
01755   if ( uid == 0 ) {
01756      kdDebug( 5006 ) << "KMFolderImap::deleteMessage: Attempt to delete "
01757                         "an empty UID. Aborting."  << endl;
01758      return;
01759   }
01760   url.setPath(msg_parent->imapPath() + ";UID=" + QString::number(uid) );
01761   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01762     return;
01763   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01764   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01765   ImapAccountBase::jobData jd( url.url(), 0 );
01766   mAccount->insertJob(job, jd);
01767   connect(job, SIGNAL(result(KIO::Job *)),
01768           mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01769 }
01770 
01771 void KMFolderImap::deleteMessage(const QPtrList<KMMessage>& msgList)
01772 {
01773   QPtrListIterator<KMMessage> it( msgList );
01774   KMMessage *msg;
01775   while ( (msg = it.current()) != 0 ) {
01776     ++it;
01777     mUidMetaDataMap.remove( msg->UID() );
01778     mMetaDataMap.remove( msg->msgIdMD5() );
01779   }
01780 
01781   QValueList<ulong> uids;
01782   getUids(msgList, uids);
01783   QStringList sets = makeSets(uids);
01784 
01785   KURL url = mAccount->getUrl();
01786   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msgList.getFirst()->storage());
01787   for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
01788   {
01789     QString uid = *it;
01790     // Don't delete with no uid, that nukes the folder. Should not happen, but
01791     // better safe than sorry.
01792     if ( uid.isEmpty() ) continue;
01793     url.setPath(msg_parent->imapPath() + ";UID=" + uid);
01794     if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01795       return;
01796     KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01797     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01798     ImapAccountBase::jobData jd( url.url(), 0 );
01799     mAccount->insertJob(job, jd);
01800     connect(job, SIGNAL(result(KIO::Job *)),
01801         mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01802   }
01803 }
01804 
01805 //-----------------------------------------------------------------------------
01806 void KMFolderImap::setStatus(int idx, KMMsgStatus status, bool toggle)
01807 {
01808   QValueList<int> ids; ids.append(idx);
01809   setStatus(ids, status, toggle);
01810 }
01811 
01812 void KMFolderImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01813 {
01814   FolderStorage::setStatus(ids, status, toggle);
01815   if (mReadOnly) return;
01816 
01817   /* The status has been already set in the local index. Update the flags on
01818    * the server. To avoid doing that for each message individually, group them
01819    * by the status string they will be assigned and make sets for each of those
01820    * groups of mails. This is necessary because the imap kio_slave status job
01821    * does not append flags but overwrites them. Example:
01822    *
01823    * 2 important mails and 3 unimportant mail, all unread. Mark all as read calls
01824    * this method with a list of uids. The 2 important mails need to get the string
01825    * \SEEN \FLAGGED while the others need to get just \SEEN. Build sets for each
01826    * of those and sort them, so the server can handle them efficiently. */
01827   QMap< QString, QStringList > groups;
01828   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) {
01829     KMMessage *msg = 0;
01830     bool unget = !isMessage(*it);
01831     msg = getMsg(*it);
01832     if (!msg) continue;
01833     QString flags = statusToFlags(msg->status());
01834     // Collect uids for each type of flags.
01835     groups[flags].append(QString::number(msg->UID()));
01836     if (unget) unGetMsg(*it);
01837   }
01838   QMapIterator< QString, QStringList > dit;
01839   for ( dit = groups.begin(); dit != groups.end(); ++dit ) {
01840      QCString flags = dit.key().latin1();
01841      QStringList sets = makeSets( (*dit), true );
01842      // Send off a status setting job for each set.
01843      for (  QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
01844        QString imappath = imapPath() + ";UID=" + ( *slit );
01845        mAccount->setImapStatus(folder(), imappath, flags);
01846      }
01847   }
01848   if ( mContentState == imapListingInProgress ) {
01849     // we're currently get'ing this folder
01850     // to make sure that we get the latest flags abort the current listing and
01851     // create a new one
01852     kdDebug(5006) << "Set status during folder listing, restarting listing." << endl;
01853     disconnect(this, SLOT(slotListFolderResult(KIO::Job *)));
01854     quiet( false );
01855     reallyGetFolder( QString::null );
01856   }
01857 }
01858 
01859 //-----------------------------------------------------------------------------
01860 QStringList KMFolderImap::makeSets(const QStringList& uids, bool sort)
01861 {
01862   QValueList<ulong> tmp;
01863   for ( QStringList::ConstIterator it = uids.begin(); it != uids.end(); ++it )
01864     tmp.append( (*it).toInt() );
01865   return makeSets(tmp, sort);
01866 }
01867 
01868 QStringList KMFolderImap::makeSets( QValueList<ulong>& uids, bool sort )
01869 {
01870   QStringList sets;
01871   QString set;
01872 
01873   if (uids.size() == 1)
01874   {
01875     sets.append(QString::number(uids.first()));
01876     return sets;
01877   }
01878 
01879   if (sort) qHeapSort(uids);
01880 
01881   ulong last = 0;
01882   // needed to make a uid like 124 instead of 124:124
01883   bool inserted = false;
01884   /* iterate over uids and build sets like 120:122,124,126:150 */
01885   for ( QValueList<ulong>::Iterator it = uids.begin(); it != uids.end(); ++it )
01886   {
01887     if (it == uids.begin() || set.isEmpty()) {
01888       set = QString::number(*it);
01889       inserted = true;
01890     } else
01891     {
01892       if (last+1 != *it)
01893       {
01894         // end this range
01895         if (inserted)
01896           set += ',' + QString::number(*it);
01897         else
01898           set += ':' + QString::number(last) + ',' + QString::number(*it);
01899         inserted = true;
01900         if (set.length() > 100)
01901         {
01902           // just in case the server has a problem with longer lines..
01903           sets.append(set);
01904           set = "";
01905         }
01906       } else {
01907         inserted = false;
01908       }
01909     }
01910     last = *it;
01911   }
01912   // last element
01913   if (!inserted)
01914     set += ':' + QString::number(uids.last());
01915 
01916   if (!set.isEmpty()) sets.append(set);
01917 
01918   return sets;
01919 }
01920 
01921 //-----------------------------------------------------------------------------
01922 void KMFolderImap::getUids(QValueList<int>& ids, QValueList<ulong>& uids)
01923 {
01924   KMMsgBase *msg = 0;
01925   // get the uids
01926   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
01927   {
01928     msg = getMsgBase(*it);
01929     if (!msg) continue;
01930     uids.append(msg->UID());
01931   }
01932 }
01933 
01934 void KMFolderImap::getUids(const QPtrList<KMMessage>& msgList, QValueList<ulong>& uids)
01935 {
01936   KMMessage *msg = 0;
01937 
01938   QPtrListIterator<KMMessage> it( msgList );
01939   while ( (msg = it.current()) != 0 ) {
01940     ++it;
01941     if ( msg->UID() > 0 ) {
01942       uids.append( msg->UID() );
01943     }
01944   }
01945 }
01946 
01947 //-----------------------------------------------------------------------------
01948 void KMFolderImap::expungeFolder(KMFolderImap * aFolder, bool quiet)
01949 {
01950   aFolder->setNeedsCompacting(FALSE);
01951   KURL url = mAccount->getUrl();
01952   url.setPath(aFolder->imapPath() + ";UID=*");
01953   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01954     return;
01955   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01956   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01957   ImapAccountBase::jobData jd( url.url(), 0 );
01958   jd.quiet = quiet;
01959   mAccount->insertJob(job, jd);
01960   connect(job, SIGNAL(result(KIO::Job *)),
01961           mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01962 }
01963 
01964 //-----------------------------------------------------------------------------
01965 void KMFolderImap::slotProcessNewMail( int errorCode, const QString &errorMsg )
01966 {
01967   Q_UNUSED( errorMsg );
01968   disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01969               this, SLOT( slotProcessNewMail(int, const QString&) ) );
01970   if ( !errorCode )
01971     processNewMail( false );
01972   else
01973     emit numUnreadMsgsChanged( folder() );
01974 }
01975 
01976 //-----------------------------------------------------------------------------
01977 bool KMFolderImap::processNewMail(bool)
01978 {
01979    // a little safety
01980   if ( !mAccount ) {
01981     kdDebug(5006) << "KMFolderImap::processNewMail - account is null!" << endl;
01982     return false;
01983   }
01984   if ( imapPath().isEmpty() ) {
01985     kdDebug(5006) << "KMFolderImap::processNewMail - imapPath of " << name() << " is empty!" << endl;
01986     // remove it locally
01987     setAlreadyRemoved( true );
01988     kmkernel->imapFolderMgr()->remove( folder() );
01989     return false;
01990   }
01991   // check the connection
01992   if ( mAccount->makeConnection() == ImapAccountBase::Error ) {
01993     kdDebug(5006) << "KMFolderImap::processNewMail - got no connection!" << endl;
01994     return false;
01995   } else if ( mAccount->makeConnection() == ImapAccountBase::Connecting )
01996   {
01997     // wait
01998     kdDebug(5006) << "KMFolderImap::processNewMail - waiting for connection: " << label() << endl;
01999     connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
02000         this, SLOT( slotProcessNewMail(int, const QString&) ) );
02001     return true;
02002   }
02003   KURL url = mAccount->getUrl();
02004   if (mReadOnly)
02005     url.setPath(imapPath() + ";SECTION=UIDNEXT");
02006   else
02007     url.setPath(imapPath() + ";SECTION=UNSEEN");
02008 
02009   mMailCheckProgressItem = ProgressManager::createProgressItem(
02010               "MailCheckAccount" + account()->name(),
02011               "MailCheck" + folder()->prettyURL(),
02012               QStyleSheet::escape( folder()->prettyURL() ),
02013               i18n("updating message counts"),
02014               false,
02015               account()->useSSL() || account()->useTLS() );
02016 
02017   KIO::SimpleJob *job = KIO::stat(url, FALSE);
02018   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
02019   ImapAccountBase::jobData jd(url.url(), folder() );
02020   jd.cancellable = true;
02021   mAccount->insertJob(job, jd);
02022   connect(job, SIGNAL(result(KIO::Job *)),
02023           SLOT(slotStatResult(KIO::Job *)));
02024   return true;
02025 }
02026 
02027 
02028 //-----------------------------------------------------------------------------
02029 void KMFolderImap::slotStatResult(KIO::Job * job)
02030 {
02031   slotCompleteMailCheckProgress();
02032   ImapAccountBase::JobIterator it = mAccount->findJob(job);
02033   if ( it == mAccount->jobsEnd() ) return;
02034   mAccount->removeJob(it);
02035   if (job->error())
02036   {
02037     mAccount->handleJobError( job, i18n("Error while getting folder information.") );
02038   } else {
02039     KIO::UDSEntry uds = static_cast<KIO::StatJob*>(job)->statResult();
02040     for (KIO::UDSEntry::ConstIterator it = uds.begin(); it != uds.end(); it++)
02041     {
02042       if ((*it).m_uds == KIO::UDS_SIZE)
02043       {
02044         if (mReadOnly)
02045         {
02046           mGuessedUnreadMsgs = -1;
02047           mGuessedUnreadMsgs = countUnread() + (*it).m_long - lastUid() - 1;
02048           if (mGuessedUnreadMsgs < 0) mGuessedUnreadMsgs = 0;
02049         } else {
02050           mGuessedUnreadMsgs = (*it).m_long;
02051         }
02052       }
02053     }
02054   }
02055 }
02056 
02057 //-----------------------------------------------------------------------------
02058 int KMFolderImap::create()
02059 {
02060   readConfig();
02061   mUnreadMsgs = -1;
02062   return KMFolderMbox::create();
02063 }
02064 
02065 QValueList<ulong> KMFolderImap::splitSets(const QString uids)
02066 {
02067   QValueList<ulong> uidlist;
02068 
02069   // ex: 1205,1204,1203,1202,1236:1238
02070   QString buffer = QString::null;
02071   int setstart = -1;
02072   // iterate over the uids
02073   for (uint i = 0; i < uids.length(); i++)
02074   {
02075     QChar chr = uids[i];
02076     if (chr == ',')
02077     {
02078       if (setstart > -1)
02079       {
02080         // a range (uid:uid) was before
02081         for (int j = setstart; j <= buffer.toInt(); j++)
02082         {
02083           uidlist.append(j);
02084         }
02085         setstart = -1;
02086       } else {
02087         // single uid
02088         uidlist.append(buffer.toInt());
02089       }
02090       buffer = "";
02091     } else if (chr == ':') {
02092       // remember the start of the range
02093       setstart = buffer.toInt();
02094       buffer = "";
02095     } else if (chr.category() == QChar::Number_DecimalDigit) {
02096       // digit
02097       buffer += chr;
02098     } else {
02099       // ignore
02100     }
02101   }
02102   // process the last data
02103   if (setstart > -1)
02104   {
02105     for (int j = setstart; j <= buffer.toInt(); j++)
02106     {
02107       uidlist.append(j);
02108     }
02109   } else {
02110     uidlist.append(buffer.toInt());
02111   }
02112 
02113   return uidlist;
02114 }
02115 
02116 //-----------------------------------------------------------------------------
02117 int KMFolderImap::expungeContents()
02118 {
02119   // nuke the local cache
02120   int rc = KMFolderMbox::expungeContents();
02121 
02122   // set the deleted flag for all messages in the folder
02123   KURL url = mAccount->getUrl();
02124   url.setPath( imapPath() + ";UID=1:*");
02125   if ( mAccount->makeConnection() == ImapAccountBase::Connected )
02126   {
02127     KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
02128     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
02129     ImapAccountBase::jobData jd( url.url(), 0 );
02130     jd.quiet = true;
02131     mAccount->insertJob(job, jd);
02132     connect(job, SIGNAL(result(KIO::Job *)),
02133             mAccount, SLOT(slotSimpleResult(KIO::Job *)));
02134   }
02135   /* Is the below correct? If we are expunging (in the folder sense, not the imap sense),
02136      why delete but not (imap-)expunge? Since the folder is not active there is no concept
02137      of "leaving the folder", so the setting really has little to do with it. */
02138   // if ( autoExpunge() )
02139     expungeFolder(this, true);
02140   getFolder();
02141 
02142   return rc;
02143 }
02144 
02145 //-----------------------------------------------------------------------------
02146 void
02147 KMFolderImap::setUserRights( unsigned int userRights )
02148 {
02149   mUserRights = userRights;
02150   kdDebug(5006) << imapPath() << " setUserRights: " << userRights << endl;
02151 }
02152 
02153 //-----------------------------------------------------------------------------
02154 void KMFolderImap::slotCompleteMailCheckProgress()
02155 {
02156   if ( mMailCheckProgressItem ) {
02157     mMailCheckProgressItem->setComplete();
02158     mMailCheckProgressItem = 0;
02159     emit numUnreadMsgsChanged( folder() );
02160   }
02161 }
02162 
02163 //-----------------------------------------------------------------------------
02164 void KMFolderImap::setSubfolderState( imapState state )
02165 {
02166   mSubfolderState = state;
02167   if ( state == imapNoInformation && folder()->child() )
02168   {
02169     // pass through to children
02170     KMFolderNode* node;
02171     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02172     for ( ; (node = it.current()); )
02173     {
02174       ++it;
02175       if (node->isDir()) continue;
02176       KMFolder *folder = static_cast<KMFolder*>(node);
02177       static_cast<KMFolderImap*>(folder->storage())->setSubfolderState( state );
02178     }
02179   }
02180 }
02181 
02182 //-----------------------------------------------------------------------------
02183 void KMFolderImap::setIncludeInMailCheck( bool check )
02184 {
02185   bool changed = ( mCheckMail != check );
02186   mCheckMail = check;
02187   if ( changed )
02188     account()->slotUpdateFolderList();
02189 }
02190 
02191 //-----------------------------------------------------------------------------
02192 void KMFolderImap::setAlreadyRemoved( bool removed )
02193 {
02194   mAlreadyRemoved = removed;
02195   if ( folder()->child() )
02196   {
02197     // pass through to childs
02198     KMFolderNode* node;
02199     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02200     for ( ; (node = it.current()); )
02201     {
02202       ++it;
02203       if (node->isDir()) continue;
02204       KMFolder *folder = static_cast<KMFolder*>(node);
02205       static_cast<KMFolderImap*>(folder->storage())->setAlreadyRemoved( removed );
02206     }
02207   }
02208 }
02209 
02210 void KMFolderImap::slotCreatePendingFolders( int errorCode, const QString& errorMsg )
02211 {
02212   Q_UNUSED( errorMsg );
02213   disconnect( mAccount, SIGNAL( connectionResult( int, const QString& ) ),
02214               this, SLOT( slotCreatePendingFolders( int, const QString& ) ) );
02215   if ( !errorCode ) {
02216     QStringList::Iterator it = mFoldersPendingCreation.begin();
02217     for ( ; it != mFoldersPendingCreation.end(); ++it ) {
02218       createFolder( *it );
02219     }
02220   }
02221   mFoldersPendingCreation.clear();
02222 }
02223 
02224 //-----------------------------------------------------------------------------
02225 void KMFolderImap::search( const KMSearchPattern* pattern )
02226 {
02227   if ( !pattern || pattern->isEmpty() )
02228   {
02229     // not much to do here
02230     QValueList<Q_UINT32> serNums;
02231     emit searchResult( folder(), serNums, pattern, true );
02232     return;
02233   }
02234   SearchJob* job = new SearchJob( this, mAccount, pattern );
02235   connect( job, SIGNAL( searchDone( QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ),
02236            this, SLOT( slotSearchDone( QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ) );
02237   job->start();
02238 }
02239 
02240 //-----------------------------------------------------------------------------
02241 void KMFolderImap::slotSearchDone( QValueList<Q_UINT32> serNums,
02242                                    const KMSearchPattern* pattern,
02243                                    bool complete )
02244 {
02245   emit searchResult( folder(), serNums, pattern, complete );
02246 }
02247 
02248 //-----------------------------------------------------------------------------
02249 void KMFolderImap::search( const KMSearchPattern* pattern, Q_UINT32 serNum )
02250 {
02251   if ( !pattern || pattern->isEmpty() )
02252   {
02253     // not much to do here
02254     emit searchDone( folder(), serNum, pattern, false );
02255     return;
02256   }
02257   SearchJob* job = new SearchJob( this, mAccount, pattern, serNum );
02258   connect( job, SIGNAL( searchDone( Q_UINT32, const KMSearchPattern*, bool ) ),
02259            this, SLOT( slotSearchDone( Q_UINT32, const KMSearchPattern*, bool ) ) );
02260   job->start();
02261 }
02262 
02263 //-----------------------------------------------------------------------------
02264 void KMFolderImap::slotSearchDone( Q_UINT32 serNum, const KMSearchPattern* pattern,
02265                                    bool matches )
02266 {
02267   emit searchDone( folder(), serNum, pattern, matches );
02268 }
02269 
02270 //-----------------------------------------------------------------------------
02271 bool KMFolderImap::isMoveable() const
02272 {
02273   return ( hasChildren() == HasNoChildren &&
02274       !folder()->isSystemFolder() ) ? true : false;
02275 }
02276 
02277 //-----------------------------------------------------------------------------
02278 const ulong KMFolderImap::serNumForUID( ulong uid )
02279 {
02280   if ( mUidMetaDataMap.find( uid ) ) {
02281     KMMsgMetaData *md = mUidMetaDataMap[uid];
02282     return md->serNum();
02283   } else {
02284     kdDebug(5006) << "serNumForUID: unknown uid " << uid << endl;
02285     return 0;
02286   }
02287 }
02288 
02289 //-----------------------------------------------------------------------------
02290 void KMFolderImap::saveMsgMetaData( KMMessage* msg, ulong uid )
02291 {
02292   if ( uid == 0 ) {
02293     uid = msg->UID();
02294   }
02295   ulong serNum = msg->getMsgSerNum();
02296   mUidMetaDataMap.replace( uid, new KMMsgMetaData(msg->status(), serNum) );
02297 }
02298 
02299 //-----------------------------------------------------------------------------
02300 void KMFolderImap::setImapPath( const QString& path )
02301 {
02302   if ( path.isEmpty() ) {
02303     kdWarning(5006) << k_funcinfo << "ignoring empty path" << endl;
02304   } else {
02305     mImapPath = path;
02306   }
02307 }
02308 
02309 void KMFolderImap::finishMailCheck( imapState state )
02310 {
02311   quiet( false );
02312   mContentState = state;
02313   emit folderComplete( this, mContentState == imapFinished );
02314   close();
02315 }
02316 
02317 #include "kmfolderimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys