kmail

kmfolderimap.cpp

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