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