00001
00024 #ifdef HAVE_CONFIG_H
00025 #include <config.h>
00026 #endif
00027
00028 #include "imapaccountbase.h"
00029 using KMail::SieveConfig;
00030
00031 #include "kmacctmgr.h"
00032 #include "kmfolder.h"
00033 #include "kmbroadcaststatus.h"
00034 #include "kmmainwin.h"
00035 #include "kmfolderimap.h"
00036 #include "kmmainwidget.h"
00037 #include "kmmainwin.h"
00038 #include "kmmsgpart.h"
00039 #include "bodyvisitor.h"
00040 using KMail::BodyVisitor;
00041 #include "imapjob.h"
00042 using KMail::ImapJob;
00043
00044 #include <kdebug.h>
00045 #include <kconfig.h>
00046 #include <klocale.h>
00047 #include <kmessagebox.h>
00048 using KIO::MetaData;
00049 #include <kio/passdlg.h>
00050 using KIO::PasswordDialog;
00051 #include <kio/scheduler.h>
00052 #include <mimelib/bodypart.h>
00053 #include <mimelib/body.h>
00054 #include <mimelib/headers.h>
00055 #include <mimelib/message.h>
00056
00057
00058 #include <qregexp.h>
00059
00060 namespace KMail {
00061
00062 static const unsigned short int imapDefaultPort = 143;
00063
00064
00065
00066
00067
00068
00069
00070 ImapAccountBase::ImapAccountBase( KMAcctMgr * parent, const QString & name )
00071 : NetworkAccount( parent, name ),
00072 mPrefix( "/" ),
00073 mTotal( 0 ),
00074 mCountUnread( 0 ),
00075 mCountLastUnread( 0 ),
00076 mCountRemainChecks( 0 ),
00077 mAutoExpunge( true ),
00078 mHiddenFolders( false ),
00079 mOnlySubscribedFolders( false ),
00080 mLoadOnDemand( true ),
00081 mProgressEnabled( false ),
00082 mIdle( true ),
00083 mErrorDialogIsActive( false ),
00084 mPasswordDialogIsActive( false ),
00085 mCreateInbox( false )
00086 {
00087 mPort = imapDefaultPort;
00088 mBodyPartList.setAutoDelete(true);
00089 KIO::Scheduler::connect(SIGNAL(slaveError(KIO::Slave *, int, const QString &)),
00090 this, SLOT(slotSchedulerSlaveError(KIO::Slave *, int, const QString &)));
00091 KIO::Scheduler::connect(SIGNAL(slaveConnected(KIO::Slave *)),
00092 this, SLOT(slotSchedulerSlaveConnected(KIO::Slave *)));
00093 }
00094
00095 ImapAccountBase::~ImapAccountBase() {
00096 kdWarning( mSlave, 5006 )
00097 << "slave should have been destroyed by subclass!" << endl;
00098 }
00099
00100 void ImapAccountBase::init() {
00101 mPrefix = '/';
00102 mAutoExpunge = true;
00103 mHiddenFolders = false;
00104 mOnlySubscribedFolders = false;
00105 mLoadOnDemand = true;
00106 mProgressEnabled = false;
00107 }
00108
00109 void ImapAccountBase::pseudoAssign( const KMAccount * a ) {
00110 NetworkAccount::pseudoAssign( a );
00111
00112 const ImapAccountBase * i = dynamic_cast<const ImapAccountBase*>( a );
00113 if ( !i ) return;
00114
00115 setPrefix( i->prefix() );
00116 setAutoExpunge( i->autoExpunge() );
00117 setHiddenFolders( i->hiddenFolders() );
00118 setOnlySubscribedFolders( i->onlySubscribedFolders() );
00119 setLoadOnDemand( i->loadOnDemand() );
00120 }
00121
00122 unsigned short int ImapAccountBase::defaultPort() const {
00123 return imapDefaultPort;
00124 }
00125
00126 QString ImapAccountBase::protocol() const {
00127 return useSSL() ? "imaps" : "imap";
00128 }
00129
00130
00131
00132
00133
00134
00135
00136 void ImapAccountBase::setPrefix( const QString & prefix ) {
00137 mPrefix = prefix;
00138 mPrefix.remove( QRegExp( "[%*\"]" ) );
00139 if ( mPrefix.isEmpty() || mPrefix[0] != '/' )
00140 mPrefix.prepend( '/' );
00141 if ( mPrefix[ mPrefix.length() - 1 ] != '/' )
00142 mPrefix += '/';
00143 #if 1
00144 setPrefixHook();
00145 #else
00146 if ( mFolder ) mFolder->setImapPath( mPrefix );
00147 #endif
00148 }
00149
00150 void ImapAccountBase::setAutoExpunge( bool expunge ) {
00151 mAutoExpunge = expunge;
00152 }
00153
00154 void ImapAccountBase::setHiddenFolders( bool show ) {
00155 mHiddenFolders = show;
00156 }
00157
00158 void ImapAccountBase::setOnlySubscribedFolders( bool show ) {
00159 mOnlySubscribedFolders = show;
00160 }
00161
00162 void ImapAccountBase::setLoadOnDemand( bool load ) {
00163 mLoadOnDemand = load;
00164 }
00165
00166
00167
00168
00169
00170
00171
00172 void ImapAccountBase::readConfig( KConfig & config ) {
00173 NetworkAccount::readConfig( config );
00174
00175 setPrefix( config.readEntry( "prefix", "/" ) );
00176 setAutoExpunge( config.readBoolEntry( "auto-expunge", false ) );
00177 setHiddenFolders( config.readBoolEntry( "hidden-folders", false ) );
00178 setOnlySubscribedFolders( config.readBoolEntry( "subscribed-folders", false ) );
00179 setLoadOnDemand( config.readBoolEntry( "loadondemand", false ) );
00180 }
00181
00182 void ImapAccountBase::writeConfig( KConfig & config ) {
00183 NetworkAccount::writeConfig( config );
00184
00185 config.writeEntry( "prefix", prefix() );
00186 config.writeEntry( "auto-expunge", autoExpunge() );
00187 config.writeEntry( "hidden-folders", hiddenFolders() );
00188 config.writeEntry( "subscribed-folders", onlySubscribedFolders() );
00189 config.writeEntry( "loadondemand", loadOnDemand() );
00190 }
00191
00192
00193
00194
00195
00196
00197
00198 MetaData ImapAccountBase::slaveConfig() const {
00199 MetaData m = NetworkAccount::slaveConfig();
00200
00201 m.insert( "auth", auth() );
00202 if ( autoExpunge() )
00203 m.insert( "expunge", "auto" );
00204
00205 return m;
00206 }
00207
00208 ImapAccountBase::ConnectionState ImapAccountBase::makeConnection() {
00209 if ( mSlave ) return Connected;
00210
00211 if ( mPasswordDialogIsActive ) return Connecting;
00212 if( mAskAgain || passwd().isEmpty() || login().isEmpty() ) {
00213 QString log = login();
00214 QString pass = passwd();
00215
00216
00217
00218
00219 bool store = true;
00220 KConfigGroup passwords( KGlobal::config(), "Passwords" );
00221 passwords.writeEntry( "Keep", storePasswd() );
00222 QString msg = i18n("You need to supply a username and a password to "
00223 "access this mailbox.");
00224 mPasswordDialogIsActive = true;
00225 if ( PasswordDialog::getNameAndPassword( log, pass, &store, msg, false,
00226 QString::null, name(),
00227 i18n("Account:") )
00228 != QDialog::Accepted ) {
00229 checkDone(false, 0);
00230 mPasswordDialogIsActive = false;
00231 return Error;
00232 }
00233 mPasswordDialogIsActive = false;
00234
00235
00236 setPasswd( pass, store );
00237 setLogin( log );
00238 mAskAgain = false;
00239 }
00240
00241 mSlave = KIO::Scheduler::getConnectedSlave( getUrl(), slaveConfig() );
00242 if ( !mSlave ) {
00243 KMessageBox::error(0, i18n("Could not start process for %1.")
00244 .arg( getUrl().protocol() ) );
00245 return Error;
00246 }
00247
00248 return Connecting;
00249 }
00250
00251 void ImapAccountBase::postProcessNewMail( KMFolder * folder ) {
00252
00253 disconnect( folder, SIGNAL(numUnreadMsgsChanged(KMFolder*)),
00254 this, SLOT(postProcessNewMail(KMFolder*)) );
00255
00256 mCountRemainChecks--;
00257
00258
00259 mCountUnread += folder->countUnread();
00260 if (mCountRemainChecks == 0)
00261 {
00262
00263 KMBroadcastStatus::instance()->setStatusMsgTransmissionCompleted(
00264 name(), mCountUnread );
00265 if (mCountUnread > 0 && mCountUnread > mCountLastUnread) {
00266 checkDone(true, mCountUnread);
00267 mCountLastUnread = mCountUnread;
00268 } else {
00269 checkDone(false, 0);
00270 }
00271 setCheckingMail(false);
00272 mCountUnread = 0;
00273 }
00274 }
00275
00276
00277 void ImapAccountBase::displayProgress()
00278 {
00279 if (mProgressEnabled == mapJobData.isEmpty())
00280 {
00281 mProgressEnabled = !mapJobData.isEmpty();
00282 KMBroadcastStatus::instance()->setStatusProgressEnable( "I" + mName,
00283 mProgressEnabled );
00284 }
00285 mIdle = FALSE;
00286 if (mapJobData.isEmpty())
00287 mIdleTimer.start(15000);
00288 else
00289 mIdleTimer.stop();
00290 int total = 0, done = 0;
00291 for (QMap<KIO::Job*, jobData>::Iterator it = mapJobData.begin();
00292 it != mapJobData.end(); ++it)
00293 {
00294 total += (*it).total;
00295 done += (*it).done;
00296 }
00297 if (total == 0)
00298 {
00299 mTotal = 0;
00300 return;
00301 }
00302 if (total > mTotal) mTotal = total;
00303 done += mTotal - total;
00304 KMBroadcastStatus::instance()->setStatusProgressPercent( "I" + mName,
00305 100*done / mTotal );
00306 }
00307
00308
00309 void ImapAccountBase::listDirectory(QString path, bool onlySubscribed,
00310 bool secondStep, KMFolder* parent, bool reset)
00311 {
00312 if (makeConnection() == Error)
00313 return;
00314
00315 jobData jd;
00316 jd.total = 1; jd.done = 0;
00317
00318 if (reset)
00319 mHasInbox = false;
00320
00321 jd.inboxOnly = !secondStep && prefix() != "/"
00322 && path == prefix() && !mHasInbox;
00323 jd.onlySubscribed = onlySubscribed;
00324 if (parent) jd.parent = parent;
00325 if (!secondStep) mCreateInbox = FALSE;
00326
00327 KURL url = getUrl();
00328 url.setPath(((jd.inboxOnly) ? QString("/") : path)
00329 + ";TYPE=" + ((onlySubscribed) ? "LSUB" : "LIST"));
00330 mSubfolderNames.clear();
00331 mSubfolderPaths.clear();
00332 mSubfolderMimeTypes.clear();
00333 // and go
00334 KIO::SimpleJob *job = KIO::listDir(url, FALSE);
00335 KIO::Scheduler::assignJobToSlave(mSlave, job);
00336 insertJob(job, jd);
00337 connect(job, SIGNAL(result(KIO::Job *)),
00338 this, SLOT(slotListResult(KIO::Job *)));
00339 connect(job, SIGNAL(entries(KIO::Job *, const KIO::UDSEntryList &)),
00340 this, SLOT(slotListEntries(KIO::Job *, const KIO::UDSEntryList &)));
00341 }
00342
00343 //-----------------------------------------------------------------------------
00344 void ImapAccountBase::slotListEntries(KIO::Job * job, const KIO::UDSEntryList & uds)
00345 {
00346 JobIterator it = findJob( job );
00347 if ( it == jobsEnd() ) return;
00348 QString name;
00349 KURL url;
00350 QString mimeType;
00351 for (KIO::UDSEntryList::ConstIterator udsIt = uds.begin();
00352 udsIt != uds.end(); udsIt++)
00353 {
00354 mimeType = QString::null;
00355 for (KIO::UDSEntry::ConstIterator eIt = (*udsIt).begin();
00356 eIt != (*udsIt).end(); eIt++)
00357 {
00358 // get the needed information
00359 if ((*eIt).m_uds == KIO::UDS_NAME)
00360 name = (*eIt).m_str;
00361 else if ((*eIt).m_uds == KIO::UDS_URL)
00362 url = KURL((*eIt).m_str, 106); // utf-8
00363 else if ((*eIt).m_uds == KIO::UDS_MIME_TYPE)
00364 mimeType = (*eIt).m_str;
00365 }
00366 if ((mimeType == "inode/directory" || mimeType == "message/digest"
00367 || mimeType == "message/directory")
00368 && name != ".." && (hiddenFolders() || name.at(0) != '.')
00369 && (!(*it).inboxOnly || name.upper() == "INBOX"))
00370 {
00371 if (((*it).inboxOnly ||
00372 url.path() == "/INBOX/") && name.upper() == "INBOX" &&
00373 !mHasInbox)
00374 {
00375 // our INBOX
00376 mCreateInbox = TRUE;
00377 }
00378
00379 // Some servers send _lots_ of duplicates
00380 if (mSubfolderNames.findIndex(name) == -1)
00381 {
00382 mSubfolderNames.append(name);
00383 mSubfolderPaths.append(url.path());
00384 mSubfolderMimeTypes.append(mimeType);
00385 }
00386 }
00387 }
00388 }
00389
00390 //-----------------------------------------------------------------------------
00391 void ImapAccountBase::slotListResult(KIO::Job * job)
00392 {
00393 JobIterator it = findJob( job );
00394 if ( it == jobsEnd() ) return;
00395 if (job->error())
00396 {
00397 slotSlaveError( mSlave, job->error(),
00398 job->errorText() );
00399 }
00400 if (!job->error())
00401 {
00402 // transport the information, include the jobData
00403 emit receivedFolders(mSubfolderNames, mSubfolderPaths,
00404 mSubfolderMimeTypes, *it);
00405 }
00406 if (mSlave) removeJob(job);
00407 mSubfolderNames.clear();
00408 mSubfolderPaths.clear();
00409 mSubfolderMimeTypes.clear();
00410 }
00411
00412 //-----------------------------------------------------------------------------
00413 void ImapAccountBase::changeSubscription( bool subscribe, QString imapPath )
00414 {
00415 // change the subscription of the folder
00416 KURL url = getUrl();
00417 url.setPath(imapPath);
00418
00419 QByteArray packedArgs;
00420 QDataStream stream( packedArgs, IO_WriteOnly);
00421
00422 if (subscribe)
00423 stream << (int) 'u' << url;
00424 else
00425 stream << (int) 'U' << url;
00426
00427 // create the KIO-job
00428 if (makeConnection() != Connected)
00429 return;
00430 KIO::SimpleJob *job = KIO::special(url, packedArgs, FALSE);
00431 KIO::Scheduler::assignJobToSlave(mSlave, job);
00432 jobData jd;
00433 jd.total = 1; jd.done = 0; jd.parent = NULL;
00434 // a bit of a hack to save one slot
00435 if (subscribe) jd.onlySubscribed = true;
00436 else jd.onlySubscribed = false;
00437 insertJob(job, jd);
00438
00439 connect(job, SIGNAL(result(KIO::Job *)),
00440 SLOT(slotSubscriptionResult(KIO::Job *)));
00441 }
00442
00443 //-----------------------------------------------------------------------------
00444 void ImapAccountBase::slotSubscriptionResult( KIO::Job * job )
00445 {
00446 // result of a subscription-job
00447 JobIterator it = findJob( job );
00448 if ( it == jobsEnd() ) return;
00449 if (job->error())
00450 {
00451 slotSlaveError( mSlave, job->error(),
00452 job->errorText() );
00453 } else {
00454 emit subscriptionChanged(
00455 static_cast<KIO::SimpleJob*>(job)->url().path(), (*it).onlySubscribed );
00456 }
00457 if (mSlave) removeJob(job);
00458 }
00459
00460 //-----------------------------------------------------------------------------
00461 void ImapAccountBase::slotSchedulerSlaveError(KIO::Slave *aSlave, int errorCode,
00462 const QString &errorMsg)
00463 {
00464 if (aSlave != mSlave) return;
00465 slotSlaveError( aSlave, errorCode, errorMsg );
00466 emit connectionResult( errorCode );
00467 }
00468
00469 //-----------------------------------------------------------------------------
00470 void ImapAccountBase::slotSchedulerSlaveConnected(KIO::Slave *aSlave)
00471 {
00472 if (aSlave != mSlave) return;
00473 emit connectionResult( 0 ); // success
00474 }
00475
00476 //-----------------------------------------------------------------------------
00477 void ImapAccountBase::slotSlaveError(KIO::Slave *aSlave, int errorCode,
00478 const QString &errorMsg)
00479 {
00480 if (aSlave != mSlave) return;
00481 if (errorCode == KIO::ERR_SLAVE_DIED) slaveDied();
00482 if (errorCode == KIO::ERR_COULD_NOT_LOGIN && !mStorePasswd) mAskAgain = TRUE;
00483 killAllJobs();
00484 // check if we still display an error
00485 if ( !mErrorDialogIsActive )
00486 {
00487 mErrorDialogIsActive = true;
00488 KMessageBox::messageBox(kmkernel->mainWin(), KMessageBox::Error,
00489 KIO::buildErrorString(errorCode, errorMsg),
00490 i18n("Error"));
00491 mErrorDialogIsActive = false;
00492 } else
00493 kdDebug(5006) << "suppressing error:" << errorMsg << endl;
00494 }
00495
00496 //-----------------------------------------------------------------------------
00497 QString ImapAccountBase::jobData::htmlURL() const
00498 {
00499 KURL u( url );
00500 return u.htmlURL();
00501 }
00502
00503 //-----------------------------------------------------------------------------
00504 void ImapAccountBase::processNewMailSingleFolder(KMFolder* folder)
00505 {
00506 mFoldersQueuedForChecking.append(folder);
00507 if (checkingMail())
00508 {
00509 disconnect (this, SIGNAL(finishedCheck(bool)),
00510 this, SLOT(slotCheckQueuedFolders()));
00511 connect (this, SIGNAL(finishedCheck(bool)),
00512 this, SLOT(slotCheckQueuedFolders()));
00513 } else {
00514 slotCheckQueuedFolders();
00515 }
00516 }
00517
00518 //-----------------------------------------------------------------------------
00519 void ImapAccountBase::slotCheckQueuedFolders()
00520 {
00521 disconnect (this, SIGNAL(finishedCheck(bool)),
00522 this, SLOT(slotCheckQueuedFolders()));
00523
00524 QValueList<QGuardedPtr<KMFolder> > mSaveList = mMailCheckFolders;
00525 mMailCheckFolders = mFoldersQueuedForChecking;
00526 kmkernel->acctMgr()->singleCheckMail(this, true);
00527 mMailCheckFolders = mSaveList;
00528 mFoldersQueuedForChecking.clear();
00529 }
00530
00531 //-----------------------------------------------------------------------------
00532 void ImapAccountBase::handleBodyStructure( QDataStream & stream, KMMessage * msg,
00533 const AttachmentStrategy *as )
00534 {
00535 mBodyPartList.clear();
00536 mCurrentMsg = msg;
00537 // make the parts and fill the mBodyPartList
00538 constructParts( stream, 1, 0, 0, msg->asDwMessage() );
00539 if ( mBodyPartList.count() == 1 ) // we directly set the body later
00540 msg->deleteBodyParts();
00541
00542 if ( !as )
00543 {
00544 kdWarning(5006) << "ImapAccountBase::handleBodyStructure - found no attachment strategy!" << endl;
00545 return;
00546 }
00547 // check the size, if the message is smaller than 5KB then load it in one go
00548 if ( msg->msgLength() < 5000 )
00549 {
00550 FolderJob *job = msg->parent()->createJob(
00551 msg, FolderJob::tGetMessage, 0, "TEXT" );
00552 job->start();
00553 return;
00554 }
00555
00556 // download parts according to attachmentstrategy
00557 BodyVisitor *visitor = BodyVisitorFactory::getVisitor( as );
00558 visitor->visit( mBodyPartList );
00559 QPtrList<KMMessagePart> parts = visitor->partsToLoad();
00560 QPtrListIterator<KMMessagePart> it( parts );
00561 KMMessagePart *part;
00562 while ( (part = it.current()) != 0 )
00563 {
00564 ++it;
00565 kdDebug(5006) << "ImapAccountBase::handleBodyStructure - load " << part->partSpecifier()
00566 << " (" << part->originalContentTypeStr() << ")" << endl;
00567 if ( part->loadHeaders() )
00568 {
00569 kdDebug(5006) << "load HEADER" << endl;
00570 FolderJob *job = msg->parent()->createJob(
00571 msg, FolderJob::tGetMessage, 0, part->partSpecifier()+".MIME" );
00572 job->start();
00573 }
00574 if ( part->loadPart() )
00575 {
00576 kdDebug(5006) << "load Part" << endl;
00577 FolderJob *job = msg->parent()->createJob(
00578 msg, FolderJob::tGetMessage, 0, part->partSpecifier() );
00579 job->start();
00580 }
00581 }
00582 delete visitor;
00583 }
00584
00585 //-----------------------------------------------------------------------------
00586 void ImapAccountBase::constructParts( QDataStream & stream, int count, KMMessagePart* parentKMPart,
00587 DwBodyPart * parent, const DwMessage * dwmsg )
00588 {
00589 int children;
00590 for (int i = 0; i < count; i++)
00591 {
00592 stream >> children;
00593 KMMessagePart* part = new KMMessagePart( stream );
00594 part->setParent( parentKMPart );
00595 mBodyPartList.append( part );
00596 kdDebug(5006) << "ImapAccountBase::constructParts - created id " << part->partSpecifier()
00597 << " of type " << part->originalContentTypeStr() << endl;
00598 DwBodyPart *dwpart = mCurrentMsg->createDWBodyPart( part );
00599 dwpart->Parse(); // also creates an encapsulated DwMessage if necessary
00600
00601 // kdDebug(5006) << "constructed dwpart " << dwpart << ",dwmsg " << dwmsg << ",parent " << parent
00602 // << ",dwparts msg " << dwpart->Body().Message() << endl;
00603
00604 if ( parent )
00605 {
00606 // add to parent body
00607 parent->Body().AddBodyPart( dwpart );
00608 } else if ( part->partSpecifier() != "0" &&
00609 !part->partSpecifier().endsWith(".HEADER") )
00610 {
00611 // add to message
00612 dwmsg->Body().AddBodyPart( dwpart );
00613 } else
00614 dwpart = 0;
00615
00616 if ( !parentKMPart )
00617 parentKMPart = part;
00618
00619 if (children > 0)
00620 {
00621 DwBodyPart* newparent = dwpart;
00622 const DwMessage* newmsg = dwmsg;
00623 if ( part->originalContentTypeStr() == "MESSAGE/RFC822" &&
00624 dwpart->Body().Message() )
00625 {
00626 // set the encapsulated message as new parent message
00627 newparent = 0;
00628 newmsg = dwpart->Body().Message();
00629 }
00630 KMMessagePart* newParentKMPart = part;
00631 if ( part->partSpecifier().endsWith(".HEADER") ) // we don't want headers as parent
00632 newParentKMPart = parentKMPart;
00633
00634 constructParts( stream, children, newParentKMPart, newparent, newmsg );
00635 }
00636 }
00637 }
00638
00639 } // namespace KMail
00640
00641 #include "imapaccountbase.moc"