kmail

kmfolderimap.cpp

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