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   if (!folder()->child())
00923     return;
00924   KMFolderNode *node = folder()->child()->first();
00925   while ( node )
00926   {
00927     if ( !node->isDir() && subfolderNames.findIndex(node->name()) == -1 )
00928     {
00929       KMFolder* fld = static_cast<KMFolder*>(node);
00930       KMFolderImap* imapFld = static_cast<KMFolderImap*>( fld->storage() );
00931       // as more than one namespace can be listed in the root folder we need to make sure
00932       // that the folder is within the current namespace
00933       bool isInNamespace = ( myNamespace.isEmpty() ||
00934           myNamespace == mAccount->namespaceForFolder( imapFld ) );
00935       kdDebug(5006) << node->name() << " in namespace " << myNamespace << ":" <<
00936         isInNamespace << endl;
00937       // ignore some cases
00938       QString name = node->name();
00939       bool ignore = ( ( this == mAccount->rootFolder() ) &&
00940           ( imapFld->imapPath() == "/INBOX/" ||
00941             mAccount->isNamespaceFolder( name ) ||
00942         !isInNamespace ) );
00943       // additional sanity check for broken folders
00944       if ( imapFld->imapPath().isEmpty() ) {
00945         ignore = false;
00946       }
00947       if ( !ignore )
00948       {
00949         // remove the folder without server round trip
00950         kdDebug(5006) << "checkFolders - " << node->name() << " disappeared" << endl;
00951         imapFld->setAlreadyRemoved( true );
00952         toRemove.append( fld );
00953       } else {
00954         kdDebug(5006) << "checkFolders - " << node->name() << " ignored" << endl;
00955       }
00956     }
00957     node = folder()->child()->next();
00958   }
00959   // remove folders
00960   for ( KMFolder* doomed=toRemove.first(); doomed; doomed = toRemove.next() )
00961     kmkernel->imapFolderMgr()->remove( doomed );
00962 }
00963 
00964 //-----------------------------------------------------------------------------
00965 void KMFolderImap::initializeFrom( KMFolderImap* parent, QString folderPath,
00966                                    QString mimeType )
00967 {
00968   setAccount( parent->account() );
00969   setImapPath( folderPath );
00970   setNoContent( mimeType == "inode/directory" );
00971   setNoChildren( mimeType == "message/digest" );
00972 }
00973 
00974 //-----------------------------------------------------------------------------
00975 void KMFolderImap::setChildrenState( QString attributes )
00976 {
00977   // update children state
00978   if ( attributes.find( "haschildren", 0, false ) != -1 )
00979   {
00980     setHasChildren( FolderStorage::HasChildren );
00981   } else if ( attributes.find( "hasnochildren", 0, false ) != -1 ||
00982               attributes.find( "noinferiors", 0, false ) != -1 )
00983   {
00984     setHasChildren( FolderStorage::HasNoChildren );
00985   } else
00986   {
00987     if ( mAccount->listOnlyOpenFolders() ) {
00988       setHasChildren( FolderStorage::HasChildren );
00989     } else {
00990       setHasChildren( FolderStorage::ChildrenUnknown );
00991     }
00992   }
00993 }
00994 
00995 //-----------------------------------------------------------------------------
00996 void KMFolderImap::checkValidity()
00997 {
00998   if (!mAccount) {
00999     emit folderComplete(this, false);
01000     close();
01001     return;
01002   }
01003   KURL url = mAccount->getUrl();
01004   url.setPath(imapPath() + ";UID=0:0");
01005   kdDebug(5006) << "KMFolderImap::checkValidity of: " << imapPath() << endl;
01006 
01007   // Start with a clean slate
01008   disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01009               this, SLOT( checkValidity() ) );
01010 
01011   KMAcctImap::ConnectionState connectionState = mAccount->makeConnection();
01012   if ( connectionState == ImapAccountBase::Error ) {
01013     kdDebug(5006) << "KMFolderImap::checkValidity - got no connection" << endl;
01014     emit folderComplete(this, FALSE);
01015     mContentState = imapNoInformation;
01016     close();
01017     return;
01018   } else if ( connectionState == ImapAccountBase::Connecting ) {
01019     // We'll wait for the connectionResult signal from the account. If it
01020     // errors, the above will catch it.
01021     kdDebug(5006) << "CheckValidity - waiting for connection" << endl;
01022     connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01023         this, SLOT( checkValidity() ) );
01024     return;
01025   }
01026   // Only check once at a time.
01027   if (mCheckingValidity) {
01028     kdDebug(5006) << "KMFolderImap::checkValidity - already checking" << endl;
01029     close();
01030     return;
01031   }
01032   // otherwise we already are inside a mailcheck
01033   if ( !mMailCheckProgressItem ) {
01034     ProgressItem* parent = ( account()->checkingSingleFolder() ? 0 :
01035         account()->mailCheckProgressItem() );
01036     mMailCheckProgressItem = ProgressManager::createProgressItem(
01037               parent,
01038               "MailCheck" + folder()->prettyURL(),
01039               QStyleSheet::escape( folder()->prettyURL() ),
01040               i18n("checking"),
01041               false,
01042               account()->useSSL() || account()->useTLS() );
01043   } else {
01044     mMailCheckProgressItem->setProgress(0);
01045   }
01046   if ( account()->mailCheckProgressItem() ) {
01047     account()->mailCheckProgressItem()->setStatus( folder()->prettyURL() );
01048   }
01049   ImapAccountBase::jobData jd( url.url() );
01050   KIO::SimpleJob *job = KIO::get(url, FALSE, FALSE);
01051   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01052   mAccount->insertJob(job, jd);
01053   connect(job, SIGNAL(result(KIO::Job *)),
01054           SLOT(slotCheckValidityResult(KIO::Job *)));
01055   connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
01056           SLOT(slotSimpleData(KIO::Job *, const QByteArray &)));
01057   // Only check once at a time.
01058   mCheckingValidity = true;
01059 }
01060 
01061 
01062 //-----------------------------------------------------------------------------
01063 ulong KMFolderImap::lastUid()
01064 {
01065   if ( mLastUid > 0 )
01066       return mLastUid;
01067   open();
01068   if (count() > 0)
01069   {
01070     KMMsgBase * base = getMsgBase(count()-1);
01071     mLastUid = base->UID();
01072   }
01073   close();
01074   return mLastUid;
01075 }
01076 
01077 
01078 //-----------------------------------------------------------------------------
01079 void KMFolderImap::slotCheckValidityResult(KIO::Job * job)
01080 {
01081   kdDebug(5006) << "KMFolderImap::slotCheckValidityResult of: " << fileName() << endl;
01082   mCheckingValidity = false;
01083   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01084   if ( it == mAccount->jobsEnd() ) return;
01085   if (job->error()) {
01086     if ( job->error() != KIO::ERR_ACCESS_DENIED ) {
01087       // we suppress access denied messages because they are normally a result of
01088       // explicitely set ACLs. Do not save this information (e.g. setNoContent) so that
01089       // we notice when this changes
01090       mAccount->handleJobError( job, i18n("Error while querying the server status.") );
01091     }
01092     mContentState = imapNoInformation;
01093     emit folderComplete(this, FALSE);
01094     close();
01095   } else {
01096     QCString cstr((*it).data.data(), (*it).data.size() + 1);
01097     int a = cstr.find("X-uidValidity: ");
01098     int b = cstr.find("\r\n", a);
01099     QString uidv;
01100     if ( (b - a - 15) >= 0 )
01101         uidv = cstr.mid(a + 15, b - a - 15);
01102     a = cstr.find("X-Access: ");
01103     b = cstr.find("\r\n", a);
01104     QString access;
01105     if ( (b - a - 10) >= 0 )
01106         access = cstr.mid(a + 10, b - a - 10);
01107     mReadOnly = access == "Read only";
01108     a = cstr.find("X-Count: ");
01109     b = cstr.find("\r\n", a);
01110     int exists = -1;
01111     bool ok = false;
01112     if ( (b - a - 9) >= 0 )
01113         exists = cstr.mid(a + 9, b - a - 9).toInt(&ok);
01114     if ( !ok ) exists = -1;
01115     QString startUid;
01116     if (uidValidity() != uidv)
01117     {
01118       // uidValidity changed
01119       kdDebug(5006) << k_funcinfo << "uidValidty changed from "
01120        << uidValidity() << " to " << uidv << endl;
01121       if ( !uidValidity().isEmpty() )
01122       {
01123         mAccount->ignoreJobsForFolder( folder() );
01124         mUidMetaDataMap.clear();
01125       }
01126       mLastUid = 0;
01127       setUidValidity(uidv);
01128       writeConfig();
01129     } else {
01130       if (!mCheckFlags)
01131         startUid = QString::number(lastUid() + 1);
01132     }
01133     mAccount->removeJob(it);
01134     if ( mMailCheckProgressItem )
01135     {
01136       if ( startUid.isEmpty() ) {
01137         // flags for all messages are loaded
01138         mMailCheckProgressItem->setTotalItems( exists );
01139       } else {
01140         // only an approximation but doesn't hurt
01141         int remain = exists - count();
01142         if ( remain < 0 ) remain = 1;
01143         mMailCheckProgressItem->setTotalItems( remain );
01144       }
01145       mMailCheckProgressItem->setCompletedItems( 0 );
01146     }
01147     reallyGetFolder(startUid);
01148   }
01149 }
01150 
01151 //-----------------------------------------------------------------------------
01152 void KMFolderImap::getAndCheckFolder(bool force)
01153 {
01154   if (mNoContent)
01155     return getFolder(force);
01156 
01157   if ( mAccount )
01158     mAccount->processNewMailSingleFolder( folder() );
01159   if (force) {
01160     // force an update
01161     mCheckFlags = TRUE;
01162   }
01163 }
01164 
01165 //-----------------------------------------------------------------------------
01166 void KMFolderImap::getFolder(bool force)
01167 {
01168   mGuessedUnreadMsgs = -1;
01169   if (mNoContent)
01170   {
01171     mContentState = imapFinished;
01172     emit folderComplete(this, true);
01173     return;
01174   }
01175   open();
01176   mContentState = imapListingInProgress;
01177   if (force) {
01178     // force an update
01179     mCheckFlags = TRUE;
01180   }
01181   checkValidity();
01182 }
01183 
01184 
01185 //-----------------------------------------------------------------------------
01186 void KMFolderImap::reallyGetFolder(const QString &startUid)
01187 {
01188   KURL url = mAccount->getUrl();
01189   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01190   {
01191     mContentState = imapNoInformation;
01192     emit folderComplete(this, FALSE);
01193     close();
01194     return;
01195   }
01196   quiet(true);
01197   if (startUid.isEmpty())
01198   {
01199     if ( mMailCheckProgressItem )
01200       mMailCheckProgressItem->setStatus( i18n("Retrieving message status") );
01201     url.setPath(imapPath() + ";SECTION=UID FLAGS");
01202     KIO::SimpleJob *job = KIO::listDir(url, FALSE);
01203     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01204     ImapAccountBase::jobData jd( url.url(), folder() );
01205     jd.cancellable = true;
01206     mAccount->insertJob(job, jd);
01207     connect(job, SIGNAL(result(KIO::Job *)),
01208             this, SLOT(slotListFolderResult(KIO::Job *)));
01209     connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
01210             this, SLOT(slotListFolderEntries(KIO::Job *,
01211             const KIO::UDSEntryList &)));
01212   } else {
01213     mContentState = imapDownloadInProgress;
01214     if ( mMailCheckProgressItem )
01215       mMailCheckProgressItem->setStatus( i18n("Retrieving messages") );
01216     url.setPath(imapPath() + ";UID=" + startUid
01217       + ":*;SECTION=ENVELOPE");
01218     KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
01219     KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
01220     ImapAccountBase::jobData jd( url.url(), folder() );
01221     jd.cancellable = true;
01222     mAccount->insertJob(newJob, jd);
01223     connect(newJob, SIGNAL(result(KIO::Job *)),
01224             this, SLOT(slotGetLastMessagesResult(KIO::Job *)));
01225     connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
01226             this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
01227   }
01228 }
01229 
01230 
01231 //-----------------------------------------------------------------------------
01232 void KMFolderImap::slotListFolderResult(KIO::Job * job)
01233 {
01234   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01235   if ( it == mAccount->jobsEnd() ) return;
01236   QString uids;
01237   if (job->error())
01238   {
01239     mAccount->handleJobError( job,
01240          i18n("Error while listing the contents of the folder %1.").arg( label() ) );
01241     mAccount->removeJob(it);
01242     finishMailCheck( imapNoInformation );
01243     return;
01244   }
01245   mCheckFlags = FALSE;
01246   QStringList::Iterator uid;
01247   /*
01248     The code below does the following:
01249     - for each mail in the local store and each entry we got from the server,
01250       compare the local uid with the one from the server and update the status
01251       flags of the mails
01252     - for all mails that are not already locally present, start a job which
01253       gets the envelope of each
01254     - remove all locally present mails if the server does not list them anymore
01255   */
01256   if ( count() ) {
01257     int idx = 0, c, serverFlags;
01258     ulong mailUid, serverUid;
01259     uid = (*it).items.begin();
01260     while ( idx < count() && uid != (*it).items.end() ) {
01261       KMMsgBase *msgBase = getMsgBase( idx );
01262       mailUid = msgBase->UID();
01263       // parse the uid from the server and the flags out of the list from
01264       // the server. Format: 1234, 1
01265       c = (*uid).find(",");
01266       serverUid = (*uid).left( c ).toLong();
01267       serverFlags = (*uid).mid( c+1 ).toInt();
01268       if ( mailUid < serverUid ) {
01269         removeMsg( idx, TRUE );
01270       } else if ( mailUid == serverUid ) {
01271         // if this is a read only folder, ignore status updates from the server
01272         // since we can't write our status back our local version is what has to
01273         // be considered correct.
01274         if (!mReadOnly)
01275           flagsToStatus( msgBase, serverFlags, false );
01276         idx++;
01277         uid = (*it).items.remove(uid);
01278         if ( msgBase->getMsgSerNum() > 0 ) {
01279           saveMsgMetaData( static_cast<KMMessage*>(msgBase) );
01280         }
01281       }
01282       else break;  // happens only, if deleted mails reappear on the server
01283     }
01284     // remove all remaining entries in the local cache, they are no longer
01285     // present on the server
01286     while (idx < count()) removeMsg(idx, TRUE);
01287   }
01288   // strip the flags from the list of uids, so it can be reused
01289   for (uid = (*it).items.begin(); uid != (*it).items.end(); ++uid)
01290     (*uid).truncate((*uid).find(","));
01291   ImapAccountBase::jobData jd( QString::null, (*it).parent );
01292   jd.total = (*it).items.count();
01293   if (jd.total == 0)
01294   {
01295     finishMailCheck( imapFinished );
01296     mAccount->removeJob(it);
01297     return;
01298   }
01299   if ( mMailCheckProgressItem )
01300   {
01301     // next step for the progressitem
01302     mMailCheckProgressItem->setCompletedItems( 0 );
01303     mMailCheckProgressItem->setTotalItems( jd.total );
01304     mMailCheckProgressItem->setProgress( 0 );
01305     mMailCheckProgressItem->setStatus( i18n("Retrieving messages") );
01306   }
01307 
01308   QStringList sets;
01309   uid = (*it).items.begin();
01310   if (jd.total == 1) sets.append(*uid + ":" + *uid);
01311   else sets = makeSets( (*it).items );
01312   mAccount->removeJob(it); // don't use *it below
01313 
01314   // Now kick off the getting of envelopes for the new mails in the folder
01315   for (QStringList::Iterator i = sets.begin(); i != sets.end(); ++i)
01316   {
01317     mContentState = imapDownloadInProgress;
01318     KURL url = mAccount->getUrl();
01319     url.setPath(imapPath() + ";UID=" + *i + ";SECTION=ENVELOPE");
01320     KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE);
01321     jd.url = url.url();
01322     KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob);
01323     mAccount->insertJob(newJob, jd);
01324     connect(newJob, SIGNAL(result(KIO::Job *)),
01325         this, (i == sets.at(sets.count() - 1))
01326         ? SLOT(slotGetLastMessagesResult(KIO::Job *))
01327         : SLOT(slotGetMessagesResult(KIO::Job *)));
01328     connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
01329         this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
01330   }
01331 }
01332 
01333 
01334 //-----------------------------------------------------------------------------
01335 void KMFolderImap::slotListFolderEntries(KIO::Job * job,
01336   const KIO::UDSEntryList & uds)
01337 {
01338   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01339   if ( it == mAccount->jobsEnd() ) return;
01340   QString mimeType, name;
01341   long int flags = 0;
01342   for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
01343     udsIt != uds.end(); udsIt++)
01344   {
01345     for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
01346       eIt != (*udsIt).end(); eIt++)
01347     {
01348       if ((*eIt).m_uds == KIO::UDS_NAME)
01349         name = (*eIt).m_str;
01350       else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
01351         mimeType = (*eIt).m_str;
01352       else if ((*eIt).m_uds == KIO::UDS_ACCESS)
01353         flags = (*eIt).m_long;
01354     }
01355     if ((mimeType == "message/rfc822-imap" || mimeType == "message/rfc822") &&
01356         !(flags & 8)) {
01357       (*it).items.append(name + "," + QString::number(flags));
01358       if ( mMailCheckProgressItem ) {
01359         mMailCheckProgressItem->incCompletedItems();
01360         mMailCheckProgressItem->updateProgress();
01361       }
01362     }
01363   }
01364 }
01365 
01366 
01367 // debugging helper
01368 //X static QString flagsToString( int flags )
01369 //X {
01370 //X     QString str("(");
01371 //X     if ( flags & 4 ) {
01372 //X         str += "\\Flagged ";
01373 //X     }
01374 //X     if ( flags & 2 ) {
01375 //X         str += "\\Answered ";
01376 //X     }
01377 //X     if ( flags & 1 ) {
01378 //X         str += "\\Seen";
01379 //X     }
01380 //X     str += ")";
01381 //X     return str;
01382 //X }
01383 
01384 //-----------------------------------------------------------------------------
01385 void KMFolderImap::flagsToStatus(KMMsgBase *msg, int flags, bool newMsg)
01386 {
01387   if ( !msg ) return;
01388 
01389   const KMMsgStatus oldStatus = msg->status();
01390   // Set flags if they are new
01391   if ( (flags & 4) && (oldStatus & KMMsgStatusFlag) == 0 )
01392     msg->setStatus( KMMsgStatusFlag );
01393   if ( (flags & 2) && (oldStatus & KMMsgStatusReplied) == 0 )
01394     msg->setStatus( KMMsgStatusReplied );
01395   if ( (flags & 1) && (oldStatus & KMMsgStatusOld) == 0 )
01396     msg->setStatus( KMMsgStatusOld );
01397 
01398   // Toggle flags if they changed
01399 //  if ( ( (flags & 4) > 0 ) != ( (oldStatus & KMMsgStatusFlag) > 0 ) )
01400 //    msg->toggleStatus( KMMsgStatusFlag );
01401 //  if ( ( (flags & 2) > 0 ) != ( (oldStatus & KMMsgStatusReplied) > 0 ) )
01402 //    msg->toggleStatus( KMMsgStatusReplied );
01403 //  if ( ( (flags & 1) > 0 ) != ( (oldStatus & KMMsgStatusOld) > 0 ) )
01404 //    msg->toggleStatus( KMMsgStatusOld );
01405 
01406   // In case the message does not have the seen flag set, override our local
01407   // notion that it is read. Otherwise the count of unread messages and the
01408   // number of messages which actually show up as read can go out of sync.
01409   if (msg->isOfUnknownStatus() || !(flags&1) ) {
01410     if (newMsg) {
01411       if ( (oldStatus & KMMsgStatusNew) == 0 )
01412         msg->setStatus( KMMsgStatusNew );
01413     } else {
01414       if ( (oldStatus & KMMsgStatusUnread) == 0 )
01415         msg->setStatus( KMMsgStatusUnread );
01416     }
01417   }
01418 }
01419 
01420 
01421 //-----------------------------------------------------------------------------
01422 QString KMFolderImap::statusToFlags(KMMsgStatus status)
01423 {
01424   QString flags;
01425   if (status & KMMsgStatusDeleted)
01426     flags = "\\DELETED";
01427   else {
01428     if (status & KMMsgStatusOld || status & KMMsgStatusRead)
01429       flags = "\\SEEN ";
01430     if (status & KMMsgStatusReplied)
01431       flags += "\\ANSWERED ";
01432     if (status & KMMsgStatusFlag)
01433       flags += "\\FLAGGED";
01434   }
01435 
01436   return flags.simplifyWhiteSpace();
01437 }
01438 
01439 //-------------------------------------------------------------
01440 void
01441 KMFolderImap::ignoreJobsForMessage( KMMessage* msg )
01442 {
01443   if ( !msg || msg->transferInProgress() ||
01444        !msg->parent() || msg->parent()->folderType() != KMFolderTypeImap )
01445     return;
01446   KMAcctImap *account;
01447   if ( !(account = static_cast<KMFolderImap*>(msg->storage())->account()) )
01448     return;
01449 
01450   account->ignoreJobsForMessage( msg );
01451 }
01452 
01453 //-----------------------------------------------------------------------------
01454 void KMFolderImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
01455 {
01456   if ( data.isEmpty() ) return; // optimization
01457   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01458   if ( it == mAccount->jobsEnd() ) return;
01459   (*it).cdata += QCString(data, data.size() + 1);
01460   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
01461   if ( pos == -1 ) {
01462     // if we do not find the pattern in the complete string we will not find
01463     // it in a substring.
01464     return;
01465   }
01466   if (pos > 0)
01467   {
01468     int p = (*it).cdata.find("\r\nX-uidValidity:");
01469     if (p != -1) setUidValidity((*it).cdata
01470       .mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
01471     int c = (*it).cdata.find("\r\nX-Count:");
01472     if ( c != -1 )
01473     {
01474       bool ok;
01475       int exists = (*it).cdata.mid( c+10,
01476           (*it).cdata.find("\r\n", c+1) - c-10 ).toInt(&ok);
01477       if ( ok && exists < count() ) {
01478         kdDebug(5006) << "KMFolderImap::slotGetMessagesData - server has less messages (" <<
01479           exists << ") then folder (" << count() << "), so reload" << endl;
01480         open();
01481         reallyGetFolder( QString::null );
01482         (*it).cdata.remove(0, pos);
01483         return;
01484       } else if ( ok ) {
01485         int delta = exists - count();
01486         if ( mMailCheckProgressItem ) {
01487           mMailCheckProgressItem->setTotalItems( delta );
01488         }
01489       }
01490     }
01491     (*it).cdata.remove(0, pos);
01492   }
01493   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01494   int flags;
01495   while (pos >= 0)
01496   {
01497     KMMessage *msg = new KMMessage;
01498     msg->setComplete( false );
01499     msg->setReadyToShow( false );
01500     // nothing between the boundaries, older UWs do that
01501     if ( pos != 14 ) {
01502       msg->fromString( (*it).cdata.mid(16, pos - 16) );
01503       flags = msg->headerField("X-Flags").toInt();
01504       ulong uid = msg->UID();
01505       KMMsgMetaData *md =  0;
01506       if ( mUidMetaDataMap.find( uid ) ) {
01507           md =  mUidMetaDataMap[uid];
01508       }
01509       ulong serNum = 0;
01510       if ( md ) {
01511         serNum = md->serNum();
01512       }
01513       bool ok = true;
01514       if ( uid <= lastUid() && serNum > 0 ) {
01515         // the UID is already known so no need to create it
01516         ok = false;
01517       }
01518       // deleted flag
01519       if ( flags & 8 )
01520         ok = false;
01521       if ( !ok ) {
01522         delete msg;
01523         msg = 0;
01524       } else {
01525         if ( serNum > 0 ) {
01526           // assign the sernum from the cache
01527           msg->setMsgSerNum( serNum );
01528         }
01529         // Transfer the status, if it is cached.
01530         if ( md ) {
01531           msg->setStatus( md->status() );
01532         } else if ( !mAccount->hasCapability("uidplus") ) {
01533           // see if we have cached the msgIdMD5 and get the status +
01534           // serial number from there
01535           QString id = msg->msgIdMD5();
01536           if ( mMetaDataMap.find( id ) ) {
01537             md =  mMetaDataMap[id];
01538             msg->setStatus( md->status() );
01539             if ( md->serNum() != 0 && serNum == 0 ) {
01540               msg->setMsgSerNum( md->serNum() );
01541             }
01542             mMetaDataMap.remove( id );
01543             delete md;
01544           }
01545         }
01546         KMFolderMbox::addMsg(msg, 0);
01547         // Merge with the flags from the server.
01548         flagsToStatus((KMMsgBase*)msg, flags);
01549         // set the correct size
01550         msg->setMsgSizeServer( msg->headerField("X-Length").toUInt() );
01551         msg->setUID(uid);
01552         if ( msg->getMsgSerNum() > 0 ) {
01553           saveMsgMetaData( msg );
01554         }
01555         // Filter messages that have arrived in the inbox folder
01556         if ( folder()->isSystemFolder() && imapPath() == "/INBOX/"
01557             && kmkernel->filterMgr()->atLeastOneIncomingFilterAppliesTo( mAccount->id() ) )
01558             mAccount->execFilters( msg->getMsgSerNum() );
01559 
01560         if ( count() > 1 ) {
01561           unGetMsg(count() - 1);
01562         }
01563         mLastUid = uid;
01564         if ( mMailCheckProgressItem ) {
01565           mMailCheckProgressItem->incCompletedItems();
01566           mMailCheckProgressItem->updateProgress();
01567         }
01568       }
01569     }
01570     (*it).cdata.remove(0, pos);
01571     (*it).done++;
01572     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01573   } // while
01574 }
01575 
01576 //-------------------------------------------------------------
01577 FolderJob*
01578 KMFolderImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
01579                            KMFolder *folder, QString partSpecifier,
01580                            const AttachmentStrategy *as ) const
01581 {
01582   KMFolderImap* kmfi = folder? dynamic_cast<KMFolderImap*>(folder->storage()) : 0;
01583   if ( jt == FolderJob::tGetMessage && partSpecifier == "STRUCTURE" &&
01584        mAccount && mAccount->loadOnDemand() &&
01585        ( msg->msgSizeServer() > 5000 || msg->msgSizeServer() == 0 ) &&
01586        ( msg->signatureState() == KMMsgNotSigned ||
01587          msg->signatureState() == KMMsgSignatureStateUnknown ) &&
01588        ( msg->encryptionState() == KMMsgNotEncrypted ||
01589          msg->encryptionState() == KMMsgEncryptionStateUnknown ) )
01590   {
01591     // load-on-demand: retrieve the BODYSTRUCTURE and to speed things up also the headers
01592     // this is not activated for small or signed messages
01593     ImapJob *job = new ImapJob( msg, jt, kmfi, "HEADER" );
01594     job->start();
01595     ImapJob *job2 = new ImapJob( msg, jt, kmfi, "STRUCTURE", as );
01596     job2->start();
01597     job->setParentFolder( this );
01598     return job;
01599   } else {
01600     // download complete message or part (attachment)
01601     if ( partSpecifier == "STRUCTURE" ) // hide from outside
01602       partSpecifier = QString::null;
01603 
01604     ImapJob *job = new ImapJob( msg, jt, kmfi, partSpecifier );
01605     job->setParentFolder( this );
01606     return job;
01607   }
01608 }
01609 
01610 //-------------------------------------------------------------
01611 FolderJob*
01612 KMFolderImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
01613                            FolderJob::JobType jt, KMFolder *folder ) const
01614 {
01615   KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder->storage());
01616   ImapJob *job = new ImapJob( msgList, sets, jt, kmfi );
01617   job->setParentFolder( this );
01618   return job;
01619 }
01620 
01621 //-----------------------------------------------------------------------------
01622 void KMFolderImap::getMessagesResult(KIO::Job * job, bool lastSet)
01623 {
01624   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01625   if ( it == mAccount->jobsEnd() ) return;
01626   if (job->error()) {
01627     mAccount->handleJobError( job, i18n("Error while retrieving messages.") );
01628     finishMailCheck( imapNoInformation );
01629     return;
01630   }
01631   if (lastSet) {
01632     finishMailCheck( imapFinished );
01633     mAccount->removeJob(it);
01634   }
01635 }
01636 
01637 
01638 //-----------------------------------------------------------------------------
01639 void KMFolderImap::slotGetLastMessagesResult(KIO::Job * job)
01640 {
01641   getMessagesResult(job, true);
01642 }
01643 
01644 
01645 //-----------------------------------------------------------------------------
01646 void KMFolderImap::slotGetMessagesResult(KIO::Job * job)
01647 {
01648   getMessagesResult(job, false);
01649 }
01650 
01651 
01652 //-----------------------------------------------------------------------------
01653 void KMFolderImap::createFolder(const QString &name, const QString& parentPath,
01654                                 bool askUser)
01655 {
01656   kdDebug(5006) << "KMFolderImap::createFolder - name=" << name << ",parent=" <<
01657     parentPath << ",askUser=" << askUser << endl;
01658   if ( mAccount->makeConnection() != ImapAccountBase::Connected ) {
01659     kdWarning(5006) << "KMFolderImap::createFolder - got no connection" << endl;
01660     return;
01661   }
01662   KURL url = mAccount->getUrl();
01663   QString parent = ( parentPath.isEmpty() ? imapPath() : parentPath );
01664   QString path = mAccount->createImapPath( parent, name );
01665   if ( askUser ) {
01666     path += "/;INFO=ASKUSER";
01667   }
01668   url.setPath( path );
01669 
01670   KIO::SimpleJob *job = KIO::mkdir(url);
01671   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01672   ImapAccountBase::jobData jd( url.url(), folder() );
01673   jd.items = name;
01674   mAccount->insertJob(job, jd);
01675   connect(job, SIGNAL(result(KIO::Job *)),
01676           this, SLOT(slotCreateFolderResult(KIO::Job *)));
01677 }
01678 
01679 
01680 //-----------------------------------------------------------------------------
01681 void KMFolderImap::slotCreateFolderResult(KIO::Job * job)
01682 {
01683   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01684   if ( it == mAccount->jobsEnd() ) return;
01685   if (job->error())
01686   {
01687     if ( job->error() == KIO::ERR_COULD_NOT_MKDIR ) {
01688       // Creating a folder failed, remove it from the tree.
01689       mAccount->listDirectory( );
01690     }
01691     mAccount->handleJobError( job, i18n("Error while creating a folder.") );
01692   } else {
01693     listDirectory();
01694     mAccount->removeJob(job);
01695   }
01696 }
01697 
01698 
01699 //-----------------------------------------------------------------------------
01700 static QTextCodec *sUtf7Codec = 0;
01701 
01702 QTextCodec * KMFolderImap::utf7Codec()
01703 {
01704   if (!sUtf7Codec) sUtf7Codec = QTextCodec::codecForName("utf-7");
01705   return sUtf7Codec;
01706 }
01707 
01708 
01709 //-----------------------------------------------------------------------------
01710 QString KMFolderImap::encodeFileName(const QString &name)
01711 {
01712   QString result = utf7Codec()->fromUnicode(name);
01713   return KURL::encode_string_no_slash(result);
01714 }
01715 
01716 
01717 //-----------------------------------------------------------------------------
01718 QString KMFolderImap::decodeFileName(const QString &name)
01719 {
01720   QString result = KURL::decode_string(name);
01721   return utf7Codec()->toUnicode(result.latin1());
01722 }
01723 
01724 //-----------------------------------------------------------------------------
01725 bool KMFolderImap::autoExpunge()
01726 {
01727   if (mAccount)
01728     return mAccount->autoExpunge();
01729 
01730   return false;
01731 }
01732 
01733 
01734 //-----------------------------------------------------------------------------
01735 void KMFolderImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
01736 {
01737   if ( data.isEmpty() ) return; // optimization
01738   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01739   if ( it == mAccount->jobsEnd() ) return;
01740   QBuffer buff((*it).data);
01741   buff.open(IO_WriteOnly | IO_Append);
01742   buff.writeBlock(data.data(), data.size());
01743   buff.close();
01744 }
01745 
01746 //-----------------------------------------------------------------------------
01747 void KMFolderImap::deleteMessage(KMMessage * msg)
01748 {
01749   mUidMetaDataMap.remove( msg->UID() );
01750   mMetaDataMap.remove( msg->msgIdMD5() );
01751   KURL url = mAccount->getUrl();
01752   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msg->storage());
01753   ulong uid = msg->UID();
01754   /* If the uid is empty the delete job below will nuke all mail in the
01755      folder, so we better safeguard against that. See ::expungeFolder, as
01756      to why. :( */
01757   if ( uid == 0 ) {
01758      kdDebug( 5006 ) << "KMFolderImap::deleteMessage: Attempt to delete "
01759                         "an empty UID. Aborting."  << endl;
01760      return;
01761   }
01762   url.setPath(msg_parent->imapPath() + ";UID=" + QString::number(uid) );
01763   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01764     return;
01765   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01766   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01767   ImapAccountBase::jobData jd( url.url(), 0 );
01768   mAccount->insertJob(job, jd);
01769   connect(job, SIGNAL(result(KIO::Job *)),
01770           mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01771 }
01772 
01773 void KMFolderImap::deleteMessage(const QPtrList<KMMessage>& msgList)
01774 {
01775   QPtrListIterator<KMMessage> it( msgList );
01776   KMMessage *msg;
01777   while ( (msg = it.current()) != 0 ) {
01778     ++it;
01779     mUidMetaDataMap.remove( msg->UID() );
01780     mMetaDataMap.remove( msg->msgIdMD5() );
01781   }
01782 
01783   QValueList<ulong> uids;
01784   getUids(msgList, uids);
01785   QStringList sets = makeSets(uids);
01786 
01787   KURL url = mAccount->getUrl();
01788   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msgList.getFirst()->storage());
01789   for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
01790   {
01791     QString uid = *it;
01792     // Don't delete with no uid, that nukes the folder. Should not happen, but
01793     // better safe than sorry.
01794     if ( uid.isEmpty() ) continue;
01795     url.setPath(msg_parent->imapPath() + ";UID=" + uid);
01796     if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01797       return;
01798     KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01799     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01800     ImapAccountBase::jobData jd( url.url(), 0 );
01801     mAccount->insertJob(job, jd);
01802     connect(job, SIGNAL(result(KIO::Job *)),
01803         mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01804   }
01805 }
01806 
01807 //-----------------------------------------------------------------------------
01808 void KMFolderImap::setStatus(int idx, KMMsgStatus status, bool toggle)
01809 {
01810   QValueList<int> ids; ids.append(idx);
01811   setStatus(ids, status, toggle);
01812 }
01813 
01814 void KMFolderImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01815 {
01816   FolderStorage::setStatus(ids, status, toggle);
01817   if (mReadOnly) return;
01818 
01819   /* The status has been already set in the local index. Update the flags on
01820    * the server. To avoid doing that for each message individually, group them
01821    * by the status string they will be assigned and make sets for each of those
01822    * groups of mails. This is necessary because the imap kio_slave status job
01823    * does not append flags but overwrites them. Example:
01824    *
01825    * 2 important mails and 3 unimportant mail, all unread. Mark all as read calls
01826    * this method with a list of uids. The 2 important mails need to get the string
01827    * \SEEN \FLAGGED while the others need to get just \SEEN. Build sets for each
01828    * of those and sort them, so the server can handle them efficiently. */
01829   QMap< QString, QStringList > groups;
01830   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) {
01831     KMMessage *msg = 0;
01832     bool unget = !isMessage(*it);
01833     msg = getMsg(*it);
01834     if (!msg) continue;
01835     QString flags = statusToFlags(msg->status());
01836     // Collect uids for each type of flags.
01837     groups[flags].append(QString::number(msg->UID()));
01838     if (unget) unGetMsg(*it);
01839   }
01840   QMapIterator< QString, QStringList > dit;
01841   for ( dit = groups.begin(); dit != groups.end(); ++dit ) {
01842      QCString flags = dit.key().latin1();
01843      QStringList sets = makeSets( (*dit), true );
01844      // Send off a status setting job for each set.
01845      for (  QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
01846        QString imappath = imapPath() + ";UID=" + ( *slit );
01847        mAccount->setImapStatus(folder(), imappath, flags);
01848      }
01849   }
01850   if ( mContentState == imapListingInProgress ) {
01851     // we're currently get'ing this folder
01852     // to make sure that we get the latest flags abort the current listing and
01853     // create a new one
01854     kdDebug(5006) << "Set status during folder listing, restarting listing." << endl;
01855     disconnect(this, SLOT(slotListFolderResult(KIO::Job *)));
01856     quiet( false );
01857     reallyGetFolder( QString::null );
01858   }
01859 }
01860 
01861 //-----------------------------------------------------------------------------
01862 QStringList KMFolderImap::makeSets(const QStringList& uids, bool sort)
01863 {
01864   QValueList<ulong> tmp;
01865   for ( QStringList::ConstIterator it = uids.begin(); it != uids.end(); ++it )
01866     tmp.append( (*it).toInt() );
01867   return makeSets(tmp, sort);
01868 }
01869 
01870 QStringList KMFolderImap::makeSets( QValueList<ulong>& uids, bool sort )
01871 {
01872   QStringList sets;
01873   QString set;
01874 
01875   if (uids.size() == 1)
01876   {
01877     sets.append(QString::number(uids.first()));
01878     return sets;
01879   }
01880 
01881   if (sort) qHeapSort(uids);
01882 
01883   ulong last = 0;
01884   // needed to make a uid like 124 instead of 124:124
01885   bool inserted = false;
01886   /* iterate over uids and build sets like 120:122,124,126:150 */
01887   for ( QValueList<ulong>::Iterator it = uids.begin(); it != uids.end(); ++it )
01888   {
01889     if (it == uids.begin() || set.isEmpty()) {
01890       set = QString::number(*it);
01891       inserted = true;
01892     } else
01893     {
01894       if (last+1 != *it)
01895       {
01896         // end this range
01897         if (inserted)
01898           set += ',' + QString::number(*it);
01899         else
01900           set += ':' + QString::number(last) + ',' + QString::number(*it);
01901         inserted = true;
01902         if (set.length() > 100)
01903         {
01904           // just in case the server has a problem with longer lines..
01905           sets.append(set);
01906           set = "";
01907         }
01908       } else {
01909         inserted = false;
01910       }
01911     }
01912     last = *it;
01913   }
01914   // last element
01915   if (!inserted)
01916     set += ':' + QString::number(uids.last());
01917 
01918   if (!set.isEmpty()) sets.append(set);
01919 
01920   return sets;
01921 }
01922 
01923 //-----------------------------------------------------------------------------
01924 void KMFolderImap::getUids(QValueList<int>& ids, QValueList<ulong>& uids)
01925 {
01926   KMMsgBase *msg = 0;
01927   // get the uids
01928   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
01929   {
01930     msg = getMsgBase(*it);
01931     if (!msg) continue;
01932     uids.append(msg->UID());
01933   }
01934 }
01935 
01936 void KMFolderImap::getUids(const QPtrList<KMMessage>& msgList, QValueList<ulong>& uids)
01937 {
01938   KMMessage *msg = 0;
01939 
01940   QPtrListIterator<KMMessage> it( msgList );
01941   while ( (msg = it.current()) != 0 ) {
01942     ++it;
01943     if ( msg->UID() > 0 ) {
01944       uids.append( msg->UID() );
01945     }
01946   }
01947 }
01948 
01949 //-----------------------------------------------------------------------------
01950 void KMFolderImap::expungeFolder(KMFolderImap * aFolder, bool quiet)
01951 {
01952   aFolder->setNeedsCompacting(FALSE);
01953   KURL url = mAccount->getUrl();
01954   url.setPath(aFolder->imapPath() + ";UID=*");
01955   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01956     return;
01957   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01958   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01959   ImapAccountBase::jobData jd( url.url(), 0 );
01960   jd.quiet = quiet;
01961   mAccount->insertJob(job, jd);
01962   connect(job, SIGNAL(result(KIO::Job *)),
01963           mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01964 }
01965 
01966 //-----------------------------------------------------------------------------
01967 void KMFolderImap::slotProcessNewMail( int errorCode, const QString &errorMsg )
01968 {
01969   Q_UNUSED( errorMsg );
01970   disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01971               this, SLOT( slotProcessNewMail(int, const QString&) ) );
01972   if ( !errorCode )
01973     processNewMail( false );
01974   else
01975     emit numUnreadMsgsChanged( folder() );
01976 }
01977 
01978 //-----------------------------------------------------------------------------
01979 bool KMFolderImap::processNewMail(bool)
01980 {
01981    // a little safety
01982   if ( !mAccount ) {
01983     kdDebug(5006) << "KMFolderImap::processNewMail - account is null!" << endl;
01984     return false;
01985   }
01986   if ( imapPath().isEmpty() ) {
01987     kdDebug(5006) << "KMFolderImap::processNewMail - imapPath of " << name() << " is empty!" << endl;
01988     // remove it locally
01989     setAlreadyRemoved( true );
01990     kmkernel->imapFolderMgr()->remove( folder() );
01991     return false;
01992   }
01993   // check the connection
01994   if ( mAccount->makeConnection() == ImapAccountBase::Error ) {
01995     kdDebug(5006) << "KMFolderImap::processNewMail - got no connection!" << endl;
01996     return false;
01997   } else if ( mAccount->makeConnection() == ImapAccountBase::Connecting )
01998   {
01999     // wait
02000     kdDebug(5006) << "KMFolderImap::processNewMail - waiting for connection: " << label() << endl;
02001     connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
02002         this, SLOT( slotProcessNewMail(int, const QString&) ) );
02003     return true;
02004   }
02005   KURL url = mAccount->getUrl();
02006   if (mReadOnly)
02007     url.setPath(imapPath() + ";SECTION=UIDNEXT");
02008   else
02009     url.setPath(imapPath() + ";SECTION=UNSEEN");
02010 
02011   mMailCheckProgressItem = ProgressManager::createProgressItem(
02012               "MailCheckAccount" + account()->name(),
02013               "MailCheck" + folder()->prettyURL(),
02014               QStyleSheet::escape( folder()->prettyURL() ),
02015               i18n("updating message counts"),
02016               false,
02017               account()->useSSL() || account()->useTLS() );
02018 
02019   KIO::SimpleJob *job = KIO::stat(url, FALSE);
02020   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
02021   ImapAccountBase::jobData jd(url.url(), folder() );
02022   jd.cancellable = true;
02023   mAccount->insertJob(job, jd);
02024   connect(job, SIGNAL(result(KIO::Job *)),
02025           SLOT(slotStatResult(KIO::Job *)));
02026   return true;
02027 }
02028 
02029 
02030 //-----------------------------------------------------------------------------
02031 void KMFolderImap::slotStatResult(KIO::Job * job)
02032 {
02033   slotCompleteMailCheckProgress();
02034   ImapAccountBase::JobIterator it = mAccount->findJob(job);
02035   if ( it == mAccount->jobsEnd() ) return;
02036   mAccount->removeJob(it);
02037   if (job->error())
02038   {
02039     mAccount->handleJobError( job, i18n("Error while getting folder information.") );
02040   } else {
02041     KIO::UDSEntry uds = static_cast<KIO::StatJob*>(job)->statResult();
02042     for (KIO::UDSEntry::ConstIterator it = uds.begin(); it != uds.end(); it++)
02043     {
02044       if ((*it).m_uds == KIO::UDS_SIZE)
02045       {
02046         if (mReadOnly)
02047         {
02048           mGuessedUnreadMsgs = -1;
02049           mGuessedUnreadMsgs = countUnread() + (*it).m_long - lastUid() - 1;
02050           if (mGuessedUnreadMsgs < 0) mGuessedUnreadMsgs = 0;
02051         } else {
02052           mGuessedUnreadMsgs = (*it).m_long;
02053         }
02054       }
02055     }
02056   }
02057 }
02058 
02059 //-----------------------------------------------------------------------------
02060 int KMFolderImap::create()
02061 {
02062   readConfig();
02063   mUnreadMsgs = -1;
02064   return KMFolderMbox::create();
02065 }
02066 
02067 QValueList<ulong> KMFolderImap::splitSets(const QString uids)
02068 {
02069   QValueList<ulong> uidlist;
02070 
02071   // ex: 1205,1204,1203,1202,1236:1238
02072   QString buffer = QString::null;
02073   int setstart = -1;
02074   // iterate over the uids
02075   for (uint i = 0; i < uids.length(); i++)
02076   {
02077     QChar chr = uids[i];
02078     if (chr == ',')
02079     {
02080       if (setstart > -1)
02081       {
02082         // a range (uid:uid) was before
02083         for (int j = setstart; j <= buffer.toInt(); j++)
02084         {
02085           uidlist.append(j);
02086         }
02087         setstart = -1;
02088       } else {
02089         // single uid
02090         uidlist.append(buffer.toInt());
02091       }
02092       buffer = "";
02093     } else if (chr == ':') {
02094       // remember the start of the range
02095       setstart = buffer.toInt();
02096       buffer = "";
02097     } else if (chr.category() == QChar::Number_DecimalDigit) {
02098       // digit
02099       buffer += chr;
02100     } else {
02101       // ignore
02102     }
02103   }
02104   // process the last data
02105   if (setstart > -1)
02106   {
02107     for (int j = setstart; j <= buffer.toInt(); j++)
02108     {
02109       uidlist.append(j);
02110     }
02111   } else {
02112     uidlist.append(buffer.toInt());
02113   }
02114 
02115   return uidlist;
02116 }
02117 
02118 //-----------------------------------------------------------------------------
02119 int KMFolderImap::expungeContents()
02120 {
02121   // nuke the local cache
02122   int rc = KMFolderMbox::expungeContents();
02123 
02124   // set the deleted flag for all messages in the folder
02125   KURL url = mAccount->getUrl();
02126   url.setPath( imapPath() + ";UID=1:*");
02127   if ( mAccount->makeConnection() == ImapAccountBase::Connected )
02128   {
02129     KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
02130     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
02131     ImapAccountBase::jobData jd( url.url(), 0 );
02132     jd.quiet = true;
02133     mAccount->insertJob(job, jd);
02134     connect(job, SIGNAL(result(KIO::Job *)),
02135             mAccount, SLOT(slotSimpleResult(KIO::Job *)));
02136   }
02137   /* Is the below correct? If we are expunging (in the folder sense, not the imap sense),
02138      why delete but not (imap-)expunge? Since the folder is not active there is no concept
02139      of "leaving the folder", so the setting really has little to do with it. */
02140   // if ( autoExpunge() )
02141     expungeFolder(this, true);
02142   getFolder();
02143 
02144   return rc;
02145 }
02146 
02147 //-----------------------------------------------------------------------------
02148 void
02149 KMFolderImap::setUserRights( unsigned int userRights )
02150 {
02151   mUserRights = userRights;
02152   kdDebug(5006) << imapPath() << " setUserRights: " << userRights << endl;
02153 }
02154 
02155 //-----------------------------------------------------------------------------
02156 void KMFolderImap::slotCompleteMailCheckProgress()
02157 {
02158   if ( mMailCheckProgressItem ) {
02159     mMailCheckProgressItem->setComplete();
02160     mMailCheckProgressItem = 0;
02161     emit numUnreadMsgsChanged( folder() );
02162   }
02163 }
02164 
02165 //-----------------------------------------------------------------------------
02166 void KMFolderImap::setSubfolderState( imapState state )
02167 {
02168   mSubfolderState = state;
02169   if ( state == imapNoInformation && folder()->child() )
02170   {
02171     // pass through to children
02172     KMFolderNode* node;
02173     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02174     for ( ; (node = it.current()); )
02175     {
02176       ++it;
02177       if (node->isDir()) continue;
02178       KMFolder *folder = static_cast<KMFolder*>(node);
02179       static_cast<KMFolderImap*>(folder->storage())->setSubfolderState( state );
02180     }
02181   }
02182 }
02183 
02184 //-----------------------------------------------------------------------------
02185 void KMFolderImap::setIncludeInMailCheck( bool check )
02186 {
02187   bool changed = ( mCheckMail != check );
02188   mCheckMail = check;
02189   if ( changed )
02190     account()->slotUpdateFolderList();
02191 }
02192 
02193 //-----------------------------------------------------------------------------
02194 void KMFolderImap::setAlreadyRemoved( bool removed )
02195 {
02196   mAlreadyRemoved = removed;
02197   if ( folder()->child() )
02198   {
02199     // pass through to childs
02200     KMFolderNode* node;
02201     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02202     for ( ; (node = it.current()); )
02203     {
02204       ++it;
02205       if (node->isDir()) continue;
02206       KMFolder *folder = static_cast<KMFolder*>(node);
02207       static_cast<KMFolderImap*>(folder->storage())->setAlreadyRemoved( removed );
02208     }
02209   }
02210 }
02211 
02212 void KMFolderImap::slotCreatePendingFolders( int errorCode, const QString& errorMsg )
02213 {
02214   Q_UNUSED( errorMsg );
02215   disconnect( mAccount, SIGNAL( connectionResult( int, const QString& ) ),
02216               this, SLOT( slotCreatePendingFolders( int, const QString& ) ) );
02217   if ( !errorCode ) {
02218     QStringList::Iterator it = mFoldersPendingCreation.begin();
02219     for ( ; it != mFoldersPendingCreation.end(); ++it ) {
02220       createFolder( *it );
02221     }
02222   }
02223   mFoldersPendingCreation.clear();
02224 }
02225 
02226 //-----------------------------------------------------------------------------
02227 void KMFolderImap::search( const KMSearchPattern* pattern )
02228 {
02229   if ( !pattern || pattern->isEmpty() )
02230   {
02231     // not much to do here
02232     QValueList<Q_UINT32> serNums;
02233     emit searchResult( folder(), serNums, pattern, true );
02234     return;
02235   }
02236   SearchJob* job = new SearchJob( this, mAccount, pattern );
02237   connect( job, SIGNAL( searchDone( QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ),
02238            this, SLOT( slotSearchDone( QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ) );
02239   job->start();
02240 }
02241 
02242 //-----------------------------------------------------------------------------
02243 void KMFolderImap::slotSearchDone( QValueList<Q_UINT32> serNums,
02244                                    const KMSearchPattern* pattern,
02245                                    bool complete )
02246 {
02247   emit searchResult( folder(), serNums, pattern, complete );
02248 }
02249 
02250 //-----------------------------------------------------------------------------
02251 void KMFolderImap::search( const KMSearchPattern* pattern, Q_UINT32 serNum )
02252 {
02253   if ( !pattern || pattern->isEmpty() )
02254   {
02255     // not much to do here
02256     emit searchDone( folder(), serNum, pattern, false );
02257     return;
02258   }
02259   SearchJob* job = new SearchJob( this, mAccount, pattern, serNum );
02260   connect( job, SIGNAL( searchDone( Q_UINT32, const KMSearchPattern*, bool ) ),
02261            this, SLOT( slotSearchDone( Q_UINT32, const KMSearchPattern*, bool ) ) );
02262   job->start();
02263 }
02264 
02265 //-----------------------------------------------------------------------------
02266 void KMFolderImap::slotSearchDone( Q_UINT32 serNum, const KMSearchPattern* pattern,
02267                                    bool matches )
02268 {
02269   emit searchDone( folder(), serNum, pattern, matches );
02270 }
02271 
02272 //-----------------------------------------------------------------------------
02273 bool KMFolderImap::isMoveable() const
02274 {
02275   return ( hasChildren() == HasNoChildren &&
02276       !folder()->isSystemFolder() ) ? true : false;
02277 }
02278 
02279 //-----------------------------------------------------------------------------
02280 const ulong KMFolderImap::serNumForUID( ulong uid )
02281 {
02282   if ( mUidMetaDataMap.find( uid ) ) {
02283     KMMsgMetaData *md = mUidMetaDataMap[uid];
02284     return md->serNum();
02285   } else {
02286     kdDebug(5006) << "serNumForUID: unknown uid " << uid << endl;
02287     return 0;
02288   }
02289 }
02290 
02291 //-----------------------------------------------------------------------------
02292 void KMFolderImap::saveMsgMetaData( KMMessage* msg, ulong uid )
02293 {
02294   if ( uid == 0 ) {
02295     uid = msg->UID();
02296   }
02297   ulong serNum = msg->getMsgSerNum();
02298   mUidMetaDataMap.replace( uid, new KMMsgMetaData(msg->status(), serNum) );
02299 }
02300 
02301 //-----------------------------------------------------------------------------
02302 void KMFolderImap::setImapPath( const QString& path )
02303 {
02304   if ( path.isEmpty() ) {
02305     kdWarning(5006) << k_funcinfo << "ignoring empty path" << endl;
02306   } else {
02307     mImapPath = path;
02308   }
02309 }
02310 
02311 void KMFolderImap::finishMailCheck( imapState state )
02312 {
02313   quiet( false );
02314   mContentState = state;
02315   emit folderComplete( this, mContentState == imapFinished );
02316   close();
02317 }
02318 
02319 #include "kmfolderimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys