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