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