kmail

kmfolderimap.cpp

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