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
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
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);
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
00376 mCreateInbox = TRUE;
00377 }
00378
00379
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
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
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
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
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
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 );
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
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
00538 constructParts( stream, 1, 0, 0, msg->asDwMessage() );
00539 if ( mBodyPartList.count() == 1 )
00540 msg->deleteBodyParts();
00541
00542 if ( !as )
00543 {
00544 kdWarning(5006) << "ImapAccountBase::handleBodyStructure - found no attachment strategy!" << endl;
00545 return;
00546 }
00547
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
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();
00600
00601
00602
00603
00604 if ( parent )
00605 {
00606
00607 parent->Body().AddBodyPart( dwpart );
00608 } else if ( part->partSpecifier() != "0" &&
00609 !part->partSpecifier().endsWith(".HEADER") )
00610 {
00611
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
00627 newparent = 0;
00628 newmsg = dwpart->Body().Message();
00629 }
00630 KMMessagePart* newParentKMPart = part;
00631 if ( part->partSpecifier().endsWith(".HEADER") )
00632 newParentKMPart = parentKMPart;
00633
00634 constructParts( stream, children, newParentKMPart, newparent, newmsg );
00635 }
00636 }
00637 }
00638
00639 }
00640
00641 #include "imapaccountbase.moc"