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   const KMMsgStatus oldStatus = msg->status();
01386   // Set flags if they are new
01387   if ( (flags & 4) && (oldStatus & KMMsgStatusFlag) == 0 )
01388     msg->setStatus( KMMsgStatusFlag );
01389   if ( (flags & 2) && (oldStatus & KMMsgStatusReplied) == 0 )
01390     msg->setStatus( KMMsgStatusReplied );
01391   if ( (flags & 1) && (oldStatus & KMMsgStatusOld) == 0 )
01392     msg->setStatus( KMMsgStatusOld );
01393 
01394   // Toggle flags if they changed
01395 //  if ( ( (flags & 4) > 0 ) != ( (oldStatus & KMMsgStatusFlag) > 0 ) )
01396 //    msg->toggleStatus( KMMsgStatusFlag );
01397 //  if ( ( (flags & 2) > 0 ) != ( (oldStatus & KMMsgStatusReplied) > 0 ) )
01398 //    msg->toggleStatus( KMMsgStatusReplied );
01399 //  if ( ( (flags & 1) > 0 ) != ( (oldStatus & KMMsgStatusOld) > 0 ) )
01400 //    msg->toggleStatus( KMMsgStatusOld );
01401 
01402   // In case the message does not have the seen flag set, override our local
01403   // notion that it is read. Otherwise the count of unread messages and the
01404   // number of messages which actually show up as read can go out of sync.
01405   if (msg->isOfUnknownStatus() || !(flags&1) ) {
01406     if (newMsg) {
01407       if ( (oldStatus & KMMsgStatusNew) == 0 )
01408         msg->setStatus( KMMsgStatusNew );
01409     } else {
01410       if ( (oldStatus & KMMsgStatusUnread) == 0 )
01411         msg->setStatus( KMMsgStatusUnread );
01412     }
01413   }
01414 }
01415 
01416 
01417 //-----------------------------------------------------------------------------
01418 QString KMFolderImap::statusToFlags(KMMsgStatus status)
01419 {
01420   QString flags;
01421   if (status & KMMsgStatusDeleted)
01422     flags = "\\DELETED";
01423   else {
01424     if (status & KMMsgStatusOld || status & KMMsgStatusRead)
01425       flags = "\\SEEN ";
01426     if (status & KMMsgStatusReplied)
01427       flags += "\\ANSWERED ";
01428     if (status & KMMsgStatusFlag)
01429       flags += "\\FLAGGED";
01430   }
01431 
01432   return flags.simplifyWhiteSpace();
01433 }
01434 
01435 //-------------------------------------------------------------
01436 void
01437 KMFolderImap::ignoreJobsForMessage( KMMessage* msg )
01438 {
01439   if ( !msg || msg->transferInProgress() ||
01440        !msg->parent() || msg->parent()->folderType() != KMFolderTypeImap )
01441     return;
01442   KMAcctImap *account;
01443   if ( !(account = static_cast<KMFolderImap*>(msg->storage())->account()) )
01444     return;
01445 
01446   account->ignoreJobsForMessage( msg );
01447 }
01448 
01449 //-----------------------------------------------------------------------------
01450 void KMFolderImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
01451 {
01452   if ( data.isEmpty() ) return; // optimization
01453   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01454   if ( it == mAccount->jobsEnd() ) return;
01455   (*it).cdata += QCString(data, data.size() + 1);
01456   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
01457   if ( pos == -1 ) {
01458     // if we do not find the pattern in the complete string we will not find 
01459     // it in a substring.
01460     return;
01461   }
01462   if (pos > 0)
01463   {
01464     int p = (*it).cdata.find("\r\nX-uidValidity:");
01465     if (p != -1) setUidValidity((*it).cdata
01466       .mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
01467     int c = (*it).cdata.find("\r\nX-Count:");
01468     if ( c != -1 )
01469     {
01470       bool ok;
01471       int exists = (*it).cdata.mid( c+10,
01472           (*it).cdata.find("\r\n", c+1) - c-10 ).toInt(&ok);
01473       if ( ok && exists < count() ) {
01474         kdDebug(5006) << "KMFolderImap::slotGetMessagesData - server has less messages (" <<
01475           exists << ") then folder (" << count() << "), so reload" << endl;
01476         open();
01477         reallyGetFolder( QString::null );
01478         (*it).cdata.remove(0, pos);
01479         return;
01480       } else if ( ok ) {
01481         int delta = exists - count();
01482         if ( mMailCheckProgressItem ) {
01483           mMailCheckProgressItem->setTotalItems( delta );
01484         }
01485       }
01486     }
01487     (*it).cdata.remove(0, pos);
01488   }
01489   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01490   int flags;
01491   while (pos >= 0)
01492   {
01493     KMMessage *msg = new KMMessage;
01494     msg->setComplete( false );
01495     msg->setReadyToShow( false );
01496     // nothing between the boundaries, older UWs do that
01497     if ( pos != 14 ) {
01498       msg->fromString( (*it).cdata.mid(16, pos - 16) );
01499       flags = msg->headerField("X-Flags").toInt();
01500       ulong uid = msg->UID();
01501       KMMsgMetaData *md =  0;
01502       if ( mUidMetaDataMap.find( uid ) ) {
01503           md =  mUidMetaDataMap[uid];
01504       }
01505       ulong serNum = 0;
01506       if ( md ) {
01507         serNum = md->serNum();
01508       }
01509       bool ok = true;
01510       if ( uid <= lastUid() && serNum > 0 ) {
01511         // the UID is already known so no need to create it
01512         ok = false;
01513       }
01514       // deleted flag
01515       if ( flags & 8 )
01516         ok = false;
01517       if ( !ok ) {
01518         delete msg;
01519         msg = 0;
01520       } else {
01521         if ( serNum > 0 ) {
01522           // assign the sernum from the cache
01523           msg->setMsgSerNum( serNum );
01524         }
01525         // Transfer the status, if it is cached.
01526         if ( md ) {
01527           msg->setStatus( md->status() );
01528         } else if ( !mAccount->hasCapability("uidplus") ) {
01529           // see if we have cached the msgIdMD5 and get the status +
01530           // serial number from there
01531           QString id = msg->msgIdMD5();
01532           if ( mMetaDataMap.find( id ) ) {
01533             md =  mMetaDataMap[id];
01534             msg->setStatus( md->status() );
01535             if ( md->serNum() != 0 && serNum == 0 ) {
01536               msg->setMsgSerNum( md->serNum() );
01537             }
01538             mMetaDataMap.remove( id );
01539             delete md;
01540           }
01541         }
01542         KMFolderMbox::addMsg(msg, 0);
01543         // Merge with the flags from the server.
01544         flagsToStatus((KMMsgBase*)msg, flags);
01545         // set the correct size
01546         msg->setMsgSizeServer( msg->headerField("X-Length").toUInt() );
01547         msg->setUID(uid);
01548         if ( msg->getMsgSerNum() > 0 ) {
01549           saveMsgMetaData( msg );
01550         }
01551         // Filter messages that have arrived in the inbox folder
01552         if ( folder()->isSystemFolder() && imapPath() == "/INBOX/"
01553             && kmkernel->filterMgr()->atLeastOneIncomingFilterAppliesTo( mAccount->id() ) )
01554             mAccount->execFilters( msg->getMsgSerNum() );
01555         
01556         if ( count() > 1 ) {
01557           unGetMsg(count() - 1);
01558         }
01559         mLastUid = uid;
01560         if ( mMailCheckProgressItem ) {
01561           mMailCheckProgressItem->incCompletedItems();
01562           mMailCheckProgressItem->updateProgress();
01563         }
01564       }
01565     }
01566     (*it).cdata.remove(0, pos);
01567     (*it).done++;
01568     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01569   } // while
01570 }
01571 
01572 //-------------------------------------------------------------
01573 FolderJob*
01574 KMFolderImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
01575                            KMFolder *folder, QString partSpecifier,
01576                            const AttachmentStrategy *as ) const
01577 {
01578   KMFolderImap* kmfi = folder? dynamic_cast<KMFolderImap*>(folder->storage()) : 0;
01579   if ( jt == FolderJob::tGetMessage && partSpecifier == "STRUCTURE" &&
01580        mAccount && mAccount->loadOnDemand() &&
01581        ( msg->msgSizeServer() > 5000 || msg->msgSizeServer() == 0 ) &&
01582        ( msg->signatureState() == KMMsgNotSigned ||
01583          msg->signatureState() == KMMsgSignatureStateUnknown ) &&
01584        ( msg->encryptionState() == KMMsgNotEncrypted ||
01585          msg->encryptionState() == KMMsgEncryptionStateUnknown ) )
01586   {
01587     // load-on-demand: retrieve the BODYSTRUCTURE and to speed things up also the headers
01588     // this is not activated for small or signed messages
01589     ImapJob *job = new ImapJob( msg, jt, kmfi, "HEADER" );
01590     job->start();
01591     ImapJob *job2 = new ImapJob( msg, jt, kmfi, "STRUCTURE", as );
01592     job2->start();
01593     job->setParentFolder( this );
01594     return job;
01595   } else {
01596     // download complete message or part (attachment)
01597     if ( partSpecifier == "STRUCTURE" ) // hide from outside
01598       partSpecifier = QString::null;
01599 
01600     ImapJob *job = new ImapJob( msg, jt, kmfi, partSpecifier );
01601     job->setParentFolder( this );
01602     return job;
01603   }
01604 }
01605 
01606 //-------------------------------------------------------------
01607 FolderJob*
01608 KMFolderImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
01609                            FolderJob::JobType jt, KMFolder *folder ) const
01610 {
01611   KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder->storage());
01612   ImapJob *job = new ImapJob( msgList, sets, jt, kmfi );
01613   job->setParentFolder( this );
01614   return job;
01615 }
01616 
01617 //-----------------------------------------------------------------------------
01618 void KMFolderImap::getMessagesResult(KIO::Job * job, bool lastSet)
01619 {
01620   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01621   if ( it == mAccount->jobsEnd() ) return;
01622   if (job->error()) {
01623     mAccount->handleJobError( job, i18n("Error while retrieving messages.") );
01624     finishMailCheck( imapNoInformation );
01625     return;
01626   }
01627   if (lastSet) {
01628     finishMailCheck( imapFinished );
01629     mAccount->removeJob(it);
01630   }
01631 }
01632 
01633 
01634 //-----------------------------------------------------------------------------
01635 void KMFolderImap::slotGetLastMessagesResult(KIO::Job * job)
01636 {
01637   getMessagesResult(job, true);
01638 }
01639 
01640 
01641 //-----------------------------------------------------------------------------
01642 void KMFolderImap::slotGetMessagesResult(KIO::Job * job)
01643 {
01644   getMessagesResult(job, false);
01645 }
01646 
01647 
01648 //-----------------------------------------------------------------------------
01649 void KMFolderImap::createFolder(const QString &name, const QString& parentPath,
01650                                 bool askUser)
01651 {
01652   kdDebug(5006) << "KMFolderImap::createFolder - name=" << name << ",parent=" <<
01653     parentPath << ",askUser=" << askUser << endl;
01654   if ( mAccount->makeConnection() != ImapAccountBase::Connected ) {
01655     kdWarning(5006) << "KMFolderImap::createFolder - got no connection" << endl;
01656     return;
01657   }
01658   KURL url = mAccount->getUrl();
01659   QString parent = ( parentPath.isEmpty() ? imapPath() : parentPath );
01660   QString path = mAccount->createImapPath( parent, name );
01661   if ( askUser ) {
01662     path += "/;INFO=ASKUSER";
01663   }
01664   url.setPath( path );
01665 
01666   KIO::SimpleJob *job = KIO::mkdir(url);
01667   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01668   ImapAccountBase::jobData jd( url.url(), folder() );
01669   jd.items = name;
01670   mAccount->insertJob(job, jd);
01671   connect(job, SIGNAL(result(KIO::Job *)),
01672           this, SLOT(slotCreateFolderResult(KIO::Job *)));
01673 }
01674 
01675 
01676 //-----------------------------------------------------------------------------
01677 void KMFolderImap::slotCreateFolderResult(KIO::Job * job)
01678 {
01679   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01680   if ( it == mAccount->jobsEnd() ) return;
01681   if (job->error())
01682   {
01683     if ( job->error() == KIO::ERR_COULD_NOT_MKDIR ) {
01684       // Creating a folder failed, remove it from the tree.
01685       mAccount->listDirectory( );
01686     }
01687     mAccount->handleJobError( job, i18n("Error while creating a folder.") );
01688   } else {
01689     listDirectory();
01690     mAccount->removeJob(job);
01691   }
01692 }
01693 
01694 
01695 //-----------------------------------------------------------------------------
01696 static QTextCodec *sUtf7Codec = 0;
01697 
01698 QTextCodec * KMFolderImap::utf7Codec()
01699 {
01700   if (!sUtf7Codec) sUtf7Codec = QTextCodec::codecForName("utf-7");
01701   return sUtf7Codec;
01702 }
01703 
01704 
01705 //-----------------------------------------------------------------------------
01706 QString KMFolderImap::encodeFileName(const QString &name)
01707 {
01708   QString result = utf7Codec()->fromUnicode(name);
01709   return KURL::encode_string_no_slash(result);
01710 }
01711 
01712 
01713 //-----------------------------------------------------------------------------
01714 QString KMFolderImap::decodeFileName(const QString &name)
01715 {
01716   QString result = KURL::decode_string(name);
01717   return utf7Codec()->toUnicode(result.latin1());
01718 }
01719 
01720 //-----------------------------------------------------------------------------
01721 bool KMFolderImap::autoExpunge()
01722 {
01723   if (mAccount)
01724     return mAccount->autoExpunge();
01725 
01726   return false;
01727 }
01728 
01729 
01730 //-----------------------------------------------------------------------------
01731 void KMFolderImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
01732 {
01733   if ( data.isEmpty() ) return; // optimization
01734   ImapAccountBase::JobIterator it = mAccount->findJob(job);
01735   if ( it == mAccount->jobsEnd() ) return;
01736   QBuffer buff((*it).data);
01737   buff.open(IO_WriteOnly | IO_Append);
01738   buff.writeBlock(data.data(), data.size());
01739   buff.close();
01740 }
01741 
01742 //-----------------------------------------------------------------------------
01743 void KMFolderImap::deleteMessage(KMMessage * msg)
01744 {
01745   mUidMetaDataMap.remove( msg->UID() );
01746   mMetaDataMap.remove( msg->msgIdMD5() );
01747   KURL url = mAccount->getUrl();
01748   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msg->storage());
01749   ulong uid = msg->UID();
01750   /* If the uid is empty the delete job below will nuke all mail in the
01751      folder, so we better safeguard against that. See ::expungeFolder, as
01752      to why. :( */
01753   if ( uid == 0 ) {
01754      kdDebug( 5006 ) << "KMFolderImap::deleteMessage: Attempt to delete "
01755                         "an empty UID. Aborting."  << endl;
01756      return;
01757   }
01758   url.setPath(msg_parent->imapPath() + ";UID=" + QString::number(uid) );
01759   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01760     return;
01761   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01762   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01763   ImapAccountBase::jobData jd( url.url(), 0 );
01764   mAccount->insertJob(job, jd);
01765   connect(job, SIGNAL(result(KIO::Job *)),
01766           mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01767 }
01768 
01769 void KMFolderImap::deleteMessage(const QPtrList<KMMessage>& msgList)
01770 {
01771   QPtrListIterator<KMMessage> it( msgList );
01772   KMMessage *msg;
01773   while ( (msg = it.current()) != 0 ) {
01774     ++it;
01775     mUidMetaDataMap.remove( msg->UID() );
01776     mMetaDataMap.remove( msg->msgIdMD5() );
01777   }
01778 
01779   QValueList<ulong> uids;
01780   getUids(msgList, uids);
01781   QStringList sets = makeSets(uids);
01782 
01783   KURL url = mAccount->getUrl();
01784   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msgList.getFirst()->storage());
01785   for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
01786   {
01787     QString uid = *it;
01788     // Don't delete with no uid, that nukes the folder. Should not happen, but
01789     // better safe than sorry.
01790     if ( uid.isEmpty() ) continue;
01791     url.setPath(msg_parent->imapPath() + ";UID=" + uid);
01792     if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01793       return;
01794     KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01795     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01796     ImapAccountBase::jobData jd( url.url(), 0 );
01797     mAccount->insertJob(job, jd);
01798     connect(job, SIGNAL(result(KIO::Job *)),
01799         mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01800   }
01801 }
01802 
01803 //-----------------------------------------------------------------------------
01804 void KMFolderImap::setStatus(int idx, KMMsgStatus status, bool toggle)
01805 {
01806   QValueList<int> ids; ids.append(idx);
01807   setStatus(ids, status, toggle);
01808 }
01809 
01810 void KMFolderImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle)
01811 {
01812   FolderStorage::setStatus(ids, status, toggle);
01813   if (mReadOnly) return;
01814 
01815   /* The status has been already set in the local index. Update the flags on
01816    * the server. To avoid doing that for each message individually, group them
01817    * by the status string they will be assigned and make sets for each of those
01818    * groups of mails. This is necessary because the imap kio_slave status job
01819    * does not append flags but overwrites them. Example:
01820    *
01821    * 2 important mails and 3 unimportant mail, all unread. Mark all as read calls
01822    * this method with a list of uids. The 2 important mails need to get the string
01823    * \SEEN \FLAGGED while the others need to get just \SEEN. Build sets for each
01824    * of those and sort them, so the server can handle them efficiently. */
01825   QMap< QString, QStringList > groups;
01826   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) {
01827     KMMessage *msg = 0;
01828     bool unget = !isMessage(*it);
01829     msg = getMsg(*it);
01830     if (!msg) continue;
01831     QString flags = statusToFlags(msg->status());
01832     // Collect uids for each type of flags.
01833     groups[flags].append(QString::number(msg->UID()));
01834     if (unget) unGetMsg(*it);
01835   }
01836   QMapIterator< QString, QStringList > dit;
01837   for ( dit = groups.begin(); dit != groups.end(); ++dit ) {
01838      QCString flags = dit.key().latin1();
01839      QStringList sets = makeSets( (*dit), true );
01840      // Send off a status setting job for each set.
01841      for (  QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
01842        QString imappath = imapPath() + ";UID=" + ( *slit );
01843        mAccount->setImapStatus(folder(), imappath, flags);
01844      }
01845   }
01846   if ( mContentState == imapListingInProgress ) {
01847     // we're currently get'ing this folder
01848     // to make sure that we get the latest flags abort the current listing and
01849     // create a new one
01850     kdDebug(5006) << "Set status during folder listing, restarting listing." << endl;
01851     disconnect(this, SLOT(slotListFolderResult(KIO::Job *)));
01852     quiet( false );
01853     reallyGetFolder( QString::null );
01854   }
01855 }
01856 
01857 //-----------------------------------------------------------------------------
01858 QStringList KMFolderImap::makeSets(const QStringList& uids, bool sort)
01859 {
01860   QValueList<ulong> tmp;
01861   for ( QStringList::ConstIterator it = uids.begin(); it != uids.end(); ++it )
01862     tmp.append( (*it).toInt() );
01863   return makeSets(tmp, sort);
01864 }
01865 
01866 QStringList KMFolderImap::makeSets( QValueList<ulong>& uids, bool sort )
01867 {
01868   QStringList sets;
01869   QString set;
01870 
01871   if (uids.size() == 1)
01872   {
01873     sets.append(QString::number(uids.first()));
01874     return sets;
01875   }
01876 
01877   if (sort) qHeapSort(uids);
01878 
01879   ulong last = 0;
01880   // needed to make a uid like 124 instead of 124:124
01881   bool inserted = false;
01882   /* iterate over uids and build sets like 120:122,124,126:150 */
01883   for ( QValueList<ulong>::Iterator it = uids.begin(); it != uids.end(); ++it )
01884   {
01885     if (it == uids.begin() || set.isEmpty()) {
01886       set = QString::number(*it);
01887       inserted = true;
01888     } else
01889     {
01890       if (last+1 != *it)
01891       {
01892         // end this range
01893         if (inserted)
01894           set += ',' + QString::number(*it);
01895         else
01896           set += ':' + QString::number(last) + ',' + QString::number(*it);
01897         inserted = true;
01898         if (set.length() > 100)
01899         {
01900           // just in case the server has a problem with longer lines..
01901           sets.append(set);
01902           set = "";
01903         }
01904       } else {
01905         inserted = false;
01906       }
01907     }
01908     last = *it;
01909   }
01910   // last element
01911   if (!inserted)
01912     set += ':' + QString::number(uids.last());
01913 
01914   if (!set.isEmpty()) sets.append(set);
01915 
01916   return sets;
01917 }
01918 
01919 //-----------------------------------------------------------------------------
01920 void KMFolderImap::getUids(QValueList<int>& ids, QValueList<ulong>& uids)
01921 {
01922   KMMsgBase *msg = 0;
01923   // get the uids
01924   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
01925   {
01926     msg = getMsgBase(*it);
01927     if (!msg) continue;
01928     uids.append(msg->UID());
01929   }
01930 }
01931 
01932 void KMFolderImap::getUids(const QPtrList<KMMessage>& msgList, QValueList<ulong>& uids)
01933 {
01934   KMMessage *msg = 0;
01935 
01936   QPtrListIterator<KMMessage> it( msgList );
01937   while ( (msg = it.current()) != 0 ) {
01938     ++it;
01939     if ( msg->UID() > 0 ) {
01940       uids.append( msg->UID() );
01941     }
01942   }
01943 }
01944 
01945 //-----------------------------------------------------------------------------
01946 void KMFolderImap::expungeFolder(KMFolderImap * aFolder, bool quiet)
01947 {
01948   aFolder->setNeedsCompacting(FALSE);
01949   KURL url = mAccount->getUrl();
01950   url.setPath(aFolder->imapPath() + ";UID=*");
01951   if ( mAccount->makeConnection() != ImapAccountBase::Connected )
01952     return;
01953   KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
01954   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
01955   ImapAccountBase::jobData jd( url.url(), 0 );
01956   jd.quiet = quiet;
01957   mAccount->insertJob(job, jd);
01958   connect(job, SIGNAL(result(KIO::Job *)),
01959           mAccount, SLOT(slotSimpleResult(KIO::Job *)));
01960 }
01961 
01962 //-----------------------------------------------------------------------------
01963 void KMFolderImap::slotProcessNewMail( int errorCode, const QString &errorMsg )
01964 {
01965   Q_UNUSED( errorMsg );
01966   disconnect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01967               this, SLOT( slotProcessNewMail(int, const QString&) ) );
01968   if ( !errorCode )
01969     processNewMail( false );
01970   else
01971     emit numUnreadMsgsChanged( folder() );
01972 }
01973 
01974 //-----------------------------------------------------------------------------
01975 bool KMFolderImap::processNewMail(bool)
01976 {
01977    // a little safety
01978   if ( !mAccount ) {
01979     kdDebug(5006) << "KMFolderImap::processNewMail - account is null!" << endl;
01980     return false;
01981   }
01982   if ( imapPath().isEmpty() ) {
01983     kdDebug(5006) << "KMFolderImap::processNewMail - imapPath of " << name() << " is empty!" << endl;
01984     // remove it locally
01985     setAlreadyRemoved( true );
01986     kmkernel->imapFolderMgr()->remove( folder() );
01987     return false;
01988   }
01989   // check the connection
01990   if ( mAccount->makeConnection() == ImapAccountBase::Error ) {
01991     kdDebug(5006) << "KMFolderImap::processNewMail - got no connection!" << endl;
01992     return false;
01993   } else if ( mAccount->makeConnection() == ImapAccountBase::Connecting )
01994   {
01995     // wait
01996     kdDebug(5006) << "KMFolderImap::processNewMail - waiting for connection: " << label() << endl;
01997     connect( mAccount, SIGNAL( connectionResult(int, const QString&) ),
01998         this, SLOT( slotProcessNewMail(int, const QString&) ) );
01999     return true;
02000   }
02001   KURL url = mAccount->getUrl();
02002   if (mReadOnly)
02003     url.setPath(imapPath() + ";SECTION=UIDNEXT");
02004   else
02005     url.setPath(imapPath() + ";SECTION=UNSEEN");
02006 
02007   mMailCheckProgressItem = ProgressManager::createProgressItem(
02008               "MailCheckAccount" + account()->name(),
02009               "MailCheck" + folder()->prettyURL(),
02010               QStyleSheet::escape( folder()->prettyURL() ),
02011               i18n("updating message counts"),
02012               false,
02013               account()->useSSL() || account()->useTLS() );
02014 
02015   KIO::SimpleJob *job = KIO::stat(url, FALSE);
02016   KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
02017   ImapAccountBase::jobData jd(url.url(), folder() );
02018   jd.cancellable = true;
02019   mAccount->insertJob(job, jd);
02020   connect(job, SIGNAL(result(KIO::Job *)),
02021           SLOT(slotStatResult(KIO::Job *)));
02022   return true;
02023 }
02024 
02025 
02026 //-----------------------------------------------------------------------------
02027 void KMFolderImap::slotStatResult(KIO::Job * job)
02028 {
02029   slotCompleteMailCheckProgress();
02030   ImapAccountBase::JobIterator it = mAccount->findJob(job);
02031   if ( it == mAccount->jobsEnd() ) return;
02032   mAccount->removeJob(it);
02033   if (job->error())
02034   {
02035     mAccount->handleJobError( job, i18n("Error while getting folder information.") );
02036   } else {
02037     KIO::UDSEntry uds = static_cast<KIO::StatJob*>(job)->statResult();
02038     for (KIO::UDSEntry::ConstIterator it = uds.begin(); it != uds.end(); it++)
02039     {
02040       if ((*it).m_uds == KIO::UDS_SIZE)
02041       {
02042         if (mReadOnly)
02043         {
02044           mGuessedUnreadMsgs = -1;
02045           mGuessedUnreadMsgs = countUnread() + (*it).m_long - lastUid() - 1;
02046           if (mGuessedUnreadMsgs < 0) mGuessedUnreadMsgs = 0;
02047         } else {
02048           mGuessedUnreadMsgs = (*it).m_long;
02049         }
02050       }
02051     }
02052   }
02053 }
02054 
02055 //-----------------------------------------------------------------------------
02056 int KMFolderImap::create()
02057 {
02058   readConfig();
02059   mUnreadMsgs = -1;
02060   return KMFolderMbox::create();
02061 }
02062 
02063 QValueList<ulong> KMFolderImap::splitSets(const QString uids)
02064 {
02065   QValueList<ulong> uidlist;
02066 
02067   // ex: 1205,1204,1203,1202,1236:1238
02068   QString buffer = QString::null;
02069   int setstart = -1;
02070   // iterate over the uids
02071   for (uint i = 0; i < uids.length(); i++)
02072   {
02073     QChar chr = uids[i];
02074     if (chr == ',')
02075     {
02076       if (setstart > -1)
02077       {
02078         // a range (uid:uid) was before
02079         for (int j = setstart; j <= buffer.toInt(); j++)
02080         {
02081           uidlist.append(j);
02082         }
02083         setstart = -1;
02084       } else {
02085         // single uid
02086         uidlist.append(buffer.toInt());
02087       }
02088       buffer = "";
02089     } else if (chr == ':') {
02090       // remember the start of the range
02091       setstart = buffer.toInt();
02092       buffer = "";
02093     } else if (chr.category() == QChar::Number_DecimalDigit) {
02094       // digit
02095       buffer += chr;
02096     } else {
02097       // ignore
02098     }
02099   }
02100   // process the last data
02101   if (setstart > -1)
02102   {
02103     for (int j = setstart; j <= buffer.toInt(); j++)
02104     {
02105       uidlist.append(j);
02106     }
02107   } else {
02108     uidlist.append(buffer.toInt());
02109   }
02110 
02111   return uidlist;
02112 }
02113 
02114 //-----------------------------------------------------------------------------
02115 int KMFolderImap::expungeContents()
02116 {
02117   // nuke the local cache
02118   int rc = KMFolderMbox::expungeContents();
02119 
02120   // set the deleted flag for all messages in the folder
02121   KURL url = mAccount->getUrl();
02122   url.setPath( imapPath() + ";UID=1:*");
02123   if ( mAccount->makeConnection() == ImapAccountBase::Connected ) 
02124   {
02125     KIO::SimpleJob *job = KIO::file_delete(url, FALSE);
02126     KIO::Scheduler::assignJobToSlave(mAccount->slave(), job);
02127     ImapAccountBase::jobData jd( url.url(), 0 );
02128     jd.quiet = true;
02129     mAccount->insertJob(job, jd);
02130     connect(job, SIGNAL(result(KIO::Job *)),
02131             mAccount, SLOT(slotSimpleResult(KIO::Job *)));
02132   }
02133   /* Is the below correct? If we are expunging (in the folder sense, not the imap sense),
02134      why delete but not (imap-)expunge? Since the folder is not active there is no concept
02135      of "leaving the folder", so the setting really has little to do with it. */
02136   // if ( autoExpunge() )
02137     expungeFolder(this, true);
02138   getFolder();
02139 
02140   return rc;
02141 }
02142 
02143 //-----------------------------------------------------------------------------
02144 void
02145 KMFolderImap::setUserRights( unsigned int userRights )
02146 {
02147   mUserRights = userRights;
02148   kdDebug(5006) << imapPath() << " setUserRights: " << userRights << endl;
02149 }
02150 
02151 //-----------------------------------------------------------------------------
02152 void KMFolderImap::slotCompleteMailCheckProgress()
02153 {
02154   if ( mMailCheckProgressItem ) {
02155     mMailCheckProgressItem->setComplete();
02156     mMailCheckProgressItem = 0;
02157     emit numUnreadMsgsChanged( folder() );
02158   }
02159 }
02160 
02161 //-----------------------------------------------------------------------------
02162 void KMFolderImap::setSubfolderState( imapState state )
02163 {
02164   mSubfolderState = state;
02165   if ( state == imapNoInformation && folder()->child() )
02166   {
02167     // pass through to children
02168     KMFolderNode* node;
02169     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02170     for ( ; (node = it.current()); )
02171     {
02172       ++it;
02173       if (node->isDir()) continue;
02174       KMFolder *folder = static_cast<KMFolder*>(node);
02175       static_cast<KMFolderImap*>(folder->storage())->setSubfolderState( state );
02176     }
02177   }
02178 }
02179 
02180 //-----------------------------------------------------------------------------
02181 void KMFolderImap::setIncludeInMailCheck( bool check )
02182 {
02183   bool changed = ( mCheckMail != check );
02184   mCheckMail = check;
02185   if ( changed )
02186     account()->slotUpdateFolderList();
02187 }
02188 
02189 //-----------------------------------------------------------------------------
02190 void KMFolderImap::setAlreadyRemoved( bool removed )
02191 {
02192   mAlreadyRemoved = removed;
02193   if ( folder()->child() )
02194   {
02195     // pass through to childs
02196     KMFolderNode* node;
02197     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02198     for ( ; (node = it.current()); )
02199     {
02200       ++it;
02201       if (node->isDir()) continue;
02202       KMFolder *folder = static_cast<KMFolder*>(node);
02203       static_cast<KMFolderImap*>(folder->storage())->setAlreadyRemoved( removed );
02204     }
02205   }
02206 }
02207 
02208 void KMFolderImap::slotCreatePendingFolders( int errorCode, const QString& errorMsg )
02209 {
02210   Q_UNUSED( errorMsg );
02211   disconnect( mAccount, SIGNAL( connectionResult( int, const QString& ) ),
02212               this, SLOT( slotCreatePendingFolders( int, const QString& ) ) );
02213   if ( !errorCode ) {
02214     QStringList::Iterator it = mFoldersPendingCreation.begin();
02215     for ( ; it != mFoldersPendingCreation.end(); ++it ) {
02216       createFolder( *it );
02217     }
02218   }
02219   mFoldersPendingCreation.clear();  
02220 }
02221 
02222 //-----------------------------------------------------------------------------
02223 void KMFolderImap::search( const KMSearchPattern* pattern )
02224 {
02225   if ( !pattern || pattern->isEmpty() )
02226   {
02227     // not much to do here
02228     QValueList<Q_UINT32> serNums;
02229     emit searchResult( folder(), serNums, pattern, true );
02230     return;
02231   }
02232   SearchJob* job = new SearchJob( this, mAccount, pattern );
02233   connect( job, SIGNAL( searchDone( QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ),
02234            this, SLOT( slotSearchDone( QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ) );
02235   job->start();
02236 }
02237 
02238 //-----------------------------------------------------------------------------
02239 void KMFolderImap::slotSearchDone( QValueList<Q_UINT32> serNums,
02240                                    const KMSearchPattern* pattern,
02241                                    bool complete )
02242 {
02243   emit searchResult( folder(), serNums, pattern, complete );
02244 }
02245 
02246 //-----------------------------------------------------------------------------
02247 void KMFolderImap::search( const KMSearchPattern* pattern, Q_UINT32 serNum )
02248 {
02249   if ( !pattern || pattern->isEmpty() )
02250   {
02251     // not much to do here
02252     emit searchDone( folder(), serNum, pattern, false );
02253     return;
02254   }
02255   SearchJob* job = new SearchJob( this, mAccount, pattern, serNum );
02256   connect( job, SIGNAL( searchDone( Q_UINT32, const KMSearchPattern*, bool ) ),
02257            this, SLOT( slotSearchDone( Q_UINT32, const KMSearchPattern*, bool ) ) );
02258   job->start();
02259 }
02260 
02261 //-----------------------------------------------------------------------------
02262 void KMFolderImap::slotSearchDone( Q_UINT32 serNum, const KMSearchPattern* pattern,
02263                                    bool matches )
02264 {
02265   emit searchDone( folder(), serNum, pattern, matches );
02266 }
02267 
02268 //-----------------------------------------------------------------------------
02269 bool KMFolderImap::isMoveable() const
02270 {
02271   return ( hasChildren() == HasNoChildren && 
02272       !folder()->isSystemFolder() ) ? true : false;
02273 }
02274  
02275 //-----------------------------------------------------------------------------
02276 const ulong KMFolderImap::serNumForUID( ulong uid )
02277 {
02278   if ( mUidMetaDataMap.find( uid ) ) {
02279     KMMsgMetaData *md = mUidMetaDataMap[uid];
02280     return md->serNum();
02281   } else {
02282     kdDebug(5006) << "serNumForUID: unknown uid " << uid << endl;
02283     return 0;
02284   }
02285 }
02286 
02287 //-----------------------------------------------------------------------------
02288 void KMFolderImap::saveMsgMetaData( KMMessage* msg, ulong uid )
02289 {
02290   if ( uid == 0 ) {
02291     uid = msg->UID();
02292   }
02293   ulong serNum = msg->getMsgSerNum();
02294   mUidMetaDataMap.replace( uid, new KMMsgMetaData(msg->status(), serNum) );
02295 }
02296 
02297 //-----------------------------------------------------------------------------
02298 void KMFolderImap::setImapPath( const QString& path )
02299 {
02300   if ( path.isEmpty() ) {
02301     kdWarning(5006) << k_funcinfo << "ignoring empty path" << endl;
02302   } else {
02303     mImapPath = path;
02304   }
02305 }
02306 
02307 void KMFolderImap::finishMailCheck( imapState state )
02308 {
02309   quiet( false );
02310   mContentState = state;
02311   emit folderComplete( this, mContentState == imapFinished );
02312   close();
02313 }
02314 
02315 #include "kmfolderimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys