kmail

kmfolderimap.cpp

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