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