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