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 || !GlobalSettings::allowLocalFlags() ) {
01306           int supportedFlags = mUploadAllFlags ? 31 : mPermanentFlags;
01307           if ( mReadOnly )
01308             supportedFlags = INT_MAX;
01309           flagsToStatus( msgBase, serverFlags, false, supportedFlags );
01310         } else
01311           seenFlagToStatus( msgBase, serverFlags, false );
01312         idx++;
01313         uid = (*it).items.remove(uid);
01314         if ( msgBase->getMsgSerNum() > 0 ) {
01315           saveMsgMetaData( static_cast<KMMessage*>(msgBase) );
01316         }
01317       }
01318       else break;  // happens only, if deleted mails reappear on the server
01319     }
01320     // remove all remaining entries in the local cache, they are no longer
01321     // present on the server
01322     while (idx < count()) removeMsg(idx, true);
01323   }
01324   // strip the flags from the list of uids, so it can be reused
01325   for (uid = (*it).items.begin(); uid != (*it).items.end(); ++uid)
01326     (*uid).truncate((*uid).find(","));
01327   ImapAccountBase::jobData jd( QString::null, (*it).parent );
01328   jd.total = (*it).items.count();
01329   if (jd.total == 0)
01330   {
01331     finishMailCheck( "listfolder", imapFinished );
01332     account()->removeJob(it);
01333     return;
01334   }
01335   if ( mMailCheckProgressItem )
01336   {
01337     // next step for the progressitem
01338     mMailCheckProgressItem->setCompletedItems( 0 );
01339     mMailCheckProgressItem->setTotalItems( jd.total );
01340     mMailCheckProgressItem->setProgress( 0 );
01341     mMailCheckProgressItem->setStatus( i18n("Retrieving messages") );
01342   }
01343 
01344   QStringList sets;
01345   uid = (*it).items.begin();
01346   if (jd.total == 1) sets.append(*uid + ":" + *uid);
01347   else sets = makeSets( (*it).items );
01348   account()->removeJob(it); // don't use *it below
01349 
01350   // Now kick off the getting of envelopes for the new mails in the folder
01351   for (QStringList::Iterator i = sets.begin(); i != sets.end(); ++i)
01352   {
01353     mContentState = imapDownloadInProgress;
01354     KURL url = account()->getUrl();
01355     url.setPath(imapPath() + ";UID=" + *i + ";SECTION=ENVELOPE");
01356     KIO::SimpleJob *newJob = KIO::get(url, false, false);
01357     jd.url = url.url();
01358     KIO::Scheduler::assignJobToSlave(account()->slave(), newJob);
01359     account()->insertJob(newJob, jd);
01360     connect(newJob, SIGNAL(result(KIO::Job *)),
01361         this, (i == sets.at(sets.count() - 1))
01362         ? SLOT(slotGetLastMessagesResult(KIO::Job *))
01363         : SLOT(slotGetMessagesResult(KIO::Job *)));
01364     connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)),
01365         this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &)));
01366   }
01367 }
01368 
01369 
01370 //-----------------------------------------------------------------------------
01371 void KMFolderImap::slotListFolderEntries(KIO::Job * job,
01372   const KIO::UDSEntryList & uds)
01373 {
01374   ImapAccountBase::JobIterator it = account()->findJob(job);
01375   if ( it == account()->jobsEnd() ) return;
01376   QString mimeType, name;
01377   long int flags = 0;
01378   for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
01379     udsIt != uds.end(); udsIt++)
01380   {
01381     for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
01382       eIt != (*udsIt).end(); eIt++)
01383     {
01384       if ((*eIt).m_uds == KIO::UDS_NAME)
01385         name = (*eIt).m_str;
01386       else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
01387         mimeType = (*eIt).m_str;
01388       else if ((*eIt).m_uds == KIO::UDS_ACCESS)
01389         flags = (*eIt).m_long;
01390     }
01391     if ((mimeType == "message/rfc822-imap" || mimeType == "message/rfc822") &&
01392         !(flags & 8)) {
01393       (*it).items.append(name + "," + QString::number(flags));
01394       if ( mMailCheckProgressItem ) {
01395         mMailCheckProgressItem->incCompletedItems();
01396         mMailCheckProgressItem->updateProgress();
01397       }
01398     }
01399   }
01400 }
01401 
01402 
01403 // debugging helper
01404 //X static QString flagsToString( int flags )
01405 //X {
01406 //X     QString str("(");
01407 //X     if ( flags & 4 ) {
01408 //X         str += "\\Flagged ";
01409 //X     }
01410 //X     if ( flags & 2 ) {
01411 //X         str += "\\Answered ";
01412 //X     }
01413 //X     if ( flags & 1 ) {
01414 //X         str += "\\Seen";
01415 //X     }
01416 //X     str += ")";
01417 //X     return str;
01418 //X }
01419 
01420 //-----------------------------------------------------------------------------
01421 void KMFolderImap::flagsToStatus(KMMsgBase *msg, int flags, bool newMsg, int supportedFlags )
01422 {
01423   if ( !msg ) return;
01424 
01425   // see imap4/imapinfo.h for the magic numbers
01426   static const struct {
01427     const int imapFlag;
01428     const int kmFlag;
01429     const bool standardFlag;
01430   } imapFlagMap[] = {
01431     { 2, KMMsgStatusReplied, true },
01432     { 4, KMMsgStatusFlag, true },
01433     { 128, KMMsgStatusForwarded, false },
01434     { 256, KMMsgStatusTodo, false },
01435     { 512, KMMsgStatusWatched, false },
01436     { 1024, KMMsgStatusIgnored, false }
01437   };
01438   static const int numFlags = sizeof imapFlagMap / sizeof *imapFlagMap;
01439 
01440   const KMMsgStatus oldStatus = msg->status();
01441   for ( int i = 0; i < numFlags; ++i ) {
01442     if ( ( (supportedFlags & imapFlagMap[i].imapFlag) == 0 && (supportedFlags & 64) == 0 )
01443          && !imapFlagMap[i].standardFlag ) {
01444       continue;
01445     }
01446     if ( ((flags & imapFlagMap[i].imapFlag) > 0) != ((oldStatus & imapFlagMap[i].kmFlag) > 0) ) {
01447       msg->toggleStatus( imapFlagMap[i].kmFlag );
01448     }
01449   }
01450 
01451   seenFlagToStatus( msg, flags, newMsg );
01452 }
01453 
01454 void KMFolderImap::seenFlagToStatus(KMMsgBase * msg, int flags, bool newMsg)
01455 {
01456   if ( !msg ) return;
01457 
01458   const KMMsgStatus oldStatus = msg->status();
01459   if ( (flags & 1) && (oldStatus & KMMsgStatusOld) == 0 )
01460     msg->setStatus( KMMsgStatusOld );
01461 
01462   // In case the message does not have the seen flag set, override our local
01463   // notion that it is read. Otherwise the count of unread messages and the
01464   // number of messages which actually show up as read can go out of sync.
01465   if ( msg->isOfUnknownStatus() || (!(flags&1) && !(oldStatus&(KMMsgStatusNew|KMMsgStatusUnread)) ) ) {
01466     if (newMsg) {
01467       if ( (oldStatus & KMMsgStatusNew) == 0 )
01468         msg->setStatus( KMMsgStatusNew );
01469     } else {
01470       if ( (oldStatus & KMMsgStatusUnread) == 0 )
01471         msg->setStatus( KMMsgStatusUnread );
01472     }
01473   }
01474 }
01475 
01476 
01477 //-----------------------------------------------------------------------------
01478 QString KMFolderImap::statusToFlags(KMMsgStatus status, int supportedFlags)
01479 {
01480   QString flags;
01481   if (status & KMMsgStatusDeleted)
01482     flags = "\\DELETED";
01483   else {
01484     if (status & KMMsgStatusOld || status & KMMsgStatusRead)
01485       flags = "\\SEEN ";
01486     if (status & KMMsgStatusReplied)
01487       flags += "\\ANSWERED ";
01488     if (status & KMMsgStatusFlag)
01489       flags += "\\FLAGGED ";
01490     // non standard flags
01491     if ( (status & KMMsgStatusForwarded) && ((supportedFlags & 64) || (supportedFlags & 128)) )
01492       flags += "$FORWARDED ";
01493     if ( (status & KMMsgStatusTodo) && ((supportedFlags & 64) || (supportedFlags & 256)) )
01494       flags += "$TODO ";
01495     if ( (status & KMMsgStatusWatched) && ((supportedFlags & 64) || (supportedFlags & 512)) )
01496       flags += "$WATCHED ";
01497     if ( (status & KMMsgStatusIgnored) && ((supportedFlags & 64) || (supportedFlags & 1024)) )
01498       flags += "$IGNORED ";
01499   }
01500 
01501   return flags.simplifyWhiteSpace();
01502 }
01503 
01504 //-------------------------------------------------------------
01505 void
01506 KMFolderImap::ignoreJobsForMessage( KMMessage* msg )
01507 {
01508   if ( !msg || msg->transferInProgress() ||
01509        !msg->parent() || msg->parent()->folderType() != KMFolderTypeImap )
01510     return;
01511   KMAcctImap *account;
01512   if ( !(account = static_cast<KMFolderImap*>(msg->storage())->account()) )
01513     return;
01514 
01515   account->ignoreJobsForMessage( msg );
01516 }
01517 
01518 //-----------------------------------------------------------------------------
01519 void KMFolderImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data)
01520 {
01521   if ( data.isEmpty() ) return; // optimization
01522   ImapAccountBase::JobIterator it = account()->findJob(job);
01523   if ( it == account()->jobsEnd() ) return;
01524   (*it).cdata += QCString(data, data.size() + 1);
01525   int pos = (*it).cdata.find("\r\n--IMAPDIGEST");
01526   if ( pos == -1 ) {
01527     // if we do not find the pattern in the complete string we will not find
01528     // it in a substring.
01529     return;
01530   }
01531   if (pos > 0)
01532   {
01533     int p = (*it).cdata.find("\r\nX-uidValidity:");
01534     if (p != -1) setUidValidity((*it).cdata
01535       .mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17));
01536     int c = (*it).cdata.find("\r\nX-Count:");
01537     if ( c != -1 )
01538     {
01539       bool ok;
01540       int exists = (*it).cdata.mid( c+10,
01541           (*it).cdata.find("\r\n", c+1) - c-10 ).toInt(&ok);
01542       if ( ok && exists < count() ) {
01543         kdDebug(5006) << "KMFolderImap::slotGetMessagesData - server has less messages (" <<
01544           exists << ") then folder (" << count() << "), so reload" << endl;
01545         open("getMessage");
01546         reallyGetFolder( QString::null );
01547         (*it).cdata.remove(0, pos);
01548         return;
01549       } else if ( ok ) {
01550         int delta = exists - count();
01551         if ( mMailCheckProgressItem ) {
01552           mMailCheckProgressItem->setTotalItems( delta );
01553         }
01554       }
01555     }
01556     (*it).cdata.remove(0, pos);
01557   }
01558   pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01559   int flags;
01560   while (pos >= 0)
01561   {
01562     KMMessage *msg = new KMMessage;
01563     msg->setComplete( false );
01564     msg->setReadyToShow( false );
01565     // nothing between the boundaries, older UWs do that
01566     if ( pos != 14 ) {
01567       msg->fromString( (*it).cdata.mid(16, pos - 16) );
01568       flags = msg->headerField("X-Flags").toInt();
01569       ulong uid = msg->UID();
01570       KMMsgMetaData *md =  0;
01571       if ( mUidMetaDataMap.find( uid ) ) {
01572           md =  mUidMetaDataMap[uid];
01573       }
01574       ulong serNum = 0;
01575       if ( md ) {
01576         serNum = md->serNum();
01577       }
01578       bool ok = true;
01579       if ( uid <= lastUid() && serNum > 0 ) {
01580         // the UID is already known so no need to create it
01581         ok = false;
01582       }
01583       // deleted flag
01584       if ( flags & 8 )
01585         ok = false;
01586       if ( !ok ) {
01587         delete msg;
01588         msg = 0;
01589       } else {
01590         if ( serNum > 0 ) {
01591           // assign the sernum from the cache
01592           msg->setMsgSerNum( serNum );
01593         }
01594         // Transfer the status, if it is cached.
01595         if ( md ) {
01596           msg->setStatus( md->status() );
01597         } else if ( !account()->hasCapability("uidplus") ) {
01598           // see if we have cached the msgIdMD5 and get the status +
01599           // serial number from there
01600           QString id = msg->msgIdMD5();
01601           if ( mMetaDataMap.find( id ) ) {
01602             md =  mMetaDataMap[id];
01603             msg->setStatus( md->status() );
01604             if ( md->serNum() != 0 && serNum == 0 ) {
01605               msg->setMsgSerNum( md->serNum() );
01606             }
01607             mMetaDataMap.remove( id );
01608             delete md;
01609           }
01610         }
01611         KMFolderMbox::addMsg(msg, 0);
01612         // Merge with the flags from the server.
01613         flagsToStatus((KMMsgBase*)msg, flags, true, mUploadAllFlags ? 31 : mPermanentFlags);
01614         // set the correct size
01615         msg->setMsgSizeServer( msg->headerField("X-Length").toUInt() );
01616         msg->setUID(uid);
01617         if ( msg->getMsgSerNum() > 0 ) {
01618           saveMsgMetaData( msg );
01619         }
01620         // Filter messages that have arrived in the inbox folder
01621         if ( folder()->isSystemFolder() && imapPath() == "/INBOX/"
01622             && kmkernel->filterMgr()->atLeastOneIncomingFilterAppliesTo( account()->id() ) )
01623             account()->execFilters( msg->getMsgSerNum() );
01624 
01625         if ( count() > 1 ) {
01626           unGetMsg(count() - 1);
01627         }
01628         mLastUid = uid;
01629         if ( mMailCheckProgressItem ) {
01630           mMailCheckProgressItem->incCompletedItems();
01631           mMailCheckProgressItem->updateProgress();
01632         }
01633       }
01634     }
01635     (*it).cdata.remove(0, pos);
01636     (*it).done++;
01637     pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1);
01638   } // while
01639 }
01640 
01641 //-------------------------------------------------------------
01642 FolderJob*
01643 KMFolderImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt,
01644                            KMFolder *folder, QString partSpecifier,
01645                            const AttachmentStrategy *as ) const
01646 {
01647   KMFolderImap* kmfi = folder? dynamic_cast<KMFolderImap*>(folder->storage()) : 0;
01648   if ( jt == FolderJob::tGetMessage && partSpecifier == "STRUCTURE" &&
01649        account() && account()->loadOnDemand() &&
01650        ( msg->msgSizeServer() > 5000 || msg->msgSizeServer() == 0 ) &&
01651        ( msg->signatureState() == KMMsgNotSigned ||
01652          msg->signatureState() == KMMsgSignatureStateUnknown ) &&
01653        ( msg->encryptionState() == KMMsgNotEncrypted ||
01654          msg->encryptionState() == KMMsgEncryptionStateUnknown ) )
01655   {
01656     // load-on-demand: retrieve the BODYSTRUCTURE and to speed things up also the headers
01657     // this is not activated for small or signed messages
01658     ImapJob *job = new ImapJob( msg, jt, kmfi, "HEADER" );
01659     job->start();
01660     ImapJob *job2 = new ImapJob( msg, jt, kmfi, "STRUCTURE", as );
01661     job2->start();
01662     job->setParentFolder( this );
01663     return job;
01664   } else {
01665     // download complete message or part (attachment)
01666     if ( partSpecifier == "STRUCTURE" ) // hide from outside
01667       partSpecifier = QString::null;
01668 
01669     ImapJob *job = new ImapJob( msg, jt, kmfi, partSpecifier );
01670     job->setParentFolder( this );
01671     return job;
01672   }
01673 }
01674 
01675 //-------------------------------------------------------------
01676 FolderJob*
01677 KMFolderImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets,
01678                            FolderJob::JobType jt, KMFolder *folder ) const
01679 {
01680   KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder->storage());
01681   ImapJob *job = new ImapJob( msgList, sets, jt, kmfi );
01682   job->setParentFolder( this );
01683   return job;
01684 }
01685 
01686 //-----------------------------------------------------------------------------
01687 void KMFolderImap::getMessagesResult(KIO::Job * job, bool lastSet)
01688 {
01689   ImapAccountBase::JobIterator it = account()->findJob(job);
01690   if ( it == account()->jobsEnd() ) return;
01691   if (job->error()) {
01692     account()->handleJobError( job, i18n("Error while retrieving messages.") );
01693     finishMailCheck( "getMessage", imapNoInformation );
01694     return;
01695   }
01696   if (lastSet) {
01697     finishMailCheck( "getMessage", imapFinished );
01698     account()->removeJob(it);
01699   }
01700 }
01701 
01702 
01703 //-----------------------------------------------------------------------------
01704 void KMFolderImap::slotGetLastMessagesResult(KIO::Job * job)
01705 {
01706   getMessagesResult(job, true);
01707 }
01708 
01709 
01710 //-----------------------------------------------------------------------------
01711 void KMFolderImap::slotGetMessagesResult(KIO::Job * job)
01712 {
01713   getMessagesResult(job, false);
01714 }
01715 
01716 
01717 //-----------------------------------------------------------------------------
01718 void KMFolderImap::createFolder(const QString &name, const QString& parentPath,
01719                                 bool askUser)
01720 {
01721   kdDebug(5006) << "KMFolderImap::createFolder - name=" << name << ",parent=" <<
01722     parentPath << ",askUser=" << askUser << endl;
01723   if ( account()->makeConnection() != ImapAccountBase::Connected ) {
01724     kdWarning(5006) << "KMFolderImap::createFolder - got no connection" << endl;
01725     return;
01726   }
01727   KURL url = account()->getUrl();
01728   QString parent = ( parentPath.isEmpty() ? imapPath() : parentPath );
01729   QString path = account()->createImapPath( parent, name );
01730   if ( askUser ) {
01731     path += "/;INFO=ASKUSER";
01732   }
01733   url.setPath( path );
01734 
01735   KIO::SimpleJob *job = KIO::mkdir(url);
01736   KIO::Scheduler::assignJobToSlave(account()->slave(), job);
01737   ImapAccountBase::jobData jd( url.url(), folder() );
01738   jd.items = name;
01739   account()->insertJob(job, jd);
01740   connect(job, SIGNAL(result(KIO::Job *)),
01741           this, SLOT(slotCreateFolderResult(KIO::Job *)));
01742 }
01743 
01744 
01745 //-----------------------------------------------------------------------------
01746 void KMFolderImap::slotCreateFolderResult(KIO::Job * job)
01747 {
01748   ImapAccountBase::JobIterator it = account()->findJob(job);
01749   if ( it == account()->jobsEnd() ) return;
01750 
01751   QString name;
01752   if ( it.data().items.count() > 0 )
01753     name = it.data().items.first();
01754 
01755   if (job->error())
01756   {
01757     if ( job->error() == KIO::ERR_COULD_NOT_MKDIR ) {
01758       // Creating a folder failed, remove it from the tree.
01759       account()->listDirectory( );
01760     }
01761     account()->handleJobError( job, i18n("Error while creating a folder.") );
01762     emit folderCreationResult( name, false );
01763   } else {
01764     listDirectory();
01765     account()->removeJob(job);
01766     emit folderCreationResult( name, true );
01767   }
01768 }
01769 
01770 
01771 //-----------------------------------------------------------------------------
01772 static QTextCodec *sUtf7Codec = 0;
01773 
01774 QTextCodec * KMFolderImap::utf7Codec()
01775 {
01776   if (!sUtf7Codec) sUtf7Codec = QTextCodec::codecForName("utf-7");
01777   return sUtf7Codec;
01778 }
01779 
01780 
01781 //-----------------------------------------------------------------------------
01782 QString KMFolderImap::encodeFileName(const QString &name)
01783 {
01784   QString result = utf7Codec()->fromUnicode(name);
01785   return KURL::encode_string_no_slash(result);
01786 }
01787 
01788 
01789 //-----------------------------------------------------------------------------
01790 QString KMFolderImap::decodeFileName(const QString &name)
01791 {
01792   QString result = KURL::decode_string(name);
01793   return utf7Codec()->toUnicode(result.latin1());
01794 }
01795 
01796 //-----------------------------------------------------------------------------
01797 bool KMFolderImap::autoExpunge()
01798 {
01799   if (account())
01800     return account()->autoExpunge();
01801 
01802   return false;
01803 }
01804 
01805 
01806 //-----------------------------------------------------------------------------
01807 void KMFolderImap::slotSimpleData(KIO::Job * job, const QByteArray & data)
01808 {
01809   if ( data.isEmpty() ) return; // optimization
01810   ImapAccountBase::JobIterator it = account()->findJob(job);
01811   if ( it == account()->jobsEnd() ) return;
01812   QBuffer buff((*it).data);
01813   buff.open(IO_WriteOnly | IO_Append);
01814   buff.writeBlock(data.data(), data.size());
01815   buff.close();
01816 }
01817 
01818 //-----------------------------------------------------------------------------
01819 void KMFolderImap::deleteMessage(KMMessage * msg)
01820 {
01821   mUidMetaDataMap.remove( msg->UID() );
01822   mMetaDataMap.remove( msg->msgIdMD5() );
01823   KURL url = account()->getUrl();
01824   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msg->storage());
01825   ulong uid = msg->UID();
01826   /* If the uid is empty the delete job below will nuke all mail in the
01827      folder, so we better safeguard against that. See ::expungeFolder, as
01828      to why. :( */
01829   if ( uid == 0 ) {
01830      kdDebug( 5006 ) << "KMFolderImap::deleteMessage: Attempt to delete "
01831                         "an empty UID. Aborting."  << endl;
01832      return;
01833   }
01834   url.setPath(msg_parent->imapPath() + ";UID=" + QString::number(uid) );
01835   if ( account()->makeConnection() != ImapAccountBase::Connected )
01836     return;
01837   KIO::SimpleJob *job = KIO::file_delete(url, false);
01838   KIO::Scheduler::assignJobToSlave(account()->slave(), job);
01839   ImapAccountBase::jobData jd( url.url(), 0 );
01840   account()->insertJob(job, jd);
01841   connect(job, SIGNAL(result(KIO::Job *)),
01842           account(), SLOT(slotSimpleResult(KIO::Job *)));
01843 }
01844 
01845 void KMFolderImap::deleteMessage(const QPtrList<KMMessage>& msgList)
01846 {
01847   QPtrListIterator<KMMessage> it( msgList );
01848   KMMessage *msg;
01849   while ( (msg = it.current()) != 0 ) {
01850     ++it;
01851     mUidMetaDataMap.remove( msg->UID() );
01852     mMetaDataMap.remove( msg->msgIdMD5() );
01853   }
01854 
01855   QValueList<ulong> uids;
01856   getUids(msgList, uids);
01857   QStringList sets = makeSets(uids);
01858 
01859   KURL url = account()->getUrl();
01860   KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msgList.getFirst()->storage());
01861   for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it )
01862   {
01863     QString uid = *it;
01864     // Don't delete with no uid, that nukes the folder. Should not happen, but
01865     // better safe than sorry.
01866     if ( uid.isEmpty() ) continue;
01867     url.setPath(msg_parent->imapPath() + ";UID=" + uid);
01868     if ( account()->makeConnection() != ImapAccountBase::Connected )
01869       return;
01870     KIO::SimpleJob *job = KIO::file_delete(url, false);
01871     KIO::Scheduler::assignJobToSlave(account()->slave(), job);
01872     ImapAccountBase::jobData jd( url.url(), 0 );
01873     account()->insertJob(job, jd);
01874     connect(job, SIGNAL(result(KIO::Job *)),
01875         account(), SLOT(slotSimpleResult(KIO::Job *)));
01876   }
01877 }
01878 
01879 //-----------------------------------------------------------------------------
01880 void KMFolderImap::setStatus(int idx, KMMsgStatus status, bool toggle)
01881 {
01882   QValueList<int> ids; ids.append(idx);
01883   setStatus(ids, status, toggle);
01884 }
01885 
01886 void KMFolderImap::setStatus(QValueList<int>& _ids, KMMsgStatus status, bool toggle)
01887 {
01888   FolderStorage::setStatus(_ids, status, toggle);
01889   QValueList<int> ids;
01890   if ( mUploadAllFlags ) {
01891     kdDebug(5006) << k_funcinfo << "Migrating all flags to the server" << endl;
01892     ids.clear();
01893     for ( int i = 0; i < count(); ++i )
01894       ids << i;
01895     mUploadAllFlags = false;
01896   } else {
01897     ids = _ids;
01898   }
01899 
01900   /* The status has been already set in the local index. Update the flags on
01901    * the server. To avoid doing that for each message individually, group them
01902    * by the status string they will be assigned and make sets for each of those
01903    * groups of mails. This is necessary because the imap kio_slave status job
01904    * does not append flags but overwrites them. Example:
01905    *
01906    * 2 important mails and 3 unimportant mail, all unread. Mark all as read calls
01907    * this method with a list of uids. The 2 important mails need to get the string
01908    * \SEEN \FLAGGED while the others need to get just \SEEN. Build sets for each
01909    * of those and sort them, so the server can handle them efficiently. */
01910 
01911   if ( mReadOnly ) { // mUserRights is not available here
01912     // FIXME duplicated code in KMFolderCachedImap
01913     QValueList<ulong> seenUids, unseenUids;
01914     for ( QValueList<int>::ConstIterator it = ids.constBegin(); it != ids.constEnd(); ++it ) {
01915       KMMessage *msg = 0;
01916       bool unget = !isMessage(*it);
01917       msg = getMsg(*it);
01918       if (!msg) continue;
01919       if ( msg->status() & KMMsgStatusOld || msg->status() & KMMsgStatusRead )
01920         seenUids.append( msg->UID() );
01921       else
01922         unseenUids.append( msg->UID() );
01923       if (unget) unGetMsg(*it);
01924     }
01925     if ( !seenUids.isEmpty() ) {
01926       QStringList sets = KMFolderImap::makeSets( seenUids, true );
01927       for( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
01928         QString imappath = imapPath() + ";UID=" + ( *it );
01929         account()->setImapSeenStatus( folder(), imappath, true );
01930       }
01931     }
01932     if ( !unseenUids.isEmpty() ) {
01933       QStringList sets = KMFolderImap::makeSets( unseenUids, true );
01934       for( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) {
01935         QString imappath = imapPath() + ";UID=" + ( *it );
01936         account()->setImapSeenStatus( folder(), imappath, false );
01937       }
01938     }
01939     return;
01940   }
01941 
01942   QMap< QString, QStringList > groups;
01943   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) {
01944     KMMessage *msg = 0;
01945     bool unget = !isMessage(*it);
01946     msg = getMsg(*it);
01947     if (!msg) continue;
01948     QString flags = statusToFlags(msg->status(), mPermanentFlags);
01949     // Collect uids for each type of flags.
01950     groups[flags].append(QString::number(msg->UID()));
01951     if (unget) unGetMsg(*it);
01952   }
01953   QMapIterator< QString, QStringList > dit;
01954   for ( dit = groups.begin(); dit != groups.end(); ++dit ) {
01955      QCString flags = dit.key().latin1();
01956      QStringList sets = makeSets( (*dit), true );
01957      // Send off a status setting job for each set.
01958      for (  QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) {
01959        QString imappath = imapPath() + ";UID=" + ( *slit );
01960        account()->setImapStatus(folder(), imappath, flags);
01961      }
01962   }
01963   if ( mContentState == imapListingInProgress ) {
01964     // we're currently get'ing this folder
01965     // to make sure that we get the latest flags abort the current listing and
01966     // create a new one
01967     kdDebug(5006) << "Set status during folder listing, restarting listing." << endl;
01968     disconnect(this, SLOT(slotListFolderResult(KIO::Job *)));
01969     quiet( false );
01970     reallyGetFolder( QString::null );
01971   }
01972 }
01973 
01974 //-----------------------------------------------------------------------------
01975 QStringList KMFolderImap::makeSets(const QStringList& uids, bool sort)
01976 {
01977   QValueList<ulong> tmp;
01978   for ( QStringList::ConstIterator it = uids.begin(); it != uids.end(); ++it )
01979     tmp.append( (*it).toInt() );
01980   return makeSets(tmp, sort);
01981 }
01982 
01983 QStringList KMFolderImap::makeSets( QValueList<ulong>& uids, bool sort )
01984 {
01985   QStringList sets;
01986   QString set;
01987 
01988   if (uids.size() == 1)
01989   {
01990     sets.append(QString::number(uids.first()));
01991     return sets;
01992   }
01993 
01994   if (sort) qHeapSort(uids);
01995 
01996   ulong last = 0;
01997   // needed to make a uid like 124 instead of 124:124
01998   bool inserted = false;
01999   /* iterate over uids and build sets like 120:122,124,126:150 */
02000   for ( QValueList<ulong>::Iterator it = uids.begin(); it != uids.end(); ++it )
02001   {
02002     if (it == uids.begin() || set.isEmpty()) {
02003       set = QString::number(*it);
02004       inserted = true;
02005     } else
02006     {
02007       if (last+1 != *it)
02008       {
02009         // end this range
02010         if (inserted)
02011           set += ',' + QString::number(*it);
02012         else
02013           set += ':' + QString::number(last) + ',' + QString::number(*it);
02014         inserted = true;
02015         if (set.length() > 100)
02016         {
02017           // just in case the server has a problem with longer lines..
02018           sets.append(set);
02019           set = "";
02020         }
02021       } else {
02022         inserted = false;
02023       }
02024     }
02025     last = *it;
02026   }
02027   // last element
02028   if (!inserted)
02029     set += ':' + QString::number(uids.last());
02030 
02031   if (!set.isEmpty()) sets.append(set);
02032 
02033   return sets;
02034 }
02035 
02036 //-----------------------------------------------------------------------------
02037 void KMFolderImap::getUids(QValueList<int>& ids, QValueList<ulong>& uids)
02038 {
02039   KMMsgBase *msg = 0;
02040   // get the uids
02041   for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it )
02042   {
02043     msg = getMsgBase(*it);
02044     if (!msg) continue;
02045     uids.append(msg->UID());
02046   }
02047 }
02048 
02049 void KMFolderImap::getUids(const QPtrList<KMMessage>& msgList, QValueList<ulong>& uids)
02050 {
02051   KMMessage *msg = 0;
02052 
02053   QPtrListIterator<KMMessage> it( msgList );
02054   while ( (msg = it.current()) != 0 ) {
02055     ++it;
02056     if ( msg->UID() > 0 ) {
02057       uids.append( msg->UID() );
02058     }
02059   }
02060 }
02061 
02062 //-----------------------------------------------------------------------------
02063 void KMFolderImap::expungeFolder(KMFolderImap * aFolder, bool quiet)
02064 {
02065   aFolder->setNeedsCompacting(false);
02066   KURL url = account()->getUrl();
02067   url.setPath(aFolder->imapPath() + ";UID=*");
02068   if ( account()->makeConnection() != ImapAccountBase::Connected )
02069     return;
02070   KIO::SimpleJob *job = KIO::file_delete(url, false);
02071   KIO::Scheduler::assignJobToSlave(account()->slave(), job);
02072   ImapAccountBase::jobData jd( url.url(), 0 );
02073   jd.quiet = quiet;
02074   account()->insertJob(job, jd);
02075   connect(job, SIGNAL(result(KIO::Job *)),
02076           account(), SLOT(slotSimpleResult(KIO::Job *)));
02077 }
02078 
02079 //-----------------------------------------------------------------------------
02080 void KMFolderImap::slotProcessNewMail( int errorCode, const QString &errorMsg )
02081 {
02082   Q_UNUSED( errorMsg );
02083   disconnect( account(), SIGNAL( connectionResult(int, const QString&) ),
02084               this, SLOT( slotProcessNewMail(int, const QString&) ) );
02085   if ( !errorCode )
02086     processNewMail( false );
02087   else
02088     emit numUnreadMsgsChanged( folder() );
02089 }
02090 
02091 //-----------------------------------------------------------------------------
02092 bool KMFolderImap::processNewMail(bool)
02093 {
02094    // a little safety
02095   if ( !account() ) {
02096     kdDebug(5006) << "KMFolderImap::processNewMail - account is null!" << endl;
02097     return false;
02098   }
02099   if ( imapPath().isEmpty() ) {
02100     kdDebug(5006) << "KMFolderImap::processNewMail - imapPath of " << name() << " is empty!" << endl;
02101     // remove it locally
02102     setAlreadyRemoved( true );
02103     kmkernel->imapFolderMgr()->remove( folder() );
02104     return false;
02105   }
02106   // check the connection
02107   if ( account()->makeConnection() == ImapAccountBase::Error ) {
02108     kdDebug(5006) << "KMFolderImap::processNewMail - got no connection!" << endl;
02109     return false;
02110   } else if ( account()->makeConnection() == ImapAccountBase::Connecting )
02111   {
02112     // wait
02113     kdDebug(5006) << "KMFolderImap::processNewMail - waiting for connection: " << label() << endl;
02114     connect( account(), SIGNAL( connectionResult(int, const QString&) ),
02115         this, SLOT( slotProcessNewMail(int, const QString&) ) );
02116     return true;
02117   }
02118   KURL url = account()->getUrl();
02119   if (mReadOnly)
02120     url.setPath(imapPath() + ";SECTION=UIDNEXT");
02121   else
02122     url.setPath(imapPath() + ";SECTION=UNSEEN");
02123 
02124   mMailCheckProgressItem = ProgressManager::createProgressItem(
02125               "MailCheckAccount" + account()->name(),
02126               "MailCheck" + folder()->prettyURL(),
02127               QStyleSheet::escape( folder()->prettyURL() ),
02128               i18n("updating message counts"),
02129               false,
02130               account()->useSSL() || account()->useTLS() );
02131 
02132   KIO::SimpleJob *job = KIO::stat(url, false);
02133   KIO::Scheduler::assignJobToSlave(account()->slave(), job);
02134   ImapAccountBase::jobData jd(url.url(), folder() );
02135   jd.cancellable = true;
02136   account()->insertJob(job, jd);
02137   connect(job, SIGNAL(result(KIO::Job *)),
02138           SLOT(slotStatResult(KIO::Job *)));
02139   return true;
02140 }
02141 
02142 
02143 //-----------------------------------------------------------------------------
02144 void KMFolderImap::slotStatResult(KIO::Job * job)
02145 {
02146   slotCompleteMailCheckProgress();
02147   ImapAccountBase::JobIterator it = account()->findJob(job);
02148   if ( it == account()->jobsEnd() ) return;
02149   account()->removeJob(it);
02150   if (job->error())
02151   {
02152     account()->handleJobError( job, i18n("Error while getting folder information.") );
02153   } else {
02154     KIO::UDSEntry uds = static_cast<KIO::StatJob*>(job)->statResult();
02155     for (KIO::UDSEntry::ConstIterator it = uds.begin(); it != uds.end(); it++)
02156     {
02157       if ((*it).m_uds == KIO::UDS_SIZE)
02158       {
02159         if (mReadOnly)
02160         {
02161           mGuessedUnreadMsgs = -1;
02162           mGuessedUnreadMsgs = countUnread() + (*it).m_long - lastUid() - 1;
02163           if (mGuessedUnreadMsgs < 0) mGuessedUnreadMsgs = 0;
02164         } else {
02165           mGuessedUnreadMsgs = (*it).m_long;
02166         }
02167       }
02168     }
02169   }
02170 }
02171 
02172 //-----------------------------------------------------------------------------
02173 int KMFolderImap::create()
02174 {
02175   readConfig();
02176   mUnreadMsgs = -1;
02177   return KMFolderMbox::create();
02178 }
02179 
02180 QValueList<ulong> KMFolderImap::splitSets(const QString uids)
02181 {
02182   QValueList<ulong> uidlist;
02183 
02184   // ex: 1205,1204,1203,1202,1236:1238
02185   QString buffer = QString::null;
02186   int setstart = -1;
02187   // iterate over the uids
02188   for (uint i = 0; i < uids.length(); i++)
02189   {
02190     QChar chr = uids[i];
02191     if (chr == ',')
02192     {
02193       if (setstart > -1)
02194       {
02195         // a range (uid:uid) was before
02196         for (int j = setstart; j <= buffer.toInt(); j++)
02197         {
02198           uidlist.append(j);
02199         }
02200         setstart = -1;
02201       } else {
02202         // single uid
02203         uidlist.append(buffer.toInt());
02204       }
02205       buffer = "";
02206     } else if (chr == ':') {
02207       // remember the start of the range
02208       setstart = buffer.toInt();
02209       buffer = "";
02210     } else if (chr.category() == QChar::Number_DecimalDigit) {
02211       // digit
02212       buffer += chr;
02213     } else {
02214       // ignore
02215     }
02216   }
02217   // process the last data
02218   if (setstart > -1)
02219   {
02220     for (int j = setstart; j <= buffer.toInt(); j++)
02221     {
02222       uidlist.append(j);
02223     }
02224   } else {
02225     uidlist.append(buffer.toInt());
02226   }
02227 
02228   return uidlist;
02229 }
02230 
02231 //-----------------------------------------------------------------------------
02232 int KMFolderImap::expungeContents()
02233 {
02234   // nuke the local cache
02235   int rc = KMFolderMbox::expungeContents();
02236 
02237   // set the deleted flag for all messages in the folder
02238   KURL url = account()->getUrl();
02239   url.setPath( imapPath() + ";UID=1:*");
02240   if ( account()->makeConnection() == ImapAccountBase::Connected )
02241   {
02242     KIO::SimpleJob *job = KIO::file_delete(url, false);
02243     KIO::Scheduler::assignJobToSlave(account()->slave(), job);
02244     ImapAccountBase::jobData jd( url.url(), 0 );
02245     jd.quiet = true;
02246     account()->insertJob(job, jd);
02247     connect(job, SIGNAL(result(KIO::Job *)),
02248             account(), SLOT(slotSimpleResult(KIO::Job *)));
02249   }
02250   /* Is the below correct? If we are expunging (in the folder sense, not the imap sense),
02251      why delete but not (imap-)expunge? Since the folder is not active there is no concept
02252      of "leaving the folder", so the setting really has little to do with it. */
02253   // if ( autoExpunge() )
02254     expungeFolder(this, true);
02255   getFolder();
02256 
02257   return rc;
02258 }
02259 
02260 //-----------------------------------------------------------------------------
02261 void
02262 KMFolderImap::setUserRights( unsigned int userRights )
02263 {
02264   mUserRights = userRights;
02265   kdDebug(5006) << imapPath() << " setUserRights: " << userRights << endl;
02266 }
02267 
02268 //-----------------------------------------------------------------------------
02269 void KMFolderImap::slotCompleteMailCheckProgress()
02270 {
02271   if ( mMailCheckProgressItem ) {
02272     mMailCheckProgressItem->setComplete();
02273     mMailCheckProgressItem = 0;
02274     emit numUnreadMsgsChanged( folder() );
02275   }
02276 }
02277 
02278 //-----------------------------------------------------------------------------
02279 void KMFolderImap::setSubfolderState( imapState state )
02280 {
02281   mSubfolderState = state;
02282   if ( state == imapNoInformation && folder()->child() )
02283   {
02284     // pass through to children
02285     KMFolderNode* node;
02286     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02287     for ( ; (node = it.current()); )
02288     {
02289       ++it;
02290       if (node->isDir()) continue;
02291       KMFolder *folder = static_cast<KMFolder*>(node);
02292       static_cast<KMFolderImap*>(folder->storage())->setSubfolderState( state );
02293     }
02294   }
02295 }
02296 
02297 //-----------------------------------------------------------------------------
02298 void KMFolderImap::setIncludeInMailCheck( bool check )
02299 {
02300   bool changed = ( mCheckMail != check );
02301   mCheckMail = check;
02302   if ( changed )
02303     account()->slotUpdateFolderList();
02304 }
02305 
02306 //-----------------------------------------------------------------------------
02307 void KMFolderImap::setAlreadyRemoved( bool removed )
02308 {
02309   mAlreadyRemoved = removed;
02310   if ( folder()->child() )
02311   {
02312     // pass through to childs
02313     KMFolderNode* node;
02314     QPtrListIterator<KMFolderNode> it( *folder()->child() );
02315     for ( ; (node = it.current()); )
02316     {
02317       ++it;
02318       if (node->isDir()) continue;
02319       KMFolder *folder = static_cast<KMFolder*>(node);
02320       static_cast<KMFolderImap*>(folder->storage())->setAlreadyRemoved( removed );
02321     }
02322   }
02323 }
02324 
02325 void KMFolderImap::slotCreatePendingFolders( int errorCode, const QString& errorMsg )
02326 {
02327   Q_UNUSED( errorMsg );
02328   disconnect( account(), SIGNAL( connectionResult( int, const QString& ) ),
02329               this, SLOT( slotCreatePendingFolders( int, const QString& ) ) );
02330   if ( !errorCode ) {
02331     QStringList::Iterator it = mFoldersPendingCreation.begin();
02332     for ( ; it != mFoldersPendingCreation.end(); ++it ) {
02333       createFolder( *it );
02334     }
02335   }
02336   mFoldersPendingCreation.clear();
02337 }
02338 
02339 //-----------------------------------------------------------------------------
02340 void KMFolderImap::search( const KMSearchPattern* pattern )
02341 {
02342   if ( !pattern || pattern->isEmpty() )
02343   {
02344     // not much to do here
02345     QValueList<Q_UINT32> serNums;
02346     emit searchResult( folder(), serNums, pattern, true );
02347     return;
02348   }
02349   SearchJob* job = new SearchJob( this, account(), pattern );
02350   connect( job, SIGNAL( searchDone( QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ),
02351            this, SLOT( slotSearchDone( QValueList<Q_UINT32>, const KMSearchPattern*, bool ) ) );
02352   job->start();
02353 }
02354 
02355 //-----------------------------------------------------------------------------
02356 void KMFolderImap::slotSearchDone( QValueList<Q_UINT32> serNums,
02357                                    const KMSearchPattern* pattern,
02358                                    bool complete )
02359 {
02360   emit searchResult( folder(), serNums, pattern, complete );
02361 }
02362 
02363 //-----------------------------------------------------------------------------
02364 void KMFolderImap::search( const KMSearchPattern* pattern, Q_UINT32 serNum )
02365 {
02366   if ( !pattern || pattern->isEmpty() )
02367   {
02368     // not much to do here
02369     emit searchDone( folder(), serNum, pattern, false );
02370     return;
02371   }
02372   SearchJob* job = new SearchJob( this, account(), pattern, serNum );
02373   connect( job, SIGNAL( searchDone( Q_UINT32, const KMSearchPattern*, bool ) ),
02374            this, SLOT( slotSearchDone( Q_UINT32, const KMSearchPattern*, bool ) ) );
02375   job->start();
02376 }
02377 
02378 //-----------------------------------------------------------------------------
02379 void KMFolderImap::slotSearchDone( Q_UINT32 serNum, const KMSearchPattern* pattern,
02380                                    bool matches )
02381 {
02382   emit searchDone( folder(), serNum, pattern, matches );
02383 }
02384 
02385 //-----------------------------------------------------------------------------
02386 bool KMFolderImap::isMoveable() const
02387 {
02388   return ( hasChildren() == HasNoChildren &&
02389       !folder()->isSystemFolder() ) ? true : false;
02390 }
02391 
02392 //-----------------------------------------------------------------------------
02393 const ulong KMFolderImap::serNumForUID( ulong uid )
02394 {
02395   if ( mUidMetaDataMap.find( uid ) ) {
02396     KMMsgMetaData *md = mUidMetaDataMap[uid];
02397     return md->serNum();
02398   } else {
02399     kdDebug(5006) << "serNumForUID: unknown uid " << uid << endl;
02400     return 0;
02401   }
02402 }
02403 
02404 //-----------------------------------------------------------------------------
02405 void KMFolderImap::saveMsgMetaData( KMMessage* msg, ulong uid )
02406 {
02407   if ( uid == 0 ) {
02408     uid = msg->UID();
02409   }
02410   ulong serNum = msg->getMsgSerNum();
02411   mUidMetaDataMap.replace( uid, new KMMsgMetaData(msg->status(), serNum) );
02412 }
02413 
02414 //-----------------------------------------------------------------------------
02415 void KMFolderImap::setImapPath( const QString& path )
02416 {
02417   if ( path.isEmpty() ) {
02418     kdWarning(5006) << k_funcinfo << "ignoring empty path" << endl;
02419   } else {
02420     mImapPath = path;
02421   }
02422 }
02423 
02424 void KMFolderImap::finishMailCheck( const char *dbg, imapState state )
02425 {
02426   quiet( false );
02427   mContentState = state;
02428   emit folderComplete( this, mContentState == imapFinished );
02429   close(dbg);
02430 }
02431 
02432 #include "kmfolderimap.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys