kmail Library API Documentation

kmfolderimap.cpp

00001 00023 #ifdef HAVE_CONFIG_H 00024 #include <config.h> 00025 #endif 00026 00027 #include "kmfolderimap.h" 00028 #include "kmfoldermbox.h" 00029 #include "kmfoldertree.h" 00030 #include "undostack.h" 00031 #include "kmfoldermgr.h" 00032 #include "imapjob.h" 00033 using KMail::ImapJob; 00034 #include "attachmentstrategy.h" 00035 using KMail::AttachmentStrategy; 00036 00037 #include <kdebug.h> 00038 #include <kio/scheduler.h> 00039 #include <kconfig.h> 00040 00041 #include <qbuffer.h> 00042 #include <qtextcodec.h> 00043 00044 #include <assert.h> 00045 00046 KMFolderImap::KMFolderImap(KMFolderDir* aParent, const QString& aName) 00047 : KMFolderMbox(aParent, aName) 00048 { 00049 mContentState = imapNoInformation; 00050 mSubfolderState = imapNoInformation; 00051 mAccount = 0; 00052 mIsSelected = FALSE; 00053 mLastUid = 0; 00054 mCheckFlags = TRUE; 00055 mCheckMail = TRUE; 00056 mCheckingValidity = FALSE; 00057 00058 KConfig* config = KMKernel::config(); 00059 KConfigGroupSaver saver(config, "Folder-" + idString()); 00060 mUidValidity = config->readEntry("UidValidity"); 00061 if (mImapPath.isEmpty()) mImapPath = config->readEntry("ImapPath"); 00062 if (aName == "INBOX" && mImapPath == "/INBOX/") 00063 { 00064 mIsSystemFolder = TRUE; 00065 mLabel = i18n("inbox"); 00066 } 00067 mNoContent = config->readBoolEntry("NoContent", FALSE); 00068 mReadOnly = config->readBoolEntry("ReadOnly", FALSE); 00069 00070 readConfig(); 00071 } 00072 00073 KMFolderImap::~KMFolderImap() 00074 { 00075 if (mAccount) { 00076 mAccount->removeSlaveJobsForFolder( this ); 00077 /* Now that we've removed ourselves from the accounts jobs map, kill all 00078 ongoing operations and reset mailcheck if we were deleted during an 00079 ongoing mailcheck of our account. Not very gracefull, but safe, and the 00080 only way I can see to reset the account state cleanly. */ 00081 if ( mAccount->checkingMail() ) { 00082 mAccount->killAllJobs(); 00083 } 00084 } 00085 writeConfig(); 00086 if (kmkernel->undoStack()) kmkernel->undoStack()->folderDestroyed(this); 00087 mMetaDataMap.setAutoDelete( true ); 00088 mMetaDataMap.clear(); 00089 } 00090 00091 00092 //----------------------------------------------------------------------------- 00093 void KMFolderImap::close(bool aForced) 00094 { 00095 if (mOpenCount <= 0 ) return; 00096 if (mOpenCount > 0) mOpenCount--; 00097 if (mOpenCount > 0 && !aForced) return; 00098 // FIXME is this still needed? 00099 if (mAccount) 00100 mAccount->ignoreJobsForFolder( this ); 00101 int idx = count(); 00102 while (--idx >= 0) { 00103 if ( mMsgList[idx]->isMessage() ) { 00104 KMMessage *msg = static_cast<KMMessage*>(mMsgList[idx]); 00105 if (msg->transferInProgress()) 00106 msg->setTransferInProgress( false ); 00107 } 00108 } 00109 // The inherited close will decrement again, so we have to adjust. 00110 mOpenCount++; 00111 KMFolderMbox::close(aForced); 00112 } 00113 00114 //----------------------------------------------------------------------------- 00115 KMMessage* KMFolderImap::getMsg(int idx) 00116 { 00117 if(!(idx >= 0 && idx <= count())) 00118 return 0; 00119 00120 KMMsgBase* mb = getMsgBase(idx); 00121 if (!mb) return 0; 00122 if (mb->isMessage()) 00123 { 00124 return ((KMMessage*)mb); 00125 } else { 00126 KMMessage* msg = KMFolder::getMsg( idx ); 00127 if ( msg ) // set it incomplete as the msg was not transferred from the server 00128 msg->setComplete( false ); 00129 return msg; 00130 } 00131 } 00132 00133 //----------------------------------------------------------------------------- 00134 void KMFolderImap::setAccount(KMAcctImap *aAccount) 00135 { 00136 mAccount = aAccount; 00137 if (!mChild) return; 00138 KMFolderNode* node; 00139 for (node = mChild->first(); node; node = mChild->next()) 00140 { 00141 if (!node->isDir()) 00142 static_cast<KMFolderImap*>(node)->setAccount(aAccount); 00143 } 00144 } 00145 00146 //----------------------------------------------------------------------------- 00147 void KMFolderImap::readConfig() 00148 { 00149 KConfig* config = KMKernel::config(); 00150 KConfigGroupSaver saver(config, "Folder-" + idString()); 00151 mCheckMail = config->readBoolEntry("checkmail", true); 00152 KMFolderMbox::readConfig(); 00153 } 00154 00155 //----------------------------------------------------------------------------- 00156 void KMFolderImap::writeConfig() 00157 { 00158 KConfig* config = KMKernel::config(); 00159 KConfigGroupSaver saver(config, "Folder-" + idString()); 00160 config->writeEntry("checkmail", mCheckMail); 00161 config->writeEntry("UidValidity", mUidValidity); 00162 config->writeEntry("ImapPath", mImapPath); 00163 config->writeEntry("NoContent", mNoContent); 00164 config->writeEntry("ReadOnly", mReadOnly); 00165 KMFolderMbox::writeConfig(); 00166 } 00167 00168 //----------------------------------------------------------------------------- 00169 void KMFolderImap::removeOnServer() 00170 { 00171 KURL url = mAccount->getUrl(); 00172 url.setPath(imapPath()); 00173 if ( mAccount->makeConnection() != ImapAccountBase::Connected ) 00174 return; 00175 KIO::SimpleJob *job = KIO::file_delete(url, FALSE); 00176 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); 00177 ImapAccountBase::jobData jd(url.url()); 00178 mAccount->insertJob(job, jd); 00179 connect(job, SIGNAL(result(KIO::Job *)), 00180 this, SLOT(slotRemoveFolderResult(KIO::Job *))); 00181 } 00182 00183 //----------------------------------------------------------------------------- 00184 void KMFolderImap::slotRemoveFolderResult(KIO::Job *job) 00185 { 00186 ImapAccountBase::JobIterator it = mAccount->findJob(job); 00187 if ( it == mAccount->jobsEnd() ) return; 00188 mAccount->removeJob(it); 00189 if (job->error()) 00190 { 00191 mAccount->slotSlaveError( mAccount->slave(), job->error(), 00192 job->errorText() ); 00193 } else { 00194 mAccount->displayProgress(); 00195 kmkernel->imapFolderMgr()->remove(this); 00196 } 00197 } 00198 00199 //----------------------------------------------------------------------------- 00200 void KMFolderImap::removeMsg(int idx, bool quiet) 00201 { 00202 if (idx < 0) 00203 return; 00204 00205 if (!quiet) 00206 { 00207 KMMessage *msg = getMsg(idx); 00208 deleteMessage(msg); 00209 } 00210 00211 mLastUid = 0; 00212 KMFolderMbox::removeMsg(idx); 00213 } 00214 00215 void KMFolderImap::removeMsg(QPtrList<KMMessage> msgList, bool quiet) 00216 { 00217 if (!quiet) 00218 deleteMessage(msgList); 00219 00220 mLastUid = 0; 00221 00222 /* Remove the messages from the local store as well. 00223 We don't call KMFolderInherited::removeMsg(QPtrList<KMMessage>) but 00224 iterate ourselves, as that would call KMFolderImap::removeMsg(int) 00225 and not the one from the store we want to be used. */ 00226 for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() ) 00227 { 00228 int idx = find(msg); 00229 assert( idx != -1); 00230 // ATTENTION port me to maildir 00231 KMFolderMbox::removeMsg(idx, quiet); 00232 } 00233 } 00234 00235 //----------------------------------------------------------------------------- 00236 int KMFolderImap::rename( const QString& newName, KMFolderDir */*aParent*/ ) 00237 { 00238 if ( newName == name() ) 00239 return 0; 00240 00241 QString path = imapPath(); 00242 int i = path.findRev( '.' ); 00243 path = path.left( i ); 00244 path += "." + newName; 00245 KURL src( mAccount->getUrl() ); 00246 src.setPath( imapPath() ); 00247 KURL dst( mAccount->getUrl() ); 00248 dst.setPath( path ); 00249 KIO::SimpleJob *job = KIO::rename( src, dst, true ); 00250 kdDebug(5006)<< "### Rename : " << src.prettyURL() 00251 << " |=> " << dst.prettyURL() 00252 << endl; 00253 KIO::Scheduler::assignJobToSlave( mAccount->slave(), job ); 00254 connect( job, SIGNAL(result(KIO::Job*)), 00255 SLOT(slotRenameResult(KIO::Job*)) ); 00256 setImapPath( path ); 00257 return 0; 00258 } 00259 00260 //----------------------------------------------------------------------------- 00261 void KMFolderImap::slotRenameResult( KIO::Job *job ) 00262 { 00263 KIO::SimpleJob* sj = static_cast<KIO::SimpleJob*>(job); 00264 if ( job->error() ) { 00265 setImapPath( sj->url().path() ); 00266 mAccount->slotSlaveError( mAccount->slave(), job->error(), 00267 job->errorText() ); 00268 return; 00269 } 00270 QString path = imapPath(); 00271 int i = path.findRev( '.' ); 00272 path = path.mid( ++i ); 00273 path.remove( '/' ); 00274 KMFolderMbox::rename( path ); 00275 kmkernel->folderMgr()->contentsChanged(); 00276 } 00277 00278 //----------------------------------------------------------------------------- 00279 void KMFolderImap::addMsgQuiet(KMMessage* aMsg) 00280 { 00281 KMFolder *folder = aMsg->parent(); 00282 Q_UINT32 serNum = 0; 00283 aMsg->setTransferInProgress( false ); 00284 if (folder) { 00285 serNum = aMsg->getMsgSerNum(); 00286 kmkernel->undoStack()->pushSingleAction( serNum, folder, this ); 00287 int idx = folder->find( aMsg ); 00288 assert( idx != -1 ); 00289 folder->take( idx ); 00290 } 00291 // Remember the status, so it can be transfered to the new message. 00292 mMetaDataMap.insert(aMsg->msgIdMD5(), new KMMsgMetaData(aMsg->status(), serNum)); 00293 00294 delete aMsg; 00295 aMsg = 0; 00296 getFolder(); 00297 } 00298 00299 //----------------------------------------------------------------------------- 00300 void KMFolderImap::addMsgQuiet(QPtrList<KMMessage> msgList) 00301 { 00302 KMFolder *folder = msgList.first()->parent(); 00303 Q_UINT32 serNum = 0; 00304 if (folder) serNum = msgList.first()->getMsgSerNum(); 00305 int undoId = -1; 00306 for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() ) 00307 { 00308 if ( undoId == -1 ) 00309 undoId = kmkernel->undoStack()->newUndoAction( folder, this ); 00310 kmkernel->undoStack()->addMsgToAction( undoId, msg->getMsgSerNum() ); 00311 // Remember the status, so it can be transfered to the new message. 00312 mMetaDataMap.insert(msg->msgIdMD5(), new KMMsgMetaData(msg->status(), serNum)); 00313 msg->setTransferInProgress( false ); 00314 } 00315 if (folder) folder->take(msgList); 00316 msgList.setAutoDelete(true); 00317 msgList.clear(); 00318 getFolder(); 00319 } 00320 00321 //----------------------------------------------------------------------------- 00322 int KMFolderImap::addMsg(KMMessage* aMsg, int* aIndex_ret) 00323 { 00324 QPtrList<KMMessage> list; list.append(aMsg); 00325 return addMsg(list, aIndex_ret); 00326 } 00327 00328 int KMFolderImap::addMsg(QPtrList<KMMessage>& msgList, int* aIndex_ret) 00329 { 00330 KMMessage *aMsg = msgList.getFirst(); 00331 KMFolder *msgParent = aMsg->parent(); 00332 00333 ImapJob *imapJob = 0; 00334 if (msgParent) 00335 { 00336 if (msgParent->folderType() == KMFolderTypeImap) 00337 { 00338 if (static_cast<KMFolderImap*>(msgParent)->account() == account()) 00339 { 00340 // make sure the messages won't be deleted while we work with them 00341 for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() ) 00342 msg->setTransferInProgress(true); 00343 00344 if (this == msgParent) 00345 { 00346 // transfer the whole message, e.g. a draft-message is canceled and re-added to the drafts-folder 00347 for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() ) 00348 { 00349 if (!msg->isComplete()) 00350 { 00351 int idx = msgParent->find(msg); 00352 assert(idx != -1); 00353 msg = msgParent->getMsg(idx); 00354 } 00355 imapJob = new ImapJob(msg, ImapJob::tPutMessage, this); 00356 connect(imapJob, SIGNAL(messageStored(KMMessage*)), 00357 SLOT(addMsgQuiet(KMMessage*))); 00358 imapJob->start(); 00359 } 00360 00361 } else { 00362 00363 // get the messages and the uids 00364 QValueList<int> uids; 00365 getUids(msgList, uids); 00366 00367 // get the sets (do not sort the uids) 00368 QStringList sets = makeSets(uids, false); 00369 00370 for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) 00371 { 00372 // we need the messages that belong to the current set to pass them to the ImapJob 00373 QPtrList<KMMessage> temp_msgs = splitMessageList(*it, msgList); 00374 00375 imapJob = new ImapJob(temp_msgs, *it, ImapJob::tMoveMessage, this); 00376 connect(imapJob, SIGNAL(messageCopied(QPtrList<KMMessage>)), 00377 SLOT(addMsgQuiet(QPtrList<KMMessage>))); 00378 imapJob->start(); 00379 } 00380 } 00381 if (aIndex_ret) *aIndex_ret = -1; 00382 return 0; 00383 } 00384 else 00385 { 00386 // different account, check if messages can be added 00387 QPtrListIterator<KMMessage> it( msgList ); 00388 KMMessage *msg; 00389 while ( (msg = it.current()) != 0 ) 00390 { 00391 ++it; 00392 if (!canAddMsgNow(msg, aIndex_ret)) 00393 msgList.remove(msg); 00394 else { 00395 if (!msg->transferInProgress()) 00396 msg->setTransferInProgress(true); 00397 } 00398 } 00399 } 00400 } // if imap 00401 } 00402 00403 for ( KMMessage* msg = msgList.first(); msg; msg = msgList.next() ) 00404 { 00405 // transfer from local folders or other accounts 00406 if (msgParent && !msg->isMessage()) 00407 { 00408 int idx = msgParent->find(msg); 00409 assert(idx != -1); 00410 msg = msgParent->getMsg(idx); 00411 } 00412 if (!msg->transferInProgress()) 00413 msg->setTransferInProgress(true); 00414 imapJob = new ImapJob(msg, ImapJob::tPutMessage, this); 00415 connect(imapJob, SIGNAL(messageStored(KMMessage*)), 00416 SLOT(addMsgQuiet(KMMessage*))); 00417 imapJob->start(); 00418 } 00419 00420 if (aIndex_ret) *aIndex_ret = -1; 00421 return 0; 00422 } 00423 00424 //----------------------------------------------------------------------------- 00425 void KMFolderImap::copyMsg(QPtrList<KMMessage>& msgList) 00426 { 00427 for (KMMessage *msg = msgList.first(); msg; msg = msgList.next()) { 00428 // Remember the status, so it can be transfered to the new message. 00429 mMetaDataMap.insert(msg->msgIdMD5(), new KMMsgMetaData(msg->status())); 00430 } 00431 QValueList<int> uids; 00432 getUids(msgList, uids); 00433 QStringList sets = makeSets(uids, false); 00434 for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) 00435 { 00436 // we need the messages that belong to the current set to pass them to the ImapJob 00437 QPtrList<KMMessage> temp_msgs = splitMessageList(*it, msgList); 00438 00439 ImapJob *job = new ImapJob(temp_msgs, *it, ImapJob::tCopyMessage, this); 00440 job->start(); 00441 } 00442 } 00443 00444 //----------------------------------------------------------------------------- 00445 QPtrList<KMMessage> KMFolderImap::splitMessageList(QString set, QPtrList<KMMessage>& msgList) 00446 { 00447 int lastcomma = set.findRev(","); 00448 int lastdub = set.findRev(":"); 00449 int last = 0; 00450 if (lastdub > lastcomma) last = lastdub; 00451 else last = lastcomma; 00452 last++; 00453 if (last < 0) last = set.length(); 00454 // the last uid of the current set 00455 QString last_uid = set.right(set.length() - last); 00456 QPtrList<KMMessage> temp_msgs; 00457 QString uid; 00458 if (!last_uid.isEmpty()) 00459 { 00460 QPtrListIterator<KMMessage> it( msgList ); 00461 KMMessage* msg = 0; 00462 while ( (msg = it.current()) != 0 ) 00463 { 00464 // append the msg to the new list and delete it from the old 00465 temp_msgs.append(msg); 00466 uid = msg->headerField("X-UID"); 00467 // remove modifies the current 00468 msgList.remove(msg); 00469 if (uid == last_uid) break; 00470 } 00471 } 00472 else 00473 { 00474 // probably only one element 00475 temp_msgs = msgList; 00476 } 00477 00478 return temp_msgs; 00479 } 00480 00481 //----------------------------------------------------------------------------- 00482 KMMessage* KMFolderImap::take(int idx) 00483 { 00484 KMMsgBase* mb(mMsgList[idx]); 00485 if (!mb) return 0; 00486 if (!mb->isMessage()) readMsg(idx); 00487 00488 KMMessage *msg = static_cast<KMMessage*>(mb); 00489 deleteMessage(msg); 00490 00491 mLastUid = 0; 00492 return KMFolderMbox::take(idx); 00493 } 00494 00495 void KMFolderImap::take(QPtrList<KMMessage> msgList) 00496 { 00497 deleteMessage(msgList); 00498 00499 mLastUid = 0; 00500 KMFolderMbox::take(msgList); 00501 } 00502 00503 //----------------------------------------------------------------------------- 00504 bool KMFolderImap::listDirectory(bool secondStep) 00505 { 00506 mSubfolderState = imapInProgress; 00507 if ( mAccount->makeConnection() == ImapAccountBase::Error ) 00508 return false; 00509 00510 // connect to folderlisting 00511 connect(mAccount, SIGNAL(receivedFolders(QStringList, QStringList, 00512 QStringList, const ImapAccountBase::jobData &)), 00513 this, SLOT(slotListResult(QStringList, QStringList, 00514 QStringList, const ImapAccountBase::jobData &))); 00515 00516 // start a new listing for the root-folder 00517 bool reset = (mImapPath == mAccount->prefix() && 00518 !secondStep && !mIsSystemFolder) ? true : false; 00519 00520 // get the folders 00521 mAccount->listDirectory(mImapPath, mAccount->onlySubscribedFolders(), 00522 secondStep, this, reset); 00523 00524 return true; 00525 } 00526 00527 00528 //----------------------------------------------------------------------------- 00529 void KMFolderImap::slotListResult( QStringList mSubfolderNames, 00530 QStringList mSubfolderPaths, 00531 QStringList mSubfolderMimeTypes, 00532 const ImapAccountBase::jobData & jobData ) 00533 { 00534 if (jobData.parent) { 00535 // the account is connected to several folders, so we 00536 // have to sort out if this result is for us 00537 if (jobData.parent != this) return; 00538 } 00539 // disconnect to avoid recursions 00540 disconnect(mAccount, SIGNAL(receivedFolders(QStringList, QStringList, 00541 QStringList, const ImapAccountBase::jobData &)), 00542 this, SLOT(slotListResult(QStringList, QStringList, 00543 QStringList, const ImapAccountBase::jobData &))); 00544 00545 mSubfolderState = imapFinished; 00546 bool it_inboxOnly = jobData.inboxOnly; 00547 // kdDebug(5006) << name() << ": " << mSubfolderNames.join(",") << "; inboxOnly:" << it_inboxOnly 00548 // << ", createinbox:" << mAccount->createInbox() << ", hasinbox:" << mAccount->hasInbox() << endl; 00549 // don't react on changes 00550 kmkernel->imapFolderMgr()->quiet(TRUE); 00551 if (it_inboxOnly) { 00552 // list again only for the INBOX 00553 listDirectory(TRUE); 00554 } else { 00555 if (mIsSystemFolder && mImapPath == "/INBOX/" 00556 && mAccount->prefix() == "/INBOX/") 00557 { 00558 // do not create folders under INBOX 00559 mAccount->setCreateInbox(FALSE); 00560 mSubfolderNames.clear(); 00561 } 00562 createChildFolder(); 00563 KMFolderImap *folder; 00564 KMFolderNode *node = mChild->first(); 00565 while (node) 00566 { 00567 // check if the folders still exist on the server 00568 if (!node->isDir() && (node->name().upper() != "INBOX" || !mAccount->createInbox()) 00569 && mSubfolderNames.findIndex(node->name()) == -1) 00570 { 00571 kdDebug(5006) << node->name() << " disappeared." << endl; 00572 kmkernel->imapFolderMgr()->remove(static_cast<KMFolder*>(node)); 00573 node = mChild->first(); 00574 } 00575 else node = mChild->next(); 00576 } 00577 if (mAccount->createInbox()) 00578 { 00579 // create the INBOX 00580 for (node = mChild->first(); node; node = mChild->next()) 00581 if (!node->isDir() && node->name() == "INBOX") break; 00582 if (node) folder = static_cast<KMFolderImap*>(node); 00583 else folder = static_cast<KMFolderImap*> 00584 (mChild->createFolder("INBOX", TRUE)); 00585 folder->setAccount(mAccount); 00586 folder->setImapPath("/INBOX/"); 00587 folder->setLabel(i18n("inbox")); 00588 if (!node) folder->close(); 00589 // so we have an INBOX 00590 mAccount->setCreateInbox( false ); 00591 mAccount->setHasInbox( true ); 00592 folder->listDirectory(); 00593 kmkernel->imapFolderMgr()->contentsChanged(); 00594 } 00595 for (uint i = 0; i < mSubfolderNames.count(); i++) 00596 { 00597 // create folders if necessary 00598 if (mSubfolderNames[i].upper() == "INBOX" && 00599 mSubfolderPaths[i].upper() == "/INBOX/" && 00600 mAccount->hasInbox()) // do not create an additional inbox 00601 continue; 00602 for (node = mChild->first(); node; node = mChild->next()) 00603 if (!node->isDir() && node->name() == mSubfolderNames[i]) break; 00604 if (node) folder = static_cast<KMFolderImap*>(node); 00605 else { 00606 folder = static_cast<KMFolderImap*> 00607 (mChild->createFolder(mSubfolderNames[i])); 00608 if (folder) 00609 { 00610 folder->close(); 00611 kmkernel->imapFolderMgr()->contentsChanged(); 00612 } else { 00613 kdWarning(5006) << "can't create folder " << mSubfolderNames[i] << endl; 00614 } 00615 } 00616 if (folder) 00617 { 00618 folder->setAccount(mAccount); 00619 folder->setNoContent(mSubfolderMimeTypes[i] == "inode/directory"); 00620 folder->setImapPath(mSubfolderPaths[i]); 00621 if (mSubfolderMimeTypes[i] == "message/directory" || 00622 mSubfolderMimeTypes[i] == "inode/directory") 00623 folder->listDirectory(); 00624 } 00625 } 00626 } 00627 // now others should react on the changes 00628 kmkernel->imapFolderMgr()->quiet(FALSE); 00629 } 00630 00631 //----------------------------------------------------------------------------- 00632 void KMFolderImap::checkValidity() 00633 { 00634 if (!mAccount) { 00635 emit folderComplete(this, false); 00636 return; 00637 } 00638 KURL url = mAccount->getUrl(); 00639 url.setPath(imapPath() + ";UID=0:0"); 00640 kdDebug(5006) << "KMFolderImap::checkValidity of: " << imapPath() << endl; 00641 if ( mAccount->makeConnection() != ImapAccountBase::Connected ) 00642 { 00643 kdWarning(5006) << "KMFolderImap::checkValidity - got no connection" << endl; 00644 emit folderComplete(this, FALSE); 00645 return; 00646 } 00647 // Only check once at a time. 00648 if (mCheckingValidity) { 00649 kdDebug(5006) << "KMFolderImap::checkValidity - already checking" << endl; 00650 return; 00651 } 00652 ImapAccountBase::jobData jd( url.url(), this ); 00653 KIO::SimpleJob *job = KIO::get(url, FALSE, FALSE); 00654 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); 00655 mAccount->insertJob(job, jd); 00656 connect(job, SIGNAL(result(KIO::Job *)), 00657 SLOT(slotCheckValidityResult(KIO::Job *))); 00658 connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)), 00659 SLOT(slotSimpleData(KIO::Job *, const QByteArray &))); 00660 // Only check once at a time. 00661 mCheckingValidity = true; 00662 00663 } 00664 00665 00666 //----------------------------------------------------------------------------- 00667 ulong KMFolderImap::lastUid() 00668 { 00669 if (mLastUid) return mLastUid; 00670 open(); 00671 if (count() > 0) 00672 { 00673 bool unget = !isMessage(count() - 1); 00674 KMMessage *msg = getMsg(count() - 1); 00675 mLastUid = msg->headerField("X-UID").toULong(); 00676 if (unget) unGetMsg(count() - 1); 00677 } 00678 close(); 00679 return mLastUid; 00680 } 00681 00682 00683 //----------------------------------------------------------------------------- 00684 void KMFolderImap::slotCheckValidityResult(KIO::Job * job) 00685 { 00686 kdDebug(5006) << "KMFolderImap::slotCheckValidityResult of: " << fileName() << endl; 00687 mCheckingValidity = false; 00688 ImapAccountBase::JobIterator it = mAccount->findJob(job); 00689 if ( it == mAccount->jobsEnd() ) return; 00690 if (job->error()) { 00691 mAccount->slotSlaveError(mAccount->slave(), job->error(), job->errorText()); 00692 emit folderComplete(this, FALSE); 00693 mAccount->displayProgress(); 00694 } else { 00695 QCString cstr((*it).data.data(), (*it).data.size() + 1); 00696 int a = cstr.find("X-uidValidity: "); 00697 int b = cstr.find("\r\n", a); 00698 QString uidv; 00699 if ( (b - a - 15) >= 0 ) uidv = cstr.mid(a + 15, b - a - 15); 00700 a = cstr.find("X-Access: "); 00701 b = cstr.find("\r\n", a); 00702 QString access; 00703 if ( (b - a - 10) >= 0 ) access = cstr.mid(a + 10, b - a - 10); 00704 mReadOnly = access == "Read only"; 00705 QString startUid; 00706 if (uidValidity() != uidv) 00707 { 00708 // uidValidity changed 00709 kdDebug(5006) << "KMFolderImap::slotCheckValidityResult uidValidty changed." << endl; 00710 mAccount->ignoreJobsForFolder(this); 00711 expunge(); 00712 mLastUid = 0; 00713 uidmap.clear(); 00714 setUidValidity(uidv); 00715 } else { 00716 if (!mCheckFlags) 00717 startUid = QString::number(lastUid() + 1); 00718 } 00719 mAccount->removeJob(it); 00720 reallyGetFolder(startUid); 00721 } 00722 } 00723 00724 //----------------------------------------------------------------------------- 00725 void KMFolderImap::getAndCheckFolder(bool force) 00726 { 00727 if (mNoContent) 00728 return getFolder(force); 00729 00730 if ( mAccount ) 00731 mAccount->processNewMailSingleFolder(this); 00732 if (force) { 00733 // force an update 00734 mCheckFlags = TRUE; 00735 } 00736 } 00737 00738 //----------------------------------------------------------------------------- 00739 void KMFolderImap::getFolder(bool force) 00740 { 00741 mGuessedUnreadMsgs = -1; 00742 if (mNoContent) 00743 { 00744 mContentState = imapFinished; 00745 emit folderComplete(this, true); 00746 return; 00747 } 00748 mContentState = imapInProgress; 00749 if (force) { 00750 // force an update 00751 mCheckFlags = TRUE; 00752 } 00753 checkValidity(); 00754 } 00755 00756 00757 //----------------------------------------------------------------------------- 00758 void KMFolderImap::reallyGetFolder(const QString &startUid) 00759 { 00760 KURL url = mAccount->getUrl(); 00761 if ( mAccount->makeConnection() != ImapAccountBase::Connected ) 00762 { 00763 emit folderComplete(this, FALSE); 00764 mAccount->displayProgress(); 00765 return; 00766 } 00767 quiet(true); 00768 if (startUid.isEmpty()) 00769 { 00770 url.setPath(imapPath() + ";SECTION=UID FLAGS"); 00771 KIO::SimpleJob *job = KIO::listDir(url, FALSE); 00772 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); 00773 ImapAccountBase::jobData jd( url.url(), this ); 00774 mAccount->insertJob(job, jd); 00775 connect(job, SIGNAL(result(KIO::Job *)), 00776 this, SLOT(slotListFolderResult(KIO::Job *))); 00777 connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)), 00778 this, SLOT(slotListFolderEntries(KIO::Job *, 00779 const KIO::UDSEntryList &))); 00780 } else { 00781 url.setPath(imapPath() + ";UID=" + startUid 00782 + ":*;SECTION=ENVELOPE"); 00783 KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE); 00784 KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob); 00785 ImapAccountBase::jobData jd( url.url(), this ); 00786 mAccount->insertJob(newJob, jd); 00787 connect(newJob, SIGNAL(result(KIO::Job *)), 00788 this, SLOT(slotGetLastMessagesResult(KIO::Job *))); 00789 connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)), 00790 this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &))); 00791 } 00792 } 00793 00794 00795 //----------------------------------------------------------------------------- 00796 void KMFolderImap::slotListFolderResult(KIO::Job * job) 00797 { 00798 ImapAccountBase::JobIterator it = mAccount->findJob(job); 00799 if ( it == mAccount->jobsEnd() ) return; 00800 QString uids; 00801 if (job->error()) 00802 { 00803 mAccount->slotSlaveError( mAccount->slave(), job->error(), 00804 job->errorText() ); 00805 quiet( false ); 00806 emit folderComplete(this, FALSE); 00807 mAccount->removeJob(it); 00808 mAccount->displayProgress(); 00809 return; 00810 } 00811 mCheckFlags = FALSE; 00812 QStringList::Iterator uid; 00813 // Check for already retrieved headers 00814 if (count()) 00815 { 00816 QCString cstr; 00817 int idx = 0, a, b, c, serverFlags; 00818 long int mailUid, serverUid; 00819 uid = (*it).items.begin(); 00820 while (idx < count() && uid != (*it).items.end()) 00821 { 00822 getMsgString(idx, cstr); 00823 a = cstr.find("X-UID: "); 00824 b = cstr.find("\n", a); 00825 if (a == -1 || b == -1) mailUid = -1; 00826 else mailUid = cstr.mid(a + 7, b - a - 7).toLong(); 00827 c = (*uid).find(","); 00828 serverUid = (*uid).left(c).toLong(); 00829 serverFlags = (*uid).mid(c+1).toInt(); 00830 if (mailUid < serverUid) removeMsg(idx, TRUE); 00831 else if (mailUid == serverUid) 00832 { 00833 if (!mReadOnly) 00834 flagsToStatus(getMsgBase(idx), serverFlags, false); 00835 idx++; 00836 uid = (*it).items.remove(uid); 00837 } 00838 else break; // happens only, if deleted mails reappear on the server 00839 } 00840 while (idx < count()) removeMsg(idx, TRUE); 00841 } 00842 for (uid = (*it).items.begin(); uid != (*it).items.end(); uid++) 00843 (*uid).truncate((*uid).find(",")); 00844 ImapAccountBase::jobData jd( QString::null, (*it).parent ); 00845 //jd.items = (*it).items; 00846 jd.total = (*it).items.count(); 00847 if (jd.total == 0) 00848 { 00849 quiet(false); 00850 mContentState = imapFinished; 00851 emit folderComplete(this, TRUE); 00852 mAccount->removeJob(it); 00853 mAccount->displayProgress(); 00854 return; 00855 } 00856 00857 QStringList sets; 00858 uid = (*it).items.begin(); 00859 if (jd.total == 1) sets.append(*uid + ":" + *uid); 00860 else sets = makeSets( (*it).items ); 00861 mAccount->removeJob(it); // don't use *it below 00862 00863 for (QStringList::Iterator i = sets.begin(); i != sets.end(); ++i) 00864 { 00865 KURL url = mAccount->getUrl(); 00866 url.setPath(imapPath() + ";UID=" + *i + ";SECTION=ENVELOPE"); 00867 if ( mAccount->makeConnection() != ImapAccountBase::Connected ) 00868 { 00869 quiet(false); 00870 emit folderComplete(this, FALSE); 00871 return; 00872 } 00873 KIO::SimpleJob *newJob = KIO::get(url, FALSE, FALSE); 00874 jd.url = url.url(); 00875 KIO::Scheduler::assignJobToSlave(mAccount->slave(), newJob); 00876 mAccount->insertJob(newJob, jd); 00877 connect(newJob, SIGNAL(result(KIO::Job *)), 00878 this, (i == sets.at(sets.count() - 1)) 00879 ? SLOT(slotGetLastMessagesResult(KIO::Job *)) 00880 : SLOT(slotGetMessagesResult(KIO::Job *))); 00881 connect(newJob, SIGNAL(data(KIO::Job *, const QByteArray &)), 00882 this, SLOT(slotGetMessagesData(KIO::Job *, const QByteArray &))); 00883 } 00884 } 00885 00886 00887 //----------------------------------------------------------------------------- 00888 void KMFolderImap::slotListFolderEntries(KIO::Job * job, 00889 const KIO::UDSEntryList & uds) 00890 { 00891 ImapAccountBase::JobIterator it = mAccount->findJob(job); 00892 if ( it == mAccount->jobsEnd() ) return; 00893 QString mimeType, name; 00894 long int flags = 0; 00895 for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin(); 00896 udsIt != uds.end(); udsIt++) 00897 { 00898 for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin(); 00899 eIt != (*udsIt).end(); eIt++) 00900 { 00901 if ((*eIt).m_uds == KIO::UDS_NAME) 00902 name = (*eIt).m_str; 00903 else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE) 00904 mimeType = (*eIt).m_str; 00905 else if ((*eIt).m_uds == KIO::UDS_ACCESS) 00906 flags = (*eIt).m_long; 00907 } 00908 if (mimeType == "message/rfc822-imap" && !(flags & 8)) 00909 (*it).items.append(name + "," + QString::number(flags)); 00910 } 00911 } 00912 00913 00914 //----------------------------------------------------------------------------- 00915 void KMFolderImap::flagsToStatus(KMMsgBase *msg, int flags, bool newMsg) 00916 { 00917 if (flags & 4) 00918 msg->setStatus( KMMsgStatusFlag ); 00919 if (flags & 2) 00920 msg->setStatus( KMMsgStatusReplied ); 00921 if (flags & 1) 00922 msg->setStatus( KMMsgStatusOld ); 00923 00924 if (msg->isOfUnknownStatus()) { 00925 if (newMsg) 00926 msg->setStatus( KMMsgStatusNew ); 00927 else 00928 msg->setStatus( KMMsgStatusUnread ); 00929 } 00930 } 00931 00932 00933 //----------------------------------------------------------------------------- 00934 QString KMFolderImap::statusToFlags(KMMsgStatus status) 00935 { 00936 QString flags; 00937 if (status & KMMsgStatusDeleted) 00938 flags = "\\DELETED"; 00939 else { 00940 if (status & KMMsgStatusOld || status & KMMsgStatusRead) 00941 flags = "\\SEEN "; 00942 if (status & KMMsgStatusReplied) 00943 flags += "\\ANSWERED "; 00944 if (status & KMMsgStatusFlag) 00945 flags += "\\FLAGGED"; 00946 } 00947 00948 return flags.simplifyWhiteSpace(); 00949 } 00950 00951 //------------------------------------------------------------- 00952 void 00953 KMFolderImap::ignoreJobsForMessage( KMMessage* msg ) 00954 { 00955 if ( !msg || msg->transferInProgress() || 00956 !msg->parent() || msg->parent()->folderType() != KMFolderTypeImap ) 00957 return; 00958 KMAcctImap *account; 00959 if ( !(account = static_cast<KMFolderImap*>(msg->parent())->account()) ) 00960 return; 00961 00962 account->ignoreJobsForMessage( msg ); 00963 } 00964 00965 //----------------------------------------------------------------------------- 00966 void KMFolderImap::slotGetMessagesData(KIO::Job * job, const QByteArray & data) 00967 { 00968 if ( data.isEmpty() ) return; // optimization 00969 ImapAccountBase::JobIterator it = mAccount->findJob(job); 00970 if ( it == mAccount->jobsEnd() ) return; 00971 (*it).cdata += QCString(data, data.size() + 1); 00972 int pos = (*it).cdata.find("\r\n--IMAPDIGEST"); 00973 if (pos > 0) 00974 { 00975 int p = (*it).cdata.find("\r\nX-uidValidity:"); 00976 if (p != -1) setUidValidity((*it).cdata 00977 .mid(p + 17, (*it).cdata.find("\r\n", p+1) - p - 17)); 00978 int c = (*it).cdata.find("\r\nX-Count:"); 00979 if ( c != -1 ) 00980 { 00981 bool ok; 00982 int exists = (*it).cdata.mid( c+10, 00983 (*it).cdata.find("\r\n", c+1) - c-10 ).toInt(&ok); 00984 if ( ok && exists < count() ) 00985 { 00986 kdDebug(5006) << "KMFolderImap::slotGetMessagesData - server has less messages (" << 00987 exists << ") then folder (" << count() << "), so reload" << endl; 00988 mAccount->displayProgress(); 00989 reallyGetFolder( QString::null ); 00990 (*it).cdata.remove(0, pos); 00991 return; 00992 } 00993 } 00994 (*it).cdata.remove(0, pos); 00995 } 00996 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1); 00997 int flags; 00998 while (pos >= 0) 00999 { 01000 KMMessage *msg = new KMMessage; 01001 msg->fromString((*it).cdata.mid(16, pos - 16)); 01002 flags = msg->headerField("X-Flags").toInt(); 01003 ulong uid = msg->headerField("X-UID").toULong(); 01004 if (flags & 8 || uid <= lastUid()) { 01005 delete msg; 01006 msg = 0; 01007 } 01008 else { 01009 if (uidmap.find(uid)) 01010 { 01011 // assign the sernum from the cache 01012 const ulong sernum = (ulong) uidmap[uid]; 01013 msg->setMsgSerNum(sernum); 01014 // delete the entry 01015 uidmap.remove(uid); 01016 } 01017 open(); 01018 KMFolderMbox::addMsg(msg, 0); 01019 // Transfer the status, if it is cached. 01020 QString id = msg->msgIdMD5(); 01021 if ( mMetaDataMap.find( id ) ) { 01022 KMMsgMetaData *md = mMetaDataMap[id]; 01023 msg->setStatus( md->status() ); 01024 if ( md->serNum() != 0 ) 01025 msg->setMsgSerNum( md->serNum() ); 01026 mMetaDataMap.remove( id ); 01027 delete md; 01028 } 01029 // Merge with the flags from the server. 01030 flagsToStatus((KMMsgBase*)msg, flags); 01031 // set the correct size 01032 msg->setMsgLength( msg->headerField("X-Length").toUInt() ); 01033 close(); 01034 01035 if (count() > 1) unGetMsg(count() - 1); 01036 mLastUid = uid; 01037 } 01038 (*it).cdata.remove(0, pos); 01039 (*it).done++; 01040 pos = (*it).cdata.find("\r\n--IMAPDIGEST", 1); 01041 mAccount->displayProgress(); 01042 } 01043 } 01044 01045 //------------------------------------------------------------- 01046 FolderJob* 01047 KMFolderImap::doCreateJob( KMMessage *msg, FolderJob::JobType jt, 01048 KMFolder *folder, QString partSpecifier, 01049 const AttachmentStrategy *as ) const 01050 { 01051 KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder); 01052 if ( jt == FolderJob::tGetMessage && partSpecifier == "STRUCTURE" && 01053 mAccount && mAccount->loadOnDemand() && 01054 ( msg->signatureState() == KMMsgNotSigned || 01055 msg->signatureState() == KMMsgSignatureStateUnknown ) ) 01056 { 01057 // load-on-demand: retrieve the BODYSTRUCTURE and to speed things up also the headers 01058 // this is not activated for small or signed messages 01059 ImapJob *job = new ImapJob( msg, jt, kmfi, "HEADER" ); 01060 job->start(); 01061 ImapJob *job2 = new ImapJob( msg, jt, kmfi, "STRUCTURE", as ); 01062 job2->start(); 01063 return job; 01064 } else { 01065 // download complete message or part (attachment) 01066 if ( partSpecifier == "STRUCTURE" ) // hide from outside 01067 partSpecifier = QString::null; 01068 01069 ImapJob *job = new ImapJob( msg, jt, kmfi, partSpecifier ); 01070 return job; 01071 } 01072 } 01073 01074 //------------------------------------------------------------- 01075 FolderJob* 01076 KMFolderImap::doCreateJob( QPtrList<KMMessage>& msgList, const QString& sets, 01077 FolderJob::JobType jt, KMFolder *folder ) const 01078 { 01079 KMFolderImap* kmfi = dynamic_cast<KMFolderImap*>(folder); 01080 ImapJob *job = new ImapJob( msgList, sets, jt, kmfi ); 01081 return job; 01082 } 01083 01084 //----------------------------------------------------------------------------- 01085 void KMFolderImap::getMessagesResult(KIO::Job * job, bool lastSet) 01086 { 01087 ImapAccountBase::JobIterator it = mAccount->findJob(job); 01088 if ( it == mAccount->jobsEnd() ) return; 01089 if (job->error()) 01090 { 01091 mAccount->slotSlaveError( mAccount->slave(), job->error(), 01092 job->errorText() ); 01093 mContentState = imapNoInformation; 01094 quiet( false ); 01095 emit folderComplete(this, false); 01096 } 01097 else 01098 { 01099 if (lastSet) 01100 { 01101 mContentState = imapFinished; 01102 quiet(false); 01103 emit folderComplete(this, true); 01104 } 01105 mAccount->removeJob(it); 01106 } 01107 01108 mAccount->displayProgress(); 01109 } 01110 01111 01112 //----------------------------------------------------------------------------- 01113 void KMFolderImap::slotGetLastMessagesResult(KIO::Job * job) 01114 { 01115 getMessagesResult(job, true); 01116 } 01117 01118 01119 //----------------------------------------------------------------------------- 01120 void KMFolderImap::slotGetMessagesResult(KIO::Job * job) 01121 { 01122 getMessagesResult(job, false); 01123 } 01124 01125 01126 //----------------------------------------------------------------------------- 01127 void KMFolderImap::createFolder(const QString &name) 01128 { 01129 KURL url = mAccount->getUrl(); 01130 url.setPath(imapPath() + name); 01131 if ( mAccount->makeConnection() != ImapAccountBase::Connected ) 01132 return; 01133 KIO::SimpleJob *job = KIO::mkdir(url); 01134 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); 01135 ImapAccountBase::jobData jd( url.url(), this ); 01136 jd.items = name; 01137 mAccount->insertJob(job, jd); 01138 connect(job, SIGNAL(result(KIO::Job *)), 01139 this, SLOT(slotCreateFolderResult(KIO::Job *))); 01140 } 01141 01142 01143 //----------------------------------------------------------------------------- 01144 void KMFolderImap::slotCreateFolderResult(KIO::Job * job) 01145 { 01146 ImapAccountBase::JobIterator it = mAccount->findJob(job); 01147 if ( it == mAccount->jobsEnd() ) return; 01148 if (job->error()) 01149 { 01150 mAccount->slotSlaveError( mAccount->slave(), job->error(), 01151 job->errorText() ); 01152 } else { 01153 listDirectory(); 01154 } 01155 mAccount->removeJob(job); 01156 } 01157 01158 01159 //----------------------------------------------------------------------------- 01160 static QTextCodec *sUtf7Codec = 0; 01161 01162 QTextCodec * KMFolderImap::utf7Codec() 01163 { 01164 if (!sUtf7Codec) sUtf7Codec = QTextCodec::codecForName("utf-7"); 01165 return sUtf7Codec; 01166 } 01167 01168 01169 //----------------------------------------------------------------------------- 01170 QString KMFolderImap::encodeFileName(const QString &name) 01171 { 01172 QString result = utf7Codec()->fromUnicode(name); 01173 return KURL::encode_string_no_slash(result); 01174 } 01175 01176 01177 //----------------------------------------------------------------------------- 01178 QString KMFolderImap::decodeFileName(const QString &name) 01179 { 01180 QString result = KURL::decode_string(name); 01181 return utf7Codec()->toUnicode(result.latin1()); 01182 } 01183 01184 //----------------------------------------------------------------------------- 01185 bool KMFolderImap::autoExpunge() 01186 { 01187 if (mAccount) 01188 return mAccount->autoExpunge(); 01189 01190 return false; 01191 } 01192 01193 01194 //----------------------------------------------------------------------------- 01195 void KMFolderImap::slotSimpleData(KIO::Job * job, const QByteArray & data) 01196 { 01197 if ( data.isEmpty() ) return; // optimization 01198 ImapAccountBase::JobIterator it = mAccount->findJob(job); 01199 if ( it == mAccount->jobsEnd() ) return; 01200 QBuffer buff((*it).data); 01201 buff.open(IO_WriteOnly | IO_Append); 01202 buff.writeBlock(data.data(), data.size()); 01203 buff.close(); 01204 } 01205 01206 //----------------------------------------------------------------------------- 01207 void KMFolderImap::deleteMessage(KMMessage * msg) 01208 { 01209 KURL url = mAccount->getUrl(); 01210 KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msg->parent()); 01211 const QString uid = msg->headerField("X-UID"); 01212 /* If the uid is empty the delete job below will nuke all mail in the 01213 folder, so we better safeguard against that. See ::expungeFolder, as 01214 to why. :( */ 01215 if ( uid.isEmpty() ) { 01216 kdDebug( 5006 ) << "KMFolderImap::deleteMessage: Attempt to delete " 01217 "an empty UID. Aborting." << endl; 01218 return; 01219 } 01220 url.setPath(msg_parent->imapPath() + ";UID=" + uid ); 01221 if ( mAccount->makeConnection() != ImapAccountBase::Connected ) 01222 return; 01223 KIO::SimpleJob *job = KIO::file_delete(url, FALSE); 01224 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); 01225 ImapAccountBase::jobData jd( url.url(), 0 ); 01226 mAccount->insertJob(job, jd); 01227 connect(job, SIGNAL(result(KIO::Job *)), 01228 mAccount, SLOT(slotSimpleResult(KIO::Job *))); 01229 } 01230 01231 void KMFolderImap::deleteMessage(QPtrList<KMMessage> msgList) 01232 { 01233 QValueList<int> uids; 01234 getUids(msgList, uids); 01235 QStringList sets = makeSets(uids); 01236 01237 KURL url = mAccount->getUrl(); 01238 KMFolderImap *msg_parent = static_cast<KMFolderImap*>(msgList.first()->parent()); 01239 for ( QStringList::Iterator it = sets.begin(); it != sets.end(); ++it ) 01240 { 01241 const QString uid = *it; 01242 // Don't delete with no uid, that nukes the folder. Should not happen, but 01243 // better safe than sorry. 01244 if ( uid.isEmpty() ) continue; 01245 url.setPath(msg_parent->imapPath() + ";UID=" + uid); 01246 if ( mAccount->makeConnection() != ImapAccountBase::Connected ) 01247 return; 01248 KIO::SimpleJob *job = KIO::file_delete(url, FALSE); 01249 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); 01250 ImapAccountBase::jobData jd( url.url(), 0 ); 01251 mAccount->insertJob(job, jd); 01252 connect(job, SIGNAL(result(KIO::Job *)), 01253 mAccount, SLOT(slotSimpleResult(KIO::Job *))); 01254 } 01255 } 01256 01257 //----------------------------------------------------------------------------- 01258 void KMFolderImap::setStatus(int idx, KMMsgStatus status, bool toggle) 01259 { 01260 QValueList<int> ids; ids.append(idx); 01261 setStatus(ids, status, toggle); 01262 } 01263 01264 void KMFolderImap::setStatus(QValueList<int>& ids, KMMsgStatus status, bool toggle) 01265 { 01266 KMFolder::setStatus(ids, status, toggle); 01267 if (mReadOnly) return; 01268 01269 /* The status has been already set in the local index. Update the flags on 01270 * the server. To avoid doing that for each message individually, group them 01271 * by the status string they will be assigned and make sets for each of those 01272 * groups of mails. This is necessary because the imap kio_slave status job 01273 * does not append flags but overwrites them. Example: 01274 * 01275 * 2 important mails and 3 unimportant mail, all unread. Mark all as read calls 01276 * this method with a list of uids. The 2 important mails need to get the string 01277 * \SEEN \FLAGGED while the others need to get just \SEEN. Build sets for each 01278 * of those and sort them, so the server can handle them efficiently. */ 01279 QMap< QString, QStringList > groups; 01280 for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) { 01281 KMMessage *msg = 0; 01282 bool unget = !isMessage(*it); 01283 msg = getMsg(*it); 01284 if (!msg) continue; 01285 QString flags = statusToFlags(msg->status()); 01286 // Collect uids for each type of flags. 01287 groups[flags].append(msg->headerField("X-UID")); 01288 if (unget) unGetMsg(*it); 01289 } 01290 QMapIterator< QString, QStringList > dit; 01291 for ( dit = groups.begin(); dit != groups.end(); ++dit ) { 01292 QCString flags = dit.key().latin1(); 01293 QStringList sets = makeSets( (*dit), true ); 01294 // Send off a status setting job for each set. 01295 for ( QStringList::Iterator slit = sets.begin(); slit != sets.end(); ++slit ) { 01296 QString imappath = imapPath() + ";UID=" + ( *slit ); 01297 setImapStatus(imappath, flags); 01298 } 01299 } 01300 mAccount->displayProgress(); 01301 } 01302 01303 //----------------------------------------------------------------------------- 01304 QStringList KMFolderImap::makeSets(QStringList& uids, bool sort) 01305 { 01306 QValueList<int> tmp; 01307 for ( QStringList::Iterator it = uids.begin(); it != uids.end(); ++it ) 01308 tmp.append( (*it).toInt() ); 01309 return makeSets(tmp, sort); 01310 } 01311 01312 QStringList KMFolderImap::makeSets(QValueList<int>& uids, bool sort) 01313 { 01314 QStringList sets; 01315 QString set; 01316 01317 if (uids.size() == 1) 01318 { 01319 sets.append(QString::number(uids.first())); 01320 return sets; 01321 } 01322 01323 if (sort) qHeapSort(uids); 01324 01325 int last = 0; 01326 // needed to make a uid like 124 instead of 124:124 01327 bool inserted = false; 01328 /* iterate over uids and build sets like 120:122,124,126:150 */ 01329 for ( QValueList<int>::Iterator it = uids.begin(); it != uids.end(); ++it ) 01330 { 01331 if (it == uids.begin() || set.isEmpty()) { 01332 set = QString::number(*it); 01333 inserted = true; 01334 } else 01335 { 01336 if (last+1 != *it) 01337 { 01338 // end this range 01339 if (inserted) 01340 set += ',' + QString::number(*it); 01341 else 01342 set += ':' + QString::number(last) + ',' + QString::number(*it); 01343 inserted = true; 01344 if (set.length() > 100) 01345 { 01346 // just in case the server has a problem with longer lines.. 01347 sets.append(set); 01348 set = ""; 01349 } 01350 } else { 01351 inserted = false; 01352 } 01353 } 01354 last = *it; 01355 } 01356 // last element 01357 if (!inserted) 01358 set += ':' + QString::number(uids.last()); 01359 01360 if (!set.isEmpty()) sets.append(set); 01361 01362 return sets; 01363 } 01364 01365 //----------------------------------------------------------------------------- 01366 void KMFolderImap::getUids(QValueList<int>& ids, QValueList<int>& uids) 01367 { 01368 KMMessage *msg = 0; 01369 // get the uids 01370 for ( QValueList<int>::Iterator it = ids.begin(); it != ids.end(); ++it ) 01371 { 01372 bool unget = !isMessage(*it); 01373 msg = getMsg(*it); 01374 if (!msg) continue; 01375 uids.append(msg->headerField("X-UID").toInt()); 01376 if (unget) unGetMsg(*it); 01377 } 01378 } 01379 01380 void KMFolderImap::getUids(QPtrList<KMMessage>& msgList, QValueList<int>& uids, KMFolder* msgParent) 01381 { 01382 KMMessage *msg = 0; 01383 01384 if (!msgParent) msgParent = msgList.first()->parent(); 01385 if (!msgParent) return; 01386 01387 for ( msg = msgList.first(); msg; msg = msgList.next() ) 01388 { 01389 if ( !msg->headerField("X-UID").isEmpty() ) 01390 uids.append(msg->headerField("X-UID").toInt()); 01391 } 01392 } 01393 01394 //----------------------------------------------------------------------------- 01395 void KMFolderImap::setImapStatus(QString path, QCString flags) 01396 { 01397 // set the status on the server, the uids are integrated in the path 01398 kdDebug(5006) << "setImapStatus path=" << path << endl; 01399 KURL url = mAccount->getUrl(); 01400 url.setPath(path); 01401 01402 QByteArray packedArgs; 01403 QDataStream stream( packedArgs, IO_WriteOnly); 01404 01405 stream << (int) 'S' << url << flags; 01406 01407 if ( mAccount->makeConnection() != ImapAccountBase::Connected ) 01408 return; 01409 KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE); 01410 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); 01411 ImapAccountBase::jobData jd( url.url(), 0 ); 01412 mAccount->insertJob(job, jd); 01413 connect(job, SIGNAL(result(KIO::Job *)), 01414 SLOT(slotSetStatusResult(KIO::Job *))); 01415 } 01416 01417 01418 //----------------------------------------------------------------------------- 01419 void KMFolderImap::expungeFolder(KMFolderImap * aFolder, bool quiet) 01420 { 01421 aFolder->setNeedsCompacting(FALSE); 01422 KURL url = mAccount->getUrl(); 01423 url.setPath(aFolder->imapPath() + ";UID=*"); 01424 if ( mAccount->makeConnection() != ImapAccountBase::Connected ) 01425 return; 01426 KIO::SimpleJob *job = KIO::file_delete(url, FALSE); 01427 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); 01428 ImapAccountBase::jobData jd( url.url(), 0 ); 01429 jd.quiet = quiet; 01430 mAccount->insertJob(job, jd); 01431 connect(job, SIGNAL(result(KIO::Job *)), 01432 mAccount, SLOT(slotSimpleResult(KIO::Job *))); 01433 } 01434 01435 01436 //----------------------------------------------------------------------------- 01437 void KMFolderImap::slotSetStatusResult(KIO::Job * job) 01438 { 01439 ImapAccountBase::JobIterator it = mAccount->findJob(job); 01440 if ( it == mAccount->jobsEnd() ) return; 01441 mAccount->removeJob(it); 01442 if (job->error() && job->error() != KIO::ERR_CANNOT_OPEN_FOR_WRITING) 01443 { 01444 mAccount->slotSlaveError( mAccount->slave(), job->error(), 01445 job->errorText() ); 01446 } 01447 mAccount->displayProgress(); 01448 } 01449 01450 01451 //----------------------------------------------------------------------------- 01452 bool KMFolderImap::processNewMail(bool) 01453 { 01454 // a little safety 01455 if ( !mAccount ) { 01456 kdWarning(5006) << "KMFolderImap::processNewMail - account is null!" << endl; 01457 return false; 01458 } 01459 if (imapPath().isEmpty()) { 01460 kdWarning(5006) << "KMFolderImap::processNewMail - imapPath of " << name() << " is empty!" << endl; 01461 kmkernel->imapFolderMgr()->remove(this); 01462 return false; 01463 } 01464 KURL url = mAccount->getUrl(); 01465 if (mReadOnly) 01466 url.setPath(imapPath() + ";SECTION=UIDNEXT"); 01467 else 01468 url.setPath(imapPath() + ";SECTION=UNSEEN"); 01469 if ( mAccount->makeConnection() != ImapAccountBase::Connected ) { 01470 kdWarning(5006) << "KMFolderImap::processNewMail - got no connection!" << endl; 01471 return false; 01472 } 01473 KIO::SimpleJob *job = KIO::stat(url, FALSE); 01474 KIO::Scheduler::assignJobToSlave(mAccount->slave(), job); 01475 ImapAccountBase::jobData jd(url.url()); 01476 mAccount->insertJob(job, jd); 01477 connect(job, SIGNAL(result(KIO::Job *)), 01478 SLOT(slotStatResult(KIO::Job *))); 01479 return true; 01480 } 01481 01482 01483 //----------------------------------------------------------------------------- 01484 void KMFolderImap::slotStatResult(KIO::Job * job) 01485 { 01486 ImapAccountBase::JobIterator it = mAccount->findJob(job); 01487 if ( it == mAccount->jobsEnd() ) return; 01488 mAccount->removeJob(it); 01489 if (job->error()) 01490 { 01491 mAccount->slotSlaveError( mAccount->slave(), job->error(), 01492 job->errorText() ); 01493 } else { 01494 KIO::UDSEntry uds = static_cast<KIO::StatJob*>(job)->statResult(); 01495 for (KIO::UDSEntry::ConstIterator it = uds.begin(); it != uds.end(); it++) 01496 { 01497 if ((*it).m_uds == KIO::UDS_SIZE) 01498 { 01499 if (mReadOnly) 01500 { 01501 mGuessedUnreadMsgs = -1; 01502 mGuessedUnreadMsgs = countUnread() + (*it).m_long - lastUid() - 1; 01503 if (mGuessedUnreadMsgs < 0) mGuessedUnreadMsgs = 0; 01504 } else { 01505 mGuessedUnreadMsgs = (*it).m_long; 01506 } 01507 } 01508 } 01509 } 01510 emit numUnreadMsgsChanged( this ); 01511 mAccount->displayProgress(); 01512 } 01513 01514 //----------------------------------------------------------------------------- 01515 int KMFolderImap::create(bool imap) 01516 { 01517 readConfig(); 01518 mUnreadMsgs = -1; 01519 return KMFolderMbox::create(imap); 01520 } 01521 01522 QValueList<int> KMFolderImap::splitSets(QString uids) 01523 { 01524 QValueList<int> uidlist; 01525 01526 // ex: 1205,1204,1203,1202,1236:1238 01527 QString buffer = QString::null; 01528 int setstart = -1; 01529 // iterate over the uids 01530 for (uint i = 0; i < uids.length(); i++) 01531 { 01532 QChar chr = uids[i]; 01533 if (chr == ',') 01534 { 01535 if (setstart > -1) 01536 { 01537 // a range (uid:uid) was before 01538 for (int j = setstart; j <= buffer.toInt(); j++) 01539 { 01540 uidlist.append(j); 01541 } 01542 setstart = -1; 01543 } else { 01544 // single uid 01545 uidlist.append(buffer.toInt()); 01546 } 01547 buffer = ""; 01548 } else if (chr == ':') { 01549 // remember the start of the range 01550 setstart = buffer.toInt(); 01551 buffer = ""; 01552 } else if (chr.category() == QChar::Number_DecimalDigit) { 01553 // digit 01554 buffer += chr; 01555 } else { 01556 // ignore 01557 } 01558 } 01559 // process the last data 01560 if (setstart > -1) 01561 { 01562 for (int j = setstart; j <= buffer.toInt(); j++) 01563 { 01564 uidlist.append(j); 01565 } 01566 } else { 01567 uidlist.append(buffer.toInt()); 01568 } 01569 01570 return uidlist; 01571 } 01572 01573 #include "kmfolderimap.moc"
KDE Logo
This file is part of the documentation for kmail Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Jul 28 23:58:01 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003