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