kmail Library API Documentation

imapaccountbase.cpp

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 //using KIO::Scheduler; // use FQN below
00057 
00058 #include <qregexp.h>
00059 
00060 namespace KMail {
00061 
00062   static const unsigned short int imapDefaultPort = 143;
00063 
00064   //
00065   //
00066   // Ctor and Dtor
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   // Getters and Setters
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(); // ### needed while KMFolderCachedImap exists
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   // read/write config
00169   //
00170   //
00171 
00172   void ImapAccountBase::readConfig( /*const*/ KConfig/*Base*/ & 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/*Base*/ & config ) /*const*/ {
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   // Network processing
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       // We init "store" to true to indicate that we want to have the
00216       // "keep password" checkbox. Then, we set [Passwords]Keep to
00217       // storePasswd(), so that the checkbox in the dialog will be
00218       // init'ed correctly:
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       // The user has been given the chance to change login and
00235       // password, so copy both from the dialog:
00236       setPasswd( pass, store );
00237       setLogin( log );
00238       mAskAgain = false; // ### taken from kmacctexppop
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     // count the unread messages
00259     mCountUnread += folder->countUnread();
00260     if (mCountRemainChecks == 0)
00261     {
00262       // all checks are done
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     // create jobData
00315     jobData jd;
00316     jd.total = 1; jd.done = 0;
00317     // reset for a new listing
00318     if (reset)
00319       mHasInbox = false;
00320     // this inboxonly switch is only needed when you set the INBOX as prefix
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     // make the URL
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"
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 Sat May 1 11:37:19 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003