kmail

kmkernel.cpp

00001 /*  -*- mode: C++; c-file-style: "gnu" -*- */
00002 #ifdef HAVE_CONFIG_H
00003 #include <config.h>
00004 #endif
00005 
00006 #include "config.h"
00007 #include "kmkernel.h"
00008 
00009 #include <weaver.h>
00010 #include <weaverlogger.h>
00011 
00012 #include "globalsettings.h"
00013 #include "broadcaststatus.h"
00014 using KPIM::BroadcastStatus;
00015 #include "kmstartup.h"
00016 #include "index.h"
00017 #include "kmmainwin.h"
00018 #include "composer.h"
00019 #include "kmmsgpart.h"
00020 #include "kmreadermainwin.h"
00021 #include "kmfoldermgr.h"
00022 #include "kmfoldercachedimap.h"
00023 #include "kmacctcachedimap.h"
00024 #include "kmfiltermgr.h"
00025 #include "kmfilteraction.h"
00026 #include "kmheaders.h"
00027 #define REALLY_WANT_KMSENDER
00028 #include "kmsender.h"
00029 #undef REALLY_WANT_KMSENDER
00030 #include "undostack.h"
00031 #include "accountmanager.h"
00032 using KMail::AccountManager;
00033 #include <libkdepim/kfileio.h>
00034 #include "kmversion.h"
00035 #include "kmreaderwin.h"
00036 #include "kmmainwidget.h"
00037 #include "kmfoldertree.h"
00038 #include "recentaddresses.h"
00039 using KRecentAddress::RecentAddresses;
00040 #include "kmmsgdict.h"
00041 #include <libkpimidentities/identity.h>
00042 #include <libkpimidentities/identitymanager.h>
00043 #include "configuredialog.h"
00044 #include "kmcommands.h"
00045 #include "kmsystemtray.h"
00046 #include "transportmanager.h"
00047 
00048 #include <kwin.h>
00049 #include "kmailicalifaceimpl.h"
00050 #include "mailserviceimpl.h"
00051 using KMail::MailServiceImpl;
00052 #include "mailcomposerIface.h"
00053 #include "folderIface.h"
00054 using KMail::FolderIface;
00055 #include "jobscheduler.h"
00056 #include "templateparser.h"
00057 
00058 #include <kapplication.h>
00059 #include <kmessagebox.h>
00060 #include <knotifyclient.h>
00061 #include <kstaticdeleter.h>
00062 #include <kstandarddirs.h>
00063 #include <kconfig.h>
00064 #include <kprogress.h>
00065 #include <kpassivepopup.h>
00066 #include <dcopclient.h>
00067 #include <ksystemtray.h>
00068 #include <kpgp.h>
00069 #include <kdebug.h>
00070 #include <kio/netaccess.h>
00071 #include <kwallet.h>
00072 using KWallet::Wallet;
00073 #include "actionscheduler.h"
00074 
00075 #include <qutf7codec.h>
00076 #include <qvbox.h>
00077 #include <qdir.h>
00078 #include <qwidgetlist.h>
00079 #include <qobjectlist.h>
00080 
00081 #include <sys/types.h>
00082 #include <dirent.h>
00083 #include <sys/stat.h>
00084 #include <unistd.h>
00085 #include <stdio.h>
00086 #include <stdlib.h>
00087 #include <assert.h>
00088 
00089 #include <X11/Xlib.h>
00090 #include <fixx11h.h>
00091 #include <kcmdlineargs.h>
00092 #include <kstartupinfo.h>
00093 
00094 KMKernel *KMKernel::mySelf = 0;
00095 
00096 /********************************************************************/
00097 /*                     Constructor and destructor                   */
00098 /********************************************************************/
00099 KMKernel::KMKernel (QObject *parent, const char *name) :
00100   DCOPObject("KMailIface"), QObject(parent, name),
00101   mIdentityManager(0), mConfigureDialog(0),
00102   mContextMenuShown( false ), mWallet( 0 )
00103 {
00104   kdDebug(5006) << "KMKernel::KMKernel" << endl;
00105   mySelf = this;
00106   the_startingUp = true;
00107   closed_by_user = true;
00108   the_firstInstance = true;
00109   the_msgIndex = 0;
00110 
00111   the_inboxFolder = 0;
00112   the_outboxFolder = 0;
00113   the_sentFolder = 0;
00114   the_trashFolder = 0;
00115   the_draftsFolder = 0;
00116   the_templatesFolder = 0;
00117 
00118   the_folderMgr = 0;
00119   the_imapFolderMgr = 0;
00120   the_dimapFolderMgr = 0;
00121   the_searchFolderMgr = 0;
00122   the_undoStack = 0;
00123   the_acctMgr = 0;
00124   the_filterMgr = 0;
00125   the_popFilterMgr = 0;
00126   the_filterActionDict = 0;
00127   the_msgSender = 0;
00128   mWin = 0;
00129   mMailCheckAborted = false;
00130 
00131   // make sure that we check for config updates before doing anything else
00132   KMKernel::config();
00133   // this shares the kmailrc parsing too (via KSharedConfig), and reads values from it
00134   // so better do it here, than in some code where changing the group of config()
00135   // would be unexpected
00136   GlobalSettings::self();
00137 
00138   // Set up DCOP interface
00139   mICalIface = new KMailICalIfaceImpl();
00140 
00141   mJobScheduler = new JobScheduler( this );
00142 
00143   mXmlGuiInstance = 0;
00144 
00145   new Kpgp::Module();
00146 
00147   // register our own (libkdenetwork) utf-7 codec as long as Qt
00148   // doesn't have it's own:
00149   if ( !QTextCodec::codecForName("utf-7") ) {
00150     kdDebug(5006) << "No Qt-native utf-7 codec found; registering QUtf7Codec from libkdenetwork" << endl;
00151     (void) new QUtf7Codec();
00152   }
00153 
00154   // In the case of Japan. Japanese locale name is "eucjp" but
00155   // The Japanese mail systems normally used "iso-2022-jp" of locale name.
00156   // We want to change locale name from eucjp to iso-2022-jp at KMail only.
00157   if ( QCString(QTextCodec::codecForLocale()->name()).lower() == "eucjp" )
00158   {
00159     netCodec = QTextCodec::codecForName("jis7");
00160     // QTextCodec *cdc = QTextCodec::codecForName("jis7");
00161     // QTextCodec::setCodecForLocale(cdc);
00162     // KGlobal::locale()->setEncoding(cdc->mibEnum());
00163   } else {
00164     netCodec = QTextCodec::codecForLocale();
00165   }
00166   mMailService =  new MailServiceImpl();
00167 
00168   connectDCOPSignal( 0, 0, "kmailSelectFolder(QString)",
00169                      "selectFolder(QString)", false );
00170 }
00171 
00172 KMKernel::~KMKernel ()
00173 {
00174   QMap<KIO::Job*, putData>::Iterator it = mPutJobs.begin();
00175   while ( it != mPutJobs.end() )
00176   {
00177     KIO::Job *job = it.key();
00178     mPutJobs.remove( it );
00179     job->kill();
00180     it = mPutJobs.begin();
00181   }
00182 
00183   delete mICalIface;
00184   mICalIface = 0;
00185   delete mMailService;
00186   mMailService = 0;
00187 
00188   GlobalSettings::self()->writeConfig();
00189   delete mWallet;
00190   mWallet = 0;
00191   mySelf = 0;
00192   kdDebug(5006) << "KMKernel::~KMKernel" << endl;
00193 }
00194 
00195 bool KMKernel::handleCommandLine( bool noArgsOpensReader )
00196 {
00197   QString to, cc, bcc, subj, body;
00198   QCStringList customHeaders;
00199   KURL messageFile;
00200   KURL::List attachURLs;
00201   bool mailto = false;
00202   bool checkMail = false;
00203   bool viewOnly = false;
00204   bool calledWithSession = false; // for ignoring '-session foo'
00205 
00206   // process args:
00207   KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
00208   if (args->getOption("subject"))
00209   {
00210      subj = QString::fromLocal8Bit(args->getOption("subject"));
00211      // if kmail is called with 'kmail -session abc' then this doesn't mean
00212      // that the user wants to send a message with subject "ession" but
00213      // (most likely) that the user clicked on KMail's system tray applet
00214      // which results in KMKernel::raise() calling "kmail kmail newInstance"
00215      // via dcop which apparently executes the application with the original
00216      // command line arguments and those include "-session ..." if
00217      // kmail/kontact was restored by session management
00218      if ( subj == "ession" ) {
00219        subj = QString::null;
00220        calledWithSession = true;
00221      }
00222      else
00223        mailto = true;
00224   }
00225 
00226   if (args->getOption("cc"))
00227   {
00228      mailto = true;
00229      cc = QString::fromLocal8Bit(args->getOption("cc"));
00230   }
00231 
00232   if (args->getOption("bcc"))
00233   {
00234      mailto = true;
00235      bcc = QString::fromLocal8Bit(args->getOption("bcc"));
00236   }
00237 
00238   if (args->getOption("msg"))
00239   {
00240      mailto = true;
00241      messageFile.setPath( QString::fromLocal8Bit(args->getOption("msg")) );
00242   }
00243 
00244   if (args->getOption("body"))
00245   {
00246      mailto = true;
00247      body = QString::fromLocal8Bit(args->getOption("body"));
00248   }
00249 
00250   QCStringList attachList = args->getOptionList("attach");
00251   if (!attachList.isEmpty())
00252   {
00253      mailto = true;
00254      for ( QCStringList::Iterator it = attachList.begin() ; it != attachList.end() ; ++it )
00255        if ( !(*it).isEmpty() )
00256          attachURLs += KURL( QString::fromLocal8Bit( *it ) );
00257   }
00258 
00259   customHeaders = args->getOptionList("header");
00260 
00261   if (args->isSet("composer"))
00262     mailto = true;
00263 
00264   if (args->isSet("check"))
00265     checkMail = true;
00266 
00267   if ( args->getOption( "view" ) ) {
00268     viewOnly = true;
00269     const QString filename =
00270       QString::fromLocal8Bit( args->getOption( "view" ) );
00271     messageFile = KURL::fromPathOrURL( filename );
00272     if ( !messageFile.isValid() ) {
00273       messageFile = KURL();
00274       messageFile.setPath( filename );
00275     }
00276   }
00277 
00278   if ( !calledWithSession ) {
00279     // only read additional command line arguments if kmail/kontact is
00280     // not called with "-session foo"
00281     for(int i= 0; i < args->count(); i++)
00282     {
00283       if (strncasecmp(args->arg(i),"mailto:",7)==0)
00284         to += args->url(i).path() + ", ";
00285       else {
00286         QString tmpArg = QString::fromLocal8Bit( args->arg(i) );
00287         KURL url( tmpArg );
00288         if ( url.isValid() )
00289           attachURLs += url;
00290         else
00291           to += tmpArg + ", ";
00292       }
00293       mailto = true;
00294     }
00295     if ( !to.isEmpty() ) {
00296       // cut off the superfluous trailing ", "
00297       to.truncate( to.length() - 2 );
00298     }
00299   }
00300 
00301   if ( !calledWithSession )
00302     args->clear();
00303 
00304   if ( !noArgsOpensReader && !mailto && !checkMail && !viewOnly )
00305     return false;
00306 
00307   if ( viewOnly )
00308     viewMessage( messageFile );
00309   else
00310     action( mailto, checkMail, to, cc, bcc, subj, body, messageFile,
00311             attachURLs, customHeaders );
00312   return true;
00313 }
00314 
00315 /********************************************************************/
00316 /*             DCOP-callable, and command line actions              */
00317 /********************************************************************/
00318 void KMKernel::checkMail () //might create a new reader but won't show!!
00319 {
00320   if ( !kmkernel->askToGoOnline() )
00321     return;
00322   kmkernel->acctMgr()->checkMail(false);
00323 }
00324 
00325 QStringList KMKernel::accounts()
00326 {
00327   if( kmkernel->acctMgr() )
00328      return kmkernel->acctMgr()->getAccounts();
00329   return QStringList();
00330 }
00331 
00332 void KMKernel::checkAccount (const QString &account) //might create a new reader but won't show!!
00333 {
00334   kdDebug(5006) << "KMKernel::checkMail called" << endl;
00335 
00336   KMAccount* acct = kmkernel->acctMgr()->findByName(account);
00337   if (acct)
00338     kmkernel->acctMgr()->singleCheckMail(acct, false);
00339 }
00340 
00341 void KMKernel::loadProfile( const QString& )
00342 {
00343 }
00344 
00345 void KMKernel::saveToProfile( const QString& ) const
00346 {
00347 }
00348 
00349 void KMKernel::openReader( bool onlyCheck )
00350 {
00351   mWin = 0;
00352   KMainWindow *ktmw = 0;
00353   kdDebug(5006) << "KMKernel::openReader called" << endl;
00354 
00355   if (KMainWindow::memberList)
00356     for (ktmw = KMainWindow::memberList->first(); ktmw;
00357          ktmw = KMainWindow::memberList->next())
00358       if (ktmw->isA("KMMainWin"))
00359         break;
00360 
00361   bool activate;
00362   if (ktmw) {
00363     mWin = (KMMainWin *) ktmw;
00364     activate = !onlyCheck; // existing window: only activate if not --check
00365     if ( activate )
00366        mWin->show();
00367   } else {
00368     mWin = new KMMainWin;
00369     mWin->show();
00370     activate = false; // new window: no explicit activation (#73591)
00371   }
00372 
00373   if ( activate ) {
00374     // Activate window - doing this instead of KWin::activateWindow(mWin->winId());
00375     // so that it also works when called from KMailApplication::newInstance()
00376 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00377     KStartupInfo::setNewStartupId( mWin, kapp->startupId() );
00378 #endif
00379   }
00380 }
00381 
00382 int KMKernel::openComposer (const QString &to, const QString &cc,
00383                             const QString &bcc, const QString &subject,
00384                             const QString &body, int hidden,
00385                             const KURL &messageFile,
00386                             const KURL::List &attachURLs,
00387                             const QCStringList &customHeaders)
00388 {
00389   kdDebug(5006) << "KMKernel::openComposer called" << endl;
00390   KMMessage *msg = new KMMessage;
00391   msg->initHeader();
00392   msg->setCharset("utf-8");
00393   // tentatively decode to, cc and bcc because invokeMailer calls us with
00394   // RFC 2047 encoded addresses in order to protect non-ASCII email addresses
00395   if (!to.isEmpty())
00396     msg->setTo( KMMsgBase::decodeRFC2047String( to.latin1() ) );
00397   if (!cc.isEmpty())
00398     msg->setCc( KMMsgBase::decodeRFC2047String( cc.latin1() ) );
00399   if (!bcc.isEmpty())
00400     msg->setBcc( KMMsgBase::decodeRFC2047String( bcc.latin1() ) );
00401   if (!subject.isEmpty()) msg->setSubject(subject);
00402   if (!messageFile.isEmpty() && messageFile.isLocalFile()) {
00403     QCString str = KPIM::kFileToString( messageFile.path(), true, false );
00404     if( !str.isEmpty() ) {
00405       msg->setBody( QString::fromLocal8Bit( str ).utf8() );
00406     } else {
00407       TemplateParser parser( msg, TemplateParser::NewMessage,
00408     "", false, false, false, false );
00409       parser.process( NULL, NULL );
00410     }
00411   }
00412   else if (!body.isEmpty())
00413   {
00414     msg->setBody(body.utf8());
00415   }
00416   else
00417   {
00418     TemplateParser parser( msg, TemplateParser::NewMessage,
00419       "", false, false, false, false );
00420     parser.process( NULL, NULL );
00421   }
00422 
00423   if (!customHeaders.isEmpty())
00424   {
00425     for ( QCStringList::ConstIterator it = customHeaders.begin() ; it != customHeaders.end() ; ++it )
00426       if ( !(*it).isEmpty() )
00427       {
00428         const int pos = (*it).find( ':' );
00429         if ( pos > 0 )
00430         {
00431           QCString header, value;
00432           header = (*it).left( pos ).stripWhiteSpace();
00433           value = (*it).mid( pos+1 ).stripWhiteSpace();
00434           if ( !header.isEmpty() && !value.isEmpty() )
00435             msg->setHeaderField( header, value );
00436         }
00437       }
00438   }
00439 
00440   KMail::Composer * cWin = KMail::makeComposer( msg );
00441   cWin->setCharset("", true);
00442   for ( KURL::List::ConstIterator it = attachURLs.begin() ; it != attachURLs.end() ; ++it )
00443     cWin->addAttach((*it));
00444   if (hidden == 0) {
00445     cWin->show();
00446     // Activate window - doing this instead of KWin::activateWindow(cWin->winId());
00447     // so that it also works when called from KMailApplication::newInstance()
00448 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00449     KStartupInfo::setNewStartupId( cWin, kapp->startupId() );
00450 #endif
00451   }
00452   return 1;
00453 }
00454 
00455 
00456 int KMKernel::openComposer (const QString &to, const QString &cc,
00457                             const QString &bcc, const QString &subject,
00458                             const QString &body, int hidden,
00459                             const QString &attachName,
00460                             const QCString &attachCte,
00461                             const QCString &attachData,
00462                             const QCString &attachType,
00463                             const QCString &attachSubType,
00464                             const QCString &attachParamAttr,
00465                             const QString &attachParamValue,
00466                             const QCString &attachContDisp )
00467 {
00468   kdDebug(5006) << "KMKernel::openComposer called (deprecated version)" << endl;
00469 
00470   return openComposer ( to, cc, bcc, subject, body, hidden,
00471                         attachName, attachCte, attachData,
00472                         attachType, attachSubType, attachParamAttr,
00473                         attachParamValue, attachContDisp, QCString() );
00474 }
00475 
00476 int KMKernel::openComposer (const QString &to, const QString &cc,
00477                             const QString &bcc, const QString &subject,
00478                             const QString &body, int hidden,
00479                             const QString &attachName,
00480                             const QCString &attachCte,
00481                             const QCString &attachData,
00482                             const QCString &attachType,
00483                             const QCString &attachSubType,
00484                             const QCString &attachParamAttr,
00485                             const QString &attachParamValue,
00486                             const QCString &attachContDisp,
00487                             const QCString &attachCharset )
00488 {
00489   kdDebug(5006) << "KMKernel::openComposer called (deprecated version)" << endl;
00490   return openComposer ( to, cc, bcc, subject, body, hidden,
00491                         attachName, attachCte, attachData,
00492                         attachType, attachSubType, attachParamAttr,
00493                         attachParamValue, attachContDisp, attachCharset, 0 );
00494 }
00495 
00496 int KMKernel::openComposer (const QString &to, const QString &cc,
00497                             const QString &bcc, const QString &subject,
00498                             const QString &body, int hidden,
00499                             const QString &attachName,
00500                             const QCString &attachCte,
00501                             const QCString &attachData,
00502                             const QCString &attachType,
00503                             const QCString &attachSubType,
00504                             const QCString &attachParamAttr,
00505                             const QString &attachParamValue,
00506                             const QCString &attachContDisp,
00507                             const QCString &attachCharset,
00508                             unsigned int identity )
00509 {
00510   kdDebug(5006) << "KMKernel::openComposer()" << endl;
00511 
00512   KMMessage *msg = new KMMessage;
00513   KMMessagePart *msgPart = 0;
00514   msg->initHeader();
00515   msg->setCharset( "utf-8" );
00516   if ( !cc.isEmpty() ) msg->setCc(cc);
00517   if ( !bcc.isEmpty() ) msg->setBcc(bcc);
00518   if ( !subject.isEmpty() ) msg->setSubject(subject);
00519   if ( !to.isEmpty() ) msg->setTo(to);
00520   if ( identity > 0 ) msg->setHeaderField( "X-KMail-Identity", QString::number( identity ) );
00521   if ( !body.isEmpty() ) {
00522     msg->setBody(body.utf8());
00523   } else {
00524     TemplateParser parser( msg, TemplateParser::NewMessage,
00525       "", false, false, false, false );
00526     parser.process( NULL, NULL );
00527   }
00528 
00529   bool iCalAutoSend = false;
00530   bool noWordWrap = false;
00531   bool isICalInvitation = false;
00532   KConfigGroup options( config(), "Groupware" );
00533   if ( !attachData.isEmpty() ) {
00534     isICalInvitation = attachName == "cal.ics" &&
00535       attachType == "text" &&
00536       attachSubType == "calendar" &&
00537       attachParamAttr == "method";
00538     // Remove BCC from identity on ical invitations (https://intevation.de/roundup/kolab/issue474)
00539     if ( isICalInvitation && bcc.isEmpty() )
00540       msg->setBcc( "" );
00541     if ( isICalInvitation &&
00542         GlobalSettings::self()->legacyBodyInvites() ) {
00543       // KOrganizer invitation caught and to be sent as body instead
00544       msg->setBody( attachData );
00545       msg->setHeaderField( "Content-Type",
00546                            QString( "text/calendar; method=%1; "
00547                                     "charset=\"utf-8\"" ).
00548                            arg( attachParamValue ) );
00549 
00550       iCalAutoSend = true; // no point in editing raw ICAL
00551       noWordWrap = true; // we shant word wrap inline invitations
00552     } else {
00553       // Just do what we're told to do
00554       msgPart = new KMMessagePart;
00555       msgPart->setName( attachName );
00556       msgPart->setCteStr( attachCte );
00557       msgPart->setBodyEncoded( attachData );
00558       msgPart->setTypeStr( attachType );
00559       msgPart->setSubtypeStr( attachSubType );
00560       msgPart->setParameter( attachParamAttr, attachParamValue );
00561        if( ! GlobalSettings::self()->exchangeCompatibleInvitations() ) {
00562         msgPart->setContentDisposition( attachContDisp );
00563       }
00564       if( !attachCharset.isEmpty() ) {
00565         // kdDebug(5006) << "KMKernel::openComposer set attachCharset to "
00566         // << attachCharset << endl;
00567         msgPart->setCharset( attachCharset );
00568       }
00569       // Don't show the composer window, if the automatic sending is checked
00570       KConfigGroup options( config(), "Groupware" );
00571       iCalAutoSend = options.readBoolEntry( "AutomaticSending", true );
00572     }
00573   }
00574 
00575   KMail::Composer * cWin = KMail::makeComposer();
00576   cWin->setMsg( msg, !isICalInvitation /* mayAutoSign */ );
00577   cWin->setSigningAndEncryptionDisabled( isICalInvitation
00578       && GlobalSettings::self()->legacyBodyInvites() );
00579   cWin->setAutoDelete( true );
00580   if( noWordWrap )
00581     cWin->disableWordWrap();
00582   else
00583     cWin->setCharset( "", true );
00584   if ( msgPart )
00585     cWin->addAttach(msgPart);
00586 
00587   if ( isICalInvitation ) {
00588     cWin->disableRecipientNumberCheck();
00589   }
00590 
00591   if ( hidden == 0 && !iCalAutoSend ) {
00592     cWin->show();
00593     // Activate window - doing this instead of KWin::activateWindow(cWin->winId());
00594     // so that it also works when called from KMailApplication::newInstance()
00595 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00596     KStartupInfo::setNewStartupId( cWin, kapp->startupId() );
00597 #endif
00598   } else {
00599     cWin->setAutoDeleteWindow( true );
00600     cWin->slotSendNow();
00601   }
00602 
00603   return 1;
00604 }
00605 
00606 void KMKernel::setDefaultTransport( const QString & transport )
00607 {
00608   QStringList availTransports = KMail::TransportManager::transportNames();
00609   QStringList::const_iterator it = availTransports.find( transport );
00610   if ( it == availTransports.end() ) {
00611     kdWarning() << "The transport you entered is not available" << endl;
00612     return;
00613   }
00614   GlobalSettings::self()->setDefaultTransport( transport );
00615 }
00616 
00617 DCOPRef KMKernel::openComposer(const QString &to, const QString &cc,
00618                                const QString &bcc, const QString &subject,
00619                                const QString &body,bool hidden)
00620 {
00621   KMMessage *msg = new KMMessage;
00622   msg->initHeader();
00623   msg->setCharset("utf-8");
00624   if (!cc.isEmpty()) msg->setCc(cc);
00625   if (!bcc.isEmpty()) msg->setBcc(bcc);
00626   if (!subject.isEmpty()) msg->setSubject(subject);
00627   if (!to.isEmpty()) msg->setTo(to);
00628   if (!body.isEmpty()) {
00629     msg->setBody(body.utf8());
00630   } else {
00631     TemplateParser parser( msg, TemplateParser::NewMessage,
00632       "", false, false, false, false );
00633     parser.process( NULL, NULL );
00634   }
00635 
00636   KMail::Composer * cWin = KMail::makeComposer( msg );
00637   cWin->setCharset("", true);
00638   if (!hidden) {
00639     cWin->show();
00640     // Activate window - doing this instead of KWin::activateWindow(cWin->winId());
00641     // so that it also works when called from KMailApplication::newInstance()
00642 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00643     KStartupInfo::setNewStartupId( cWin, kapp->startupId() );
00644 #endif
00645   }
00646 
00647   return DCOPRef( cWin->asMailComposerIFace() );
00648 }
00649 
00650 DCOPRef KMKernel::newMessage(const QString &to,
00651                              const QString &cc,
00652                              const QString &bcc,
00653                              bool hidden,
00654                              bool useFolderId,
00655                              const KURL & /*messageFile*/,
00656                              const KURL &attachURL)
00657 {
00658   KMail::Composer * win = 0;
00659   KMMessage *msg = new KMMessage;
00660   KMFolder *folder = NULL;
00661   uint id;
00662 
00663   if ( useFolderId ) {
00664     //create message with required folder identity
00665     folder = currentFolder();
00666     id = folder ? folder->identity() : 0;
00667     msg->initHeader( id );
00668   } else {
00669     msg->initHeader();
00670   }
00671   msg->setCharset("utf-8");
00672   //set basic headers
00673   if (!to.isEmpty()) msg->setTo(to);
00674   if (!cc.isEmpty()) msg->setCc(cc);
00675   if (!bcc.isEmpty()) msg->setBcc(bcc);
00676 
00677   if ( useFolderId ) {
00678     TemplateParser parser( msg, TemplateParser::NewMessage,
00679       "", false, false, false, false );
00680     parser.process( NULL, folder );
00681     win = makeComposer( msg, id );
00682   } else {
00683     TemplateParser parser( msg, TemplateParser::NewMessage,
00684       "", false, false, false, false );
00685     parser.process( NULL, folder );
00686     win = makeComposer( msg );
00687   }
00688 
00689   //Add the attachment if we have one
00690   if(!attachURL.isEmpty() && attachURL.isValid()) {
00691     win->addAttach(attachURL);
00692   }
00693 
00694   //only show window when required
00695   if(!hidden) {
00696     win->show();
00697   }
00698   return DCOPRef( win->asMailComposerIFace() );
00699 }
00700 
00701 int KMKernel::viewMessage( const KURL & messageFile )
00702 {
00703   KMOpenMsgCommand *openCommand = new KMOpenMsgCommand( 0, messageFile );
00704 
00705   openCommand->start();
00706 
00707   return 1;
00708 }
00709 
00710 int KMKernel::sendCertificate( const QString& to, const QByteArray& certData )
00711 {
00712   KMMessage *msg = new KMMessage;
00713   msg->initHeader();
00714   msg->setCharset("utf-8");
00715   msg->setSubject( i18n( "Certificate Signature Request" ) );
00716   if (!to.isEmpty()) msg->setTo(to);
00717   // ### Make this message customizable via KIOSK
00718   msg->setBody( i18n( "Please create a certificate from attachment and return to sender." ).utf8() );
00719 
00720   KMail::Composer * cWin = KMail::makeComposer( msg );
00721   cWin->setCharset("", true);
00722   cWin->slotSetAlwaysSend( true );
00723   if (!certData.isEmpty()) {
00724     KMMessagePart *msgPart = new KMMessagePart;
00725     msgPart->setName("smime.p10");
00726     msgPart->setCteStr("base64");
00727     msgPart->setBodyEncodedBinary(certData);
00728     msgPart->setTypeStr("application");
00729     msgPart->setSubtypeStr("pkcs10");
00730     msgPart->setContentDisposition("attachment; filename=smime.p10");
00731     cWin->addAttach(msgPart);
00732   }
00733 
00734   cWin->show();
00735   return 1;
00736 }
00737 
00738 KMMsgStatus KMKernel::strToStatus(const QString &flags)
00739 {
00740     KMMsgStatus status = 0;
00741     if (!flags.isEmpty()) {
00742         for (uint n = 0; n < flags.length() ; n++) {
00743             switch (flags[n]) {
00744                 case 'N':
00745                     status |= KMMsgStatusNew;
00746                     break;
00747                 case 'U':
00748                     status |= KMMsgStatusUnread;
00749                     break;
00750                 case 'O':
00751                     status |= KMMsgStatusOld;
00752                     break;
00753                 case 'R':
00754                     status |= KMMsgStatusRead;
00755                     break;
00756                 case 'D':
00757                     status |= KMMsgStatusDeleted;
00758                     break;
00759                 case 'A':
00760                     status |= KMMsgStatusReplied;
00761                     break;
00762                 case 'F':
00763                     status |= KMMsgStatusForwarded;
00764                     break;
00765                 case 'Q':
00766                     status |= KMMsgStatusQueued;
00767                     break;
00768                 case 'K':
00769                     status |= KMMsgStatusTodo;
00770                     break;
00771                 case 'S':
00772                     status |= KMMsgStatusSent;
00773                     break;
00774                 case 'G':
00775                     status |= KMMsgStatusFlag;
00776                     break;
00777                 case 'W':
00778                     status |= KMMsgStatusWatched;
00779                     break;
00780                 case 'I':
00781                     status |= KMMsgStatusIgnored;
00782                     break;
00783                 case 'P':
00784                     status |= KMMsgStatusSpam;
00785                     break;
00786                 case 'H':
00787                     status |= KMMsgStatusHam;
00788                     break;
00789                 case 'T':
00790                     status |= KMMsgStatusHasAttach;
00791                     break;
00792                 case 'C':
00793                     status |= KMMsgStatusHasNoAttach;
00794                     break;
00795                 default:
00796                     break;
00797             }
00798         }
00799     }
00800     return status;
00801 }
00802 
00803 int KMKernel::dcopAddMessage( const QString & foldername, const QString & msgUrlString,
00804                               const QString & MsgStatusFlags)
00805 {
00806   return dcopAddMessage(foldername, KURL(msgUrlString), MsgStatusFlags);
00807 }
00808 
00809 int KMKernel::dcopAddMessage( const QString & foldername,const KURL & msgUrl,
00810                               const QString & MsgStatusFlags)
00811 {
00812   kdDebug(5006) << "KMKernel::dcopAddMessage called" << endl;
00813 
00814   if ( foldername.isEmpty() || foldername.startsWith("."))
00815     return -1;
00816 
00817   int retval;
00818   bool readFolderMsgIds = false;
00819   QString _foldername = foldername.stripWhiteSpace();
00820   _foldername = _foldername.replace('\\',""); //try to prevent ESCAPE Sequences
00821 
00822   if ( foldername != mAddMessageLastFolder ) {
00823     mAddMessageMsgIds.clear();
00824     readFolderMsgIds = true;
00825     mAddMessageLastFolder = foldername;
00826   }
00827 
00828   if (!msgUrl.isEmpty() && msgUrl.isLocalFile()) {
00829 
00830     // This is a proposed change by Daniel Andor.
00831     // He proposed to change from the fopen(blah)
00832     // to a KPIM::kFileToString(blah).
00833     // Although it assigns a QString to a QString,
00834     // because of the implicit sharing this poses
00835     // no memory or performance penalty.
00836 
00837     const QCString messageText =
00838       KPIM::kFileToString( msgUrl.path(), true, false );
00839     if ( messageText.isEmpty() )
00840       return -2;
00841 
00842     KMMessage *msg = new KMMessage();
00843     msg->fromString( messageText );
00844 
00845     if (readFolderMsgIds) {
00846       if ( foldername.contains("/")) {
00847         QString tmp_fname = "";
00848         KMFolder *folder = NULL;
00849         KMFolderDir *subfolder;
00850         bool root = true;
00851 
00852         QStringList subFList = QStringList::split("/",_foldername,false);
00853 
00854         for ( QStringList::Iterator it = subFList.begin(); it != subFList.end(); ++it ) {
00855           QString _newFolder = *it;
00856           if(_newFolder.startsWith(".")) return -1;
00857 
00858           if(root) {
00859             folder = the_folderMgr->findOrCreate(*it, false);
00860             if (folder) {
00861               root = false;
00862               tmp_fname = "/" + *it;
00863             }
00864             else return -1;
00865           } else {
00866             subfolder = folder->createChildFolder();
00867             tmp_fname += "/" + *it;
00868             if(!the_folderMgr->getFolderByURL( tmp_fname )) {
00869              folder = the_folderMgr->createFolder(*it, false, folder->folderType(), subfolder);
00870             }
00871 
00872             if(!(folder = the_folderMgr->getFolderByURL( tmp_fname ))) return -1;
00873           }
00874         }
00875 
00876         mAddMsgCurrentFolder = the_folderMgr->getFolderByURL( tmp_fname );
00877         if(!folder) return -1;
00878 
00879       } else {
00880         mAddMsgCurrentFolder = the_folderMgr->findOrCreate(_foldername, false);
00881       }
00882     }
00883 
00884     if ( mAddMsgCurrentFolder ) {
00885       if (readFolderMsgIds) {
00886 
00887         // OLD COMMENT:
00888         // Try to determine if a message already exists in
00889         // the folder. The message id that is searched for, is
00890         // the subject line + the date. This should be quite
00891         // unique. The change that a given date with a given
00892         // subject is in the folder twice is very small.
00893         // If the subject is empty, the fromStrip string
00894         // is taken.
00895 
00896     // NEW COMMENT from Danny Kukawka (danny.kukawka@web.de):
00897     // subject line + the date is only unique if the following
00898     // return a correct unique value:
00899     //  time_t  DT = mb->date();
00900         //  QString dt = ctime(&DT);
00901     // But if the datestring in the Header isn't RFC conform
00902     // subject line + the date isn't unique.
00903     //
00904     // The only uique headerfield is the Message-ID. In some
00905     // cases this could be empty. I then I use the
00906     // subject line + dateStr .
00907 
00908         int i;
00909 
00910         mAddMsgCurrentFolder->open("dcopadd");
00911         for( i=0; i<mAddMsgCurrentFolder->count(); i++) {
00912           KMMsgBase *mb = mAddMsgCurrentFolder->getMsgBase(i);
00913       QString id = mb->msgIdMD5();
00914       if ( id.isEmpty() ) {
00915             id = mb->subject();
00916             if ( id.isEmpty() )
00917               id = mb->fromStrip();
00918             if ( id.isEmpty() )
00919               id = mb->toStrip();
00920 
00921             id += mb->dateStr();
00922       }
00923 
00924           //fprintf(stderr,"%s\n",(const char *) id);
00925           if ( !id.isEmpty() ) {
00926             mAddMessageMsgIds.append(id);
00927           }
00928         }
00929         mAddMsgCurrentFolder->close("dcopadd");
00930       }
00931 
00932       QString msgId = msg->msgIdMD5();
00933       if ( msgId.isEmpty()) {
00934     msgId = msg->subject();
00935     if ( msgId.isEmpty() )
00936           msgId = msg->fromStrip();
00937         if ( msgId.isEmpty() )
00938           msgId = msg->toStrip();
00939 
00940     msgId += msg->dateStr();
00941       }
00942 
00943       int k = mAddMessageMsgIds.findIndex( msgId );
00944       //fprintf(stderr,"find %s = %d\n",(const char *) msgId,k);
00945 
00946       if ( k == -1 ) {
00947         if ( !msgId.isEmpty() ) {
00948           mAddMessageMsgIds.append( msgId );
00949         }
00950 
00951         if ( !MsgStatusFlags.isEmpty() ) {
00952           KMMsgStatus status = strToStatus(MsgStatusFlags);
00953           if (status) msg->setStatus(status);
00954         }
00955 
00956         int index;
00957         if ( mAddMsgCurrentFolder->addMsg( msg, &index ) == 0 ) {
00958           mAddMsgCurrentFolder->unGetMsg( index );
00959           retval = 1;
00960         } else {
00961           retval =- 2;
00962           delete msg;
00963           msg = 0;
00964         }
00965       } else {
00966         //qDebug( "duplicate: " + msgId + "; subj: " + msg->subject() + ", from: " + msgId = msg->fromStrip());
00967     retval = -4;
00968       }
00969     } else {
00970       retval = -1;
00971     }
00972   } else {
00973     retval = -2;
00974   }
00975   return retval;
00976 }
00977 
00978 void KMKernel::dcopResetAddMessage()
00979 {
00980   mAddMessageMsgIds.clear();
00981   mAddMessageLastFolder = QString();
00982 }
00983 
00984 int KMKernel::dcopAddMessage_fastImport( const QString & foldername,
00985                                          const QString & msgUrlString,
00986                                          const QString & MsgStatusFlags)
00987 {
00988   return dcopAddMessage_fastImport(foldername, KURL(msgUrlString), MsgStatusFlags);
00989 }
00990 
00991 int KMKernel::dcopAddMessage_fastImport( const QString & foldername,
00992                                          const KURL & msgUrl,
00993                                          const QString & MsgStatusFlags)
00994 {
00995   // Use this function to import messages without
00996   // search for already existing emails.
00997   kdDebug(5006) << "KMKernel::dcopAddMessage_fastImport called" << endl;
00998 
00999   if ( foldername.isEmpty() || foldername.startsWith("."))
01000     return -1;
01001 
01002   int retval;
01003   bool createNewFolder = false;
01004 
01005   QString _foldername = foldername.stripWhiteSpace();
01006   _foldername = _foldername.replace('\\',""); //try to prevent ESCAPE Sequences
01007 
01008   if ( foldername != mAddMessageLastFolder ) {
01009     createNewFolder = true;
01010     mAddMessageLastFolder = foldername;
01011   }
01012 
01013 
01014   if ( !msgUrl.isEmpty() && msgUrl.isLocalFile() ) {
01015     const QCString messageText =
01016       KPIM::kFileToString( msgUrl.path(), true, false );
01017     if ( messageText.isEmpty() )
01018       return -2;
01019 
01020     KMMessage *msg = new KMMessage();
01021     msg->fromString( messageText );
01022 
01023     if (createNewFolder) {
01024       if ( foldername.contains("/")) {
01025         QString tmp_fname = "";
01026         KMFolder *folder = NULL;
01027         KMFolderDir *subfolder;
01028         bool root = true;
01029 
01030         QStringList subFList = QStringList::split("/",_foldername,false);
01031 
01032         for ( QStringList::Iterator it = subFList.begin(); it != subFList.end(); ++it ) {
01033           QString _newFolder = *it;
01034           if(_newFolder.startsWith(".")) return -1;
01035 
01036           if(root) {
01037             folder = the_folderMgr->findOrCreate(*it, false);
01038             if (folder) {
01039               root = false;
01040               tmp_fname = "/" + *it;
01041             }
01042             else return -1;
01043           } else {
01044             subfolder = folder->createChildFolder();
01045             tmp_fname += "/" + *it;
01046             if(!the_folderMgr->getFolderByURL( tmp_fname )) {
01047               folder = the_folderMgr->createFolder(*it, false, folder->folderType(), subfolder);
01048             }
01049             if(!(folder = the_folderMgr->getFolderByURL( tmp_fname ))) return -1;
01050           }
01051         }
01052 
01053       mAddMsgCurrentFolder = the_folderMgr->getFolderByURL( tmp_fname );
01054       if(!folder) return -1;
01055 
01056       } else {
01057         mAddMsgCurrentFolder = the_folderMgr->findOrCreate(_foldername, false);
01058       }
01059     }
01060 
01061     if ( mAddMsgCurrentFolder ) {
01062       int index;
01063 
01064       if( !MsgStatusFlags.isEmpty() ) {
01065         KMMsgStatus status = strToStatus(MsgStatusFlags);
01066         if (status) msg->setStatus(status);
01067       }
01068 
01069       if ( mAddMsgCurrentFolder->addMsg( msg, &index ) == 0 ) {
01070         mAddMsgCurrentFolder->unGetMsg( index );
01071         retval = 1;
01072       } else {
01073         retval =- 2;
01074         delete msg;
01075         msg = 0;
01076       }
01077     } else {
01078       retval = -1;
01079     }
01080   } else {
01081     retval = -2;
01082   }
01083 
01084   return retval;
01085 }
01086 
01087 QStringList KMKernel::folderList() const
01088 {
01089   QStringList folders;
01090   const QString localPrefix = "/Local";
01091   folders << localPrefix;
01092   the_folderMgr->getFolderURLS( folders, localPrefix );
01093   the_imapFolderMgr->getFolderURLS( folders );
01094   the_dimapFolderMgr->getFolderURLS( folders );
01095   return folders;
01096 }
01097 
01098 DCOPRef KMKernel::getFolder( const QString& vpath )
01099 {
01100   const QString localPrefix = "/Local";
01101   if ( the_folderMgr->getFolderByURL( vpath ) )
01102     return DCOPRef( new FolderIface( vpath ) );
01103   else if ( vpath.startsWith( localPrefix ) &&
01104             the_folderMgr->getFolderByURL( vpath.mid( localPrefix.length() ) ) )
01105     return DCOPRef( new FolderIface( vpath.mid( localPrefix.length() ) ) );
01106   else if ( the_imapFolderMgr->getFolderByURL( vpath ) )
01107     return DCOPRef( new FolderIface( vpath ) );
01108   else if ( the_dimapFolderMgr->getFolderByURL( vpath ) )
01109     return DCOPRef( new FolderIface( vpath ) );
01110   return DCOPRef();
01111 }
01112 
01113 void KMKernel::raise()
01114 {
01115   DCOPRef kmail( "kmail", "kmail" );
01116   kmail.call( "newInstance" );
01117 }
01118 
01119 bool KMKernel::showMail( Q_UINT32 serialNumber, QString /* messageId */ )
01120 {
01121   KMMainWidget *mainWidget = 0;
01122   if (KMainWindow::memberList) {
01123     KMainWindow *win = 0;
01124     QObjectList *l;
01125 
01126     // First look for a KMainWindow.
01127     for (win = KMainWindow::memberList->first(); win;
01128          win = KMainWindow::memberList->next()) {
01129       // Then look for a KMMainWidget.
01130       l = win->queryList("KMMainWidget");
01131       if (l && l->first()) {
01132     mainWidget = dynamic_cast<KMMainWidget *>(l->first());
01133     if (win->isActiveWindow())
01134       break;
01135       }
01136     }
01137   }
01138 
01139   if (mainWidget) {
01140     int idx = -1;
01141     KMFolder *folder = 0;
01142     KMMsgDict::instance()->getLocation(serialNumber, &folder, &idx);
01143     if (!folder || (idx == -1))
01144       return false;
01145     KMFolderOpener openFolder(folder, "showmail");
01146     KMMsgBase *msgBase = folder->getMsgBase(idx);
01147     if (!msgBase)
01148       return false;
01149     bool unGet = !msgBase->isMessage();
01150     KMMessage *msg = folder->getMsg(idx);
01151 
01152     KMReaderMainWin *win = new KMReaderMainWin( false, false );
01153     KMMessage *newMessage = new KMMessage( *msg );
01154     newMessage->setParent( msg->parent() );
01155     newMessage->setMsgSerNum( msg->getMsgSerNum() );
01156     newMessage->setReadyToShow( true );
01157     win->showMsg( GlobalSettings::self()->overrideCharacterEncoding(), newMessage );
01158     win->show();
01159 
01160     if (unGet)
01161       folder->unGetMsg(idx);
01162     return true;
01163   }
01164 
01165   return false;
01166 }
01167 
01168 QString KMKernel::getFrom( Q_UINT32 serialNumber )
01169 {
01170   int idx = -1;
01171   KMFolder *folder = 0;
01172   KMMsgDict::instance()->getLocation(serialNumber, &folder, &idx);
01173   if (!folder || (idx == -1))
01174     return QString::null;
01175   KMFolderOpener openFolder(folder, "getFrom");
01176   KMMsgBase *msgBase = folder->getMsgBase(idx);
01177   if (!msgBase)
01178     return QString::null;
01179   bool unGet = !msgBase->isMessage();
01180   KMMessage *msg = folder->getMsg(idx);
01181   QString result = msg->from();
01182   if (unGet)
01183     folder->unGetMsg(idx);
01184   return result;
01185 }
01186 
01187 QString KMKernel::debugScheduler()
01188 {
01189   QString res = KMail::ActionScheduler::debug();
01190   return res;
01191 }
01192 
01193 QString KMKernel::debugSernum( Q_UINT32 serialNumber )
01194 {
01195   QString res;
01196   if (serialNumber != 0) {
01197     int idx = -1;
01198     KMFolder *folder = 0;
01199     KMMsgBase *msg = 0;
01200     KMMsgDict::instance()->getLocation( serialNumber, &folder, &idx );
01201     // It's possible that the message has been deleted or moved into a
01202     // different folder
01203     if (folder && (idx != -1)) {
01204       // everything is ok
01205       KMFolderOpener openFolder(folder, "debugser");
01206       msg = folder->getMsgBase( idx );
01207       if (msg) {
01208         res.append( QString( " subject %s,\n sender %s,\n date %s.\n" )
01209                              .arg( msg->subject() )
01210                              .arg( msg->fromStrip() )
01211                              .arg( msg->dateStr() ) );
01212       } else {
01213         res.append( QString( "Invalid serial number." ) );
01214       }
01215     } else {
01216       res.append( QString( "Invalid serial number." ) );
01217     }
01218   }
01219   return res;
01220 }
01221 
01222 
01223 void KMKernel::pauseBackgroundJobs()
01224 {
01225   mBackgroundTasksTimer->stop();
01226   mJobScheduler->pause();
01227 }
01228 
01229 void KMKernel::resumeBackgroundJobs()
01230 {
01231   mJobScheduler->resume();
01232   mBackgroundTasksTimer->start( 4 * 60 * 60 * 1000, true );
01233 }
01234 
01235 void KMKernel::stopNetworkJobs()
01236 {
01237   if ( GlobalSettings::self()->networkState() == GlobalSettings::EnumNetworkState::Offline )
01238     return;
01239 
01240   GlobalSettings::setNetworkState( GlobalSettings::EnumNetworkState::Offline );
01241   BroadcastStatus::instance()->setStatusMsg( i18n("KMail is set to be offline; all network jobs are suspended"));
01242   emit onlineStatusChanged( (GlobalSettings::EnumNetworkState::type)GlobalSettings::networkState() );
01243 }
01244 
01245 void KMKernel::resumeNetworkJobs()
01246 {
01247   if ( GlobalSettings::self()->networkState() == GlobalSettings::EnumNetworkState::Online )
01248     return;
01249 
01250   GlobalSettings::setNetworkState( GlobalSettings::EnumNetworkState::Online );
01251   BroadcastStatus::instance()->setStatusMsg( i18n("KMail is set to be online; all network jobs resumed"));
01252   emit onlineStatusChanged( (GlobalSettings::EnumNetworkState::type)GlobalSettings::networkState() );
01253 
01254   if ( kmkernel->msgSender()->sendImmediate() ) {
01255     kmkernel->msgSender()->sendQueued();
01256   }
01257 }
01258 
01259 bool KMKernel::isOffline()
01260 {
01261   if ( GlobalSettings::self()->networkState() == GlobalSettings::EnumNetworkState::Offline )
01262     return true;
01263   else
01264     return false;
01265 }
01266 
01267 bool KMKernel::askToGoOnline()
01268 {
01269   if ( kmkernel->isOffline() ) {
01270     int rc =
01271     KMessageBox::questionYesNo( KMKernel::self()->mainWin(),
01272                                 i18n("KMail is currently in offline mode. "
01273                                      "How do you want to proceed?"),
01274                                 i18n("Online/Offline"),
01275                                 i18n("Work Online"),
01276                                 i18n("Work Offline"));
01277 
01278     if( rc == KMessageBox::No ) {
01279       return false;
01280     } else {
01281       kmkernel->resumeNetworkJobs();
01282     }
01283   }
01284   return true;
01285 }
01286 
01287 /********************************************************************/
01288 /*                        Kernel methods                            */
01289 /********************************************************************/
01290 
01291 void KMKernel::quit()
01292 {
01293   // Called when all windows are closed. Will take care of compacting,
01294   // sending... should handle session management too!!
01295 }
01296   /* TODO later:
01297    Asuming that:
01298      - msgsender is nonblocking
01299        (our own, QSocketNotifier based. Pops up errors and sends signal
01300         senderFinished when done)
01301 
01302    o If we are getting mail, stop it (but dont lose something!)
01303          [Done already, see mailCheckAborted]
01304    o If we are sending mail, go on UNLESS this was called by SM,
01305        in which case stop ASAP that too (can we warn? should we continue
01306        on next start?)
01307    o If we are compacting, or expunging, go on UNLESS this was SM call.
01308        In that case stop compacting ASAP and continue on next start, before
01309        touching any folders. [Not needed anymore with CompactionJob]
01310 
01311    KMKernel::quit ()
01312    {
01313      SM call?
01314        if compacting, stop;
01315        if sending, stop;
01316        if receiving, stop;
01317        Windows will take care of themselves (composer should dump
01318         its messages, if any but not in deadMail)
01319        declare us ready for the End of the Session
01320 
01321      No, normal quit call
01322        All windows are off. Anything to do, should compact or sender sends?
01323          Yes, maybe put an icon in panel as a sign of life
01324          if sender sending, connect us to his finished slot, declare us ready
01325                             for quit and wait for senderFinished
01326          if not, Folder manager, go compact sent-mail and outbox
01327 }                (= call slotFinished())
01328 
01329 void KMKernel::slotSenderFinished()
01330 {
01331   good, Folder manager go compact sent-mail and outbox
01332   clean up stage1 (release folders and config, unregister from dcop)
01333     -- another kmail may start now ---
01334   kapp->quit();
01335 }
01336 */
01337 
01338 
01339 /********************************************************************/
01340 /*            Init, Exit, and handler  methods                      */
01341 /********************************************************************/
01342 void KMKernel::testDir(const char *_name)
01343 {
01344   QString foldersPath = QDir::homeDirPath() + QString( _name );
01345   QFileInfo info( foldersPath );
01346   if ( !info.exists() ) {
01347     if ( ::mkdir( QFile::encodeName( foldersPath ) , S_IRWXU ) == -1 ) {
01348       KMessageBox::sorry(0, i18n("KMail could not create folder '%1';\n"
01349                                  "please make sure that you can view and "
01350                                  "modify the content of the folder '%2'.")
01351                             .arg( foldersPath ).arg( QDir::homeDirPath() ) );
01352       ::exit(-1);
01353     }
01354   }
01355   if ( !info.isDir() || !info.isReadable() || !info.isWritable() ) {
01356     KMessageBox::sorry(0, i18n("The permissions of the folder '%1' are "
01357                                "incorrect;\n"
01358                                "please make sure that you can view and modify "
01359                                "the content of this folder.")
01360                           .arg( foldersPath ) );
01361     ::exit(-1);
01362   }
01363 }
01364 
01365 
01366 //-----------------------------------------------------------------------------
01367 // Open a composer for each message found in the dead.letter folder
01368 void KMKernel::recoverDeadLetters()
01369 {
01370   const QString pathName = localDataPath();
01371   QDir dir( pathName );
01372   if ( !dir.exists( "autosave" ) )
01373     return;
01374 
01375   KMFolder folder( 0, pathName + "autosave", KMFolderTypeMaildir, false /* no index */ );
01376   KMFolderOpener openFolder( &folder, "recover" );
01377   if ( !folder.isOpened() ) {
01378     perror( "cannot open autosave folder" );
01379     return;
01380   }
01381 
01382   const int num = folder.count();
01383   for ( int i = 0; i < num; i++ ) {
01384     KMMessage *msg = folder.take( 0 );
01385     if ( msg ) {
01386       KMail::Composer * win = KMail::makeComposer();
01387       win->setMsg( msg, false, false, true );
01388       win->setAutoSaveFilename( msg->fileName() );
01389       win->show();
01390     }
01391   }
01392 }
01393 
01394 //-----------------------------------------------------------------------------
01395 void KMKernel::initFolders(KConfig* cfg)
01396 {
01397   QString name;
01398 
01399   name = cfg->readEntry("inboxFolder");
01400 
01401   // Currently the folder manager cannot manage folders which are not
01402   // in the base folder directory.
01403   //if (name.isEmpty()) name = getenv("MAIL");
01404 
01405   if (name.isEmpty()) name = I18N_NOOP("inbox");
01406 
01407   the_inboxFolder  = (KMFolder*)the_folderMgr->findOrCreate(name);
01408 
01409   if (the_inboxFolder->canAccess() != 0) {
01410     emergencyExit( i18n("You do not have read/write permission to your inbox folder.") );
01411   }
01412 
01413   the_inboxFolder->setSystemFolder(true);
01414   if ( the_inboxFolder->userWhoField().isEmpty() )
01415     the_inboxFolder->setUserWhoField( QString::null );
01416   // inboxFolder->open();
01417 
01418   the_outboxFolder = the_folderMgr->findOrCreate(cfg->readEntry("outboxFolder", I18N_NOOP("outbox")));
01419   if (the_outboxFolder->canAccess() != 0) {
01420     emergencyExit( i18n("You do not have read/write permission to your outbox folder.") );
01421   }
01422   the_outboxFolder->setNoChildren(true);
01423 
01424   the_outboxFolder->setSystemFolder(true);
01425   if ( the_outboxFolder->userWhoField().isEmpty() )
01426     the_outboxFolder->setUserWhoField( QString::null );
01427   /* Nuke the oubox's index file, to make sure that no ghost messages are in
01428    * it from a previous crash. Ghost messages happen in the outbox because it
01429    * the only folder where messages enter and leave within 5 seconds, which is
01430    * the leniency period for index invalidation. Since the number of mails in
01431    * this folder is expected to be very small, we can live with regenerating
01432    * the index on each start to be on the save side. */
01433   //if ( the_outboxFolder->folderType() == KMFolderTypeMaildir )
01434   //  unlink( QFile::encodeName( the_outboxFolder->indexLocation() ) );
01435   the_outboxFolder->open("kmkernel");
01436 
01437   the_sentFolder = the_folderMgr->findOrCreate(cfg->readEntry("sentFolder", I18N_NOOP("sent-mail")));
01438   if (the_sentFolder->canAccess() != 0) {
01439     emergencyExit( i18n("You do not have read/write permission to your sent-mail folder.") );
01440   }
01441   the_sentFolder->setSystemFolder(true);
01442   if ( the_sentFolder->userWhoField().isEmpty() )
01443     the_sentFolder->setUserWhoField( QString::null );
01444   // the_sentFolder->open();
01445 
01446   the_trashFolder  = the_folderMgr->findOrCreate(cfg->readEntry("trashFolder", I18N_NOOP("trash")));
01447   if (the_trashFolder->canAccess() != 0) {
01448     emergencyExit( i18n("You do not have read/write permission to your trash folder.") );
01449   }
01450   the_trashFolder->setSystemFolder( true );
01451   if ( the_trashFolder->userWhoField().isEmpty() )
01452     the_trashFolder->setUserWhoField( QString::null );
01453   // the_trashFolder->open();
01454 
01455   the_draftsFolder = the_folderMgr->findOrCreate(cfg->readEntry("draftsFolder", I18N_NOOP("drafts")));
01456   if (the_draftsFolder->canAccess() != 0) {
01457     emergencyExit( i18n("You do not have read/write permission to your drafts folder.") );
01458   }
01459   the_draftsFolder->setSystemFolder( true );
01460   if ( the_draftsFolder->userWhoField().isEmpty() )
01461     the_draftsFolder->setUserWhoField( QString::null );
01462   the_draftsFolder->open("kmkernel");
01463 
01464   the_templatesFolder =
01465     the_folderMgr->findOrCreate( cfg->readEntry( "templatesFolder",
01466                                                  I18N_NOOP("templates") ) );
01467   if ( the_templatesFolder->canAccess() != 0 ) {
01468     emergencyExit( i18n("You do not have read/write permission to your templates folder.") );
01469   }
01470   the_templatesFolder->setSystemFolder( true );
01471   if ( the_templatesFolder->userWhoField().isEmpty() )
01472     the_templatesFolder->setUserWhoField( QString::null );
01473   the_templatesFolder->open("kmkernel");
01474 }
01475 
01476 
01477 void KMKernel::init()
01478 {
01479   the_shuttingDown = false;
01480   the_server_is_ready = false;
01481 
01482   KConfig* cfg = KMKernel::config();
01483 
01484   QDir dir;
01485 
01486   KConfigGroupSaver saver(cfg, "General");
01487   the_firstStart = cfg->readBoolEntry("first-start", true);
01488   cfg->writeEntry("first-start", false);
01489   the_previousVersion = cfg->readEntry("previous-version");
01490   cfg->writeEntry("previous-version", KMAIL_VERSION);
01491   QString foldersPath = cfg->readPathEntry( "folders" );
01492   kdDebug(5006) << k_funcinfo << "foldersPath (from config): '" << foldersPath << "'" << endl;
01493 
01494   if ( foldersPath.isEmpty() ) {
01495     foldersPath = localDataPath() + "mail";
01496     if ( transferMail( foldersPath ) ) {
01497       cfg->writePathEntry( "folders", foldersPath );
01498     }
01499     kdDebug(5006) << k_funcinfo << "foldersPath (after transferMail): '" << foldersPath << "'" << endl;
01500   }
01501 
01502   // moved up here because KMMessage::stripOffPrefixes is used below
01503   KMMessage::readConfig();
01504 
01505   the_undoStack     = new UndoStack(20);
01506   the_folderMgr     = new KMFolderMgr(foldersPath);
01507   the_imapFolderMgr = new KMFolderMgr( KMFolderImap::cacheLocation(), KMImapDir);
01508   the_dimapFolderMgr = new KMFolderMgr( KMFolderCachedImap::cacheLocation(), KMDImapDir);
01509 
01510   the_searchFolderMgr = new KMFolderMgr(locateLocal("data","kmail/search"), KMSearchDir);
01511   KMFolder *lsf = the_searchFolderMgr->find( i18n("Last Search") );
01512   if (lsf)
01513     the_searchFolderMgr->remove( lsf );
01514 
01515   the_acctMgr       = new AccountManager();
01516   the_filterMgr     = new KMFilterMgr();
01517   the_popFilterMgr     = new KMFilterMgr(true);
01518   the_filterActionDict = new KMFilterActionDict;
01519 
01520   initFolders(cfg);
01521   the_acctMgr->readConfig();
01522   the_filterMgr->readConfig();
01523   the_popFilterMgr->readConfig();
01524   cleanupImapFolders();
01525 
01526   the_msgSender = new KMSender;
01527   the_server_is_ready = true;
01528   imProxy()->initialize();
01529   { // area for config group "Composer"
01530     KConfigGroupSaver saver(cfg, "Composer");
01531     if (cfg->readListEntry("pref-charsets").isEmpty())
01532     {
01533       cfg->writeEntry("pref-charsets", "us-ascii,iso-8859-1,locale,utf-8");
01534     }
01535   }
01536   readConfig();
01537   mICalIface->readConfig();
01538   // filterMgr->dump();
01539 #ifdef HAVE_INDEXLIB
01540   the_msgIndex = new KMMsgIndex(this); //create the indexer
01541 #else
01542   the_msgIndex = 0;
01543 #endif
01544 
01545 //#if 0
01546   the_weaver =  new KPIM::ThreadWeaver::Weaver( this );
01547   the_weaverLogger = new KPIM::ThreadWeaver::WeaverThreadLogger(this);
01548   the_weaverLogger->attach (the_weaver);
01549 //#endif
01550 
01551   connect( the_folderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01552            this, SIGNAL( folderRemoved(KMFolder*) ) );
01553   connect( the_dimapFolderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01554            this, SIGNAL( folderRemoved(KMFolder*) ) );
01555   connect( the_imapFolderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01556            this, SIGNAL( folderRemoved(KMFolder*) ) );
01557   connect( the_searchFolderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01558            this, SIGNAL( folderRemoved(KMFolder*) ) );
01559 
01560   mBackgroundTasksTimer = new QTimer( this, "mBackgroundTasksTimer" );
01561   connect( mBackgroundTasksTimer, SIGNAL( timeout() ), this, SLOT( slotRunBackgroundTasks() ) );
01562 #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h
01563   mBackgroundTasksTimer->start( 10000, true ); // 10s, singleshot
01564 #else
01565   mBackgroundTasksTimer->start( 5 * 60000, true ); // 5 minutes, singleshot
01566 #endif
01567 }
01568 
01569 void KMKernel::readConfig()
01570 {
01571   //Needed here, since this function is also called when the configuration
01572   //changes, and the static variables should be updated then - IOF
01573   KMMessage::readConfig();
01574 }
01575 
01576 void KMKernel::cleanupImapFolders()
01577 {
01578   KMAccount *acct = 0;
01579   KMFolderNode *node = the_imapFolderMgr->dir().first();
01580   while (node)
01581   {
01582     if (node->isDir() || ((acct = the_acctMgr->find(node->id()))
01583               && ( acct->type() == "imap" )) )
01584     {
01585       node = the_imapFolderMgr->dir().next();
01586     } else {
01587       KMFolder* folder = static_cast<KMFolder*>(node);
01588       // delete only local
01589       static_cast<KMFolderImap*>( folder->storage() )->setAlreadyRemoved( true );
01590       the_imapFolderMgr->remove(folder);
01591       node = the_imapFolderMgr->dir().first();
01592     }
01593   }
01594 
01595   node = the_dimapFolderMgr->dir().first();
01596   while (node)
01597   {
01598     if (node->isDir() || ((acct = the_acctMgr->find(node->id()))
01599               && ( acct->type() == "cachedimap" )) )
01600     {
01601       node = the_dimapFolderMgr->dir().next();
01602     } else {
01603       the_dimapFolderMgr->remove(static_cast<KMFolder*>(node));
01604       node = the_dimapFolderMgr->dir().first();
01605     }
01606   }
01607 
01608   the_imapFolderMgr->quiet(true);
01609   for (acct = the_acctMgr->first(); acct; acct = the_acctMgr->next())
01610   {
01611     KMFolderImap *fld;
01612     KMAcctImap *imapAcct;
01613 
01614     if (acct->type() != "imap") continue;
01615     fld = static_cast<KMFolderImap*>(the_imapFolderMgr
01616       ->findOrCreate(QString::number(acct->id()), false, acct->id())->storage());
01617     fld->setNoContent(true);
01618     fld->folder()->setLabel(acct->name());
01619     imapAcct = static_cast<KMAcctImap*>(acct);
01620     fld->setAccount(imapAcct);
01621     imapAcct->setImapFolder(fld);
01622     fld->close( "kernel", true );
01623   }
01624   the_imapFolderMgr->quiet(false);
01625 
01626   the_dimapFolderMgr->quiet( true );
01627   for (acct = the_acctMgr->first(); acct; acct = the_acctMgr->next())
01628   {
01629     KMFolderCachedImap *cfld = 0;
01630     KMAcctCachedImap *cachedImapAcct;
01631 
01632     if (acct->type() != "cachedimap" ) continue;
01633 
01634     KMFolder* fld = the_dimapFolderMgr->find(QString::number(acct->id()));
01635     if( fld )
01636       cfld = static_cast<KMFolderCachedImap*>( fld->storage() );
01637     if (cfld == 0) {
01638       // Folder doesn't exist yet
01639       cfld = static_cast<KMFolderCachedImap*>(the_dimapFolderMgr->createFolder(QString::number(acct->id()),
01640             false, KMFolderTypeCachedImap)->storage());
01641       if (!cfld) {
01642         KMessageBox::error(0,(i18n("Cannot create file `%1' in %2.\nKMail cannot start without it.").arg(acct->name()).arg(the_dimapFolderMgr->basePath())));
01643         exit(-1);
01644       }
01645       cfld->folder()->setId( acct->id() );
01646     }
01647 
01648     cfld->setNoContent(true);
01649     cfld->folder()->setLabel(acct->name());
01650     cachedImapAcct = static_cast<KMAcctCachedImap*>(acct);
01651     cfld->setAccount(cachedImapAcct);
01652     cachedImapAcct->setImapFolder(cfld);
01653     cfld->close("kmkernel");
01654   }
01655   the_dimapFolderMgr->quiet( false );
01656 }
01657 
01658 bool KMKernel::doSessionManagement()
01659 {
01660 
01661   // Do session management
01662   if (kapp->isRestored()){
01663     int n = 1;
01664     while (KMMainWin::canBeRestored(n)){
01665       //only restore main windows! (Matthias);
01666       if (KMMainWin::classNameOfToplevel(n) == "KMMainWin")
01667         (new KMMainWin)->restore(n);
01668       n++;
01669     }
01670     return true; // we were restored by SM
01671   }
01672   return false;  // no, we were not restored
01673 }
01674 
01675 void KMKernel::closeAllKMailWindows()
01676 {
01677   if (!KMainWindow::memberList) return;
01678   QPtrListIterator<KMainWindow> it(*KMainWindow::memberList);
01679   KMainWindow *window = 0;
01680   while ((window = it.current()) != 0) {
01681     ++it;
01682     if (window->isA("KMMainWindow") ||
01683     window->inherits("KMail::SecondaryWindow"))
01684       window->close( true ); // close and delete the window
01685   }
01686 }
01687 
01688 void KMKernel::cleanup(void)
01689 {
01690   dumpDeadLetters();
01691   the_shuttingDown = true;
01692   closeAllKMailWindows();
01693 
01694   delete the_acctMgr;
01695   the_acctMgr = 0;
01696   delete the_filterMgr;
01697   the_filterMgr = 0;
01698   delete the_msgSender;
01699   the_msgSender = 0;
01700   delete the_filterActionDict;
01701   the_filterActionDict = 0;
01702   delete the_undoStack;
01703   the_undoStack = 0;
01704   delete the_popFilterMgr;
01705   the_popFilterMgr = 0;
01706 
01707 #if 0
01708   delete the_weaver;
01709   the_weaver = 0;
01710 #endif
01711 
01712   KConfig* config =  KMKernel::config();
01713   KConfigGroupSaver saver(config, "General");
01714 
01715   if (the_trashFolder) {
01716 
01717     the_trashFolder->close("kmkernel", true);
01718 
01719     if (config->readBoolEntry("empty-trash-on-exit", true))
01720     {
01721       if ( the_trashFolder->count( true ) > 0 )
01722         the_trashFolder->expunge();
01723     }
01724   }
01725 
01726   mICalIface->cleanup();
01727 
01728   QValueList<QGuardedPtr<KMFolder> > folders;
01729   QStringList strList;
01730   KMFolder *folder;
01731   the_folderMgr->createFolderList(&strList, &folders);
01732   for (int i = 0; folders.at(i) != folders.end(); i++)
01733   {
01734     folder = *folders.at(i);
01735     if (!folder || folder->isDir()) continue;
01736     folder->close("kmkernel", true);
01737   }
01738   strList.clear();
01739   folders.clear();
01740   the_searchFolderMgr->createFolderList(&strList, &folders);
01741   for (int i = 0; folders.at(i) != folders.end(); i++)
01742   {
01743     folder = *folders.at(i);
01744     if (!folder || folder->isDir()) continue;
01745     folder->close("kmkernel", true);
01746   }
01747 
01748   delete the_msgIndex;
01749   the_msgIndex = 0;
01750   delete the_folderMgr;
01751   the_folderMgr = 0;
01752   delete the_imapFolderMgr;
01753   the_imapFolderMgr = 0;
01754   delete the_dimapFolderMgr;
01755   the_dimapFolderMgr = 0;
01756   delete the_searchFolderMgr;
01757   the_searchFolderMgr = 0;
01758   delete mConfigureDialog;
01759   mConfigureDialog = 0;
01760   // do not delete, because mWin may point to an existing window
01761   // delete mWin;
01762   mWin = 0;
01763 
01764   if ( RecentAddresses::exists() )
01765     RecentAddresses::self( config )->save( config );
01766   config->sync();
01767 }
01768 
01769 bool KMKernel::transferMail( QString & destinationDir )
01770 {
01771   QString dir;
01772 
01773   // check whether the user has a ~/KMail folder
01774   QFileInfo fi( QDir::home(), "KMail" );
01775   if ( fi.exists() && fi.isDir() ) {
01776     dir = QDir::homeDirPath() + "/KMail";
01777     // the following two lines can be removed once moving mail is reactivated
01778     destinationDir = dir;
01779     return true;
01780   }
01781 
01782   if ( dir.isEmpty() ) {
01783     // check whether the user has a ~/Mail folder
01784     fi.setFile( QDir::home(), "Mail" );
01785     if ( fi.exists() && fi.isDir() &&
01786          QFile::exists( QDir::homeDirPath() + "/Mail/.inbox.index" ) ) {
01787       // there's a ~/Mail folder which seems to be used by KMail (because of the
01788       // index file)
01789       dir = QDir::homeDirPath() + "/Mail";
01790       // the following two lines can be removed once moving mail is reactivated
01791       destinationDir = dir;
01792       return true;
01793     }
01794   }
01795 
01796   if ( dir.isEmpty() ) {
01797     return true; // there's no old mail folder
01798   }
01799 
01800 #if 0
01801   // disabled for now since moving fails in certain cases (e.g. if symbolic links are involved)
01802   const QString kmailName = kapp->aboutData()->programName();
01803   QString msg;
01804   if ( KIO::NetAccess::exists( destinationDir, true, 0 ) ) {
01805     // if destinationDir exists, we need to warn about possible
01806     // overwriting of files. otherwise, we don't have to
01807     msg = i18n( "%1-%3 is the application name, %4-%7 are folder path",
01808                 "<qt>The <i>%4</i> folder exists. "
01809                 "%1 now uses the <i>%5</i> folder for "
01810                 "its messages.<p>"
01811                 "%2 can move the contents of <i>%6<i> into this folder for "
01812                 "you, though this may replace any existing files with "
01813                 "the same name in <i>%7</i>.<p>"
01814                 "<strong>Would you like %3 to move the mail "
01815                 "files now?</strong></qt>" )
01816           .arg( kmailName, kmailName, kmailName )
01817           .arg( dir, destinationDir, dir, destinationDir );
01818   } else {
01819     msg = i18n( "%1-%3 is the application name, %4-%6 are folder path",
01820                 "<qt>The <i>%4</i> folder exists. "
01821                 "%1 now uses the <i>%5</i> folder for "
01822                 "its messages. %2 can move the contents of <i>%6</i> into "
01823                 "this folder for you.<p>"
01824                 "<strong>Would you like %3 to move the mail "
01825                 "files now?</strong></qt>" )
01826           .arg( kmailName, kmailName, kmailName )
01827           .arg( dir, destinationDir, dir );
01828   }
01829   QString title = i18n( "Migrate Mail Files?" );
01830   QString buttonText = i18n( "Move" );
01831 
01832   if ( KMessageBox::questionYesNo( 0, msg, title, buttonText, i18n("Do Not Move") ) ==
01833        KMessageBox::No ) {
01834     destinationDir = dir;
01835     return true;
01836   }
01837 
01838   if ( !KIO::NetAccess::move( dir, destinationDir ) ) {
01839     kdDebug(5006) << k_funcinfo << "Moving " << dir << " to " << destinationDir << " failed: " << KIO::NetAccess::lastErrorString() << endl;
01840     kdDebug(5006) << k_funcinfo << "Deleting " << destinationDir << endl;
01841     KIO::NetAccess::del( destinationDir, 0 );
01842     destinationDir = dir;
01843     return false;
01844   }
01845 #endif
01846 
01847   return true;
01848 }
01849 
01850 
01851 void KMKernel::ungrabPtrKb(void)
01852 {
01853   if(!KMainWindow::memberList) return;
01854   QWidget* widg = KMainWindow::memberList->first();
01855   Display* dpy;
01856 
01857   if (!widg) return;
01858   dpy = widg->x11Display();
01859   XUngrabKeyboard(dpy, CurrentTime);
01860   XUngrabPointer(dpy, CurrentTime);
01861 }
01862 
01863 
01864 // Message handler
01865 void KMKernel::kmailMsgHandler(QtMsgType aType, const char* aMsg)
01866 {
01867   static int recurse=-1;
01868 
01869   recurse++;
01870 
01871   switch (aType)
01872   {
01873   case QtDebugMsg:
01874   case QtWarningMsg:
01875     kdDebug(5006) << aMsg << endl;
01876     break;
01877 
01878   case QtFatalMsg: // Hm, what about using kdFatal() here?
01879     ungrabPtrKb();
01880     kdDebug(5006) << kapp->caption() << " fatal error "
01881           << aMsg << endl;
01882     KMessageBox::error(0, aMsg);
01883     abort();
01884   }
01885 
01886   recurse--;
01887 }
01888 
01889 
01890 void KMKernel::dumpDeadLetters()
01891 {
01892   if ( shuttingDown() )
01893     return; //All documents should be saved before shutting down is set!
01894 
01895   // make all composer windows autosave their contents
01896   if ( !KMainWindow::memberList )
01897     return;
01898 
01899   for ( QPtrListIterator<KMainWindow> it(*KMainWindow::memberList) ; it.current() != 0; ++it ) {
01900     if ( KMail::Composer * win = ::qt_cast<KMail::Composer*>( it.current() ) ) {
01901       win->autoSaveMessage();
01902       // saving the message has to be finished right here, we are called from a dtor,
01903       // therefore we have no chance to finish this later
01904       // yes, this is ugly and potentially dangerous, but the alternative is losing
01905       // currently composed messages...
01906       while ( win->isComposing() )
01907         qApp->processEvents();
01908     }
01909   }
01910 }
01911 
01912 
01913 
01914 void KMKernel::action(bool mailto, bool check, const QString &to,
01915                       const QString &cc, const QString &bcc,
01916                       const QString &subj, const QString &body,
01917                       const KURL &messageFile,
01918                       const KURL::List &attachURLs,
01919                       const QCStringList &customHeaders)
01920 {
01921   if ( mailto )
01922     openComposer( to, cc, bcc, subj, body, 0, messageFile, attachURLs, customHeaders );
01923   else
01924     openReader( check );
01925 
01926   if ( check )
01927     checkMail();
01928   //Anything else?
01929 }
01930 
01931 void KMKernel::byteArrayToRemoteFile(const QByteArray &aData, const KURL &aURL,
01932   bool overwrite)
01933 {
01934   // ## when KDE 3.3 is out: use KIO::storedPut to remove slotDataReq altogether
01935   KIO::Job *job = KIO::put(aURL, -1, overwrite, false);
01936   putData pd; pd.url = aURL; pd.data = aData; pd.offset = 0;
01937   mPutJobs.insert(job, pd);
01938   connect(job, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
01939     SLOT(slotDataReq(KIO::Job*,QByteArray&)));
01940   connect(job, SIGNAL(result(KIO::Job*)),
01941     SLOT(slotResult(KIO::Job*)));
01942 }
01943 
01944 void KMKernel::slotDataReq(KIO::Job *job, QByteArray &data)
01945 {
01946   // send the data in 64 KB chunks
01947   const int MAX_CHUNK_SIZE = 64*1024;
01948   QMap<KIO::Job*, putData>::Iterator it = mPutJobs.find(job);
01949   assert(it != mPutJobs.end());
01950   int remainingBytes = (*it).data.size() - (*it).offset;
01951   if( remainingBytes > MAX_CHUNK_SIZE )
01952   {
01953     // send MAX_CHUNK_SIZE bytes to the receiver (deep copy)
01954     data.duplicate( (*it).data.data() + (*it).offset, MAX_CHUNK_SIZE );
01955     (*it).offset += MAX_CHUNK_SIZE;
01956     //kdDebug( 5006 ) << "Sending " << MAX_CHUNK_SIZE << " bytes ("
01957     //                << remainingBytes - MAX_CHUNK_SIZE << " bytes remain)\n";
01958   }
01959   else
01960   {
01961     // send the remaining bytes to the receiver (deep copy)
01962     data.duplicate( (*it).data.data() + (*it).offset, remainingBytes );
01963     (*it).data = QByteArray();
01964     (*it).offset = 0;
01965     //kdDebug( 5006 ) << "Sending " << remainingBytes << " bytes\n";
01966   }
01967 }
01968 
01969 void KMKernel::slotResult(KIO::Job *job)
01970 {
01971   QMap<KIO::Job*, putData>::Iterator it = mPutJobs.find(job);
01972   assert(it != mPutJobs.end());
01973   if (job->error())
01974   {
01975     if (job->error() == KIO::ERR_FILE_ALREADY_EXIST)
01976     {
01977       if (KMessageBox::warningContinueCancel(0,
01978         i18n("File %1 exists.\nDo you want to replace it?")
01979         .arg((*it).url.prettyURL()), i18n("Save to File"), i18n("&Replace"))
01980         == KMessageBox::Continue)
01981         byteArrayToRemoteFile((*it).data, (*it).url, true);
01982     }
01983     else job->showErrorDialog();
01984   }
01985   mPutJobs.remove(it);
01986 }
01987 
01988 void KMKernel::slotRequestConfigSync() {
01989   // ### FIXME: delay as promised in the kdoc of this function ;-)
01990   KMKernel::config()->sync();
01991 }
01992 
01993 void KMKernel::slotShowConfigurationDialog()
01994 {
01995   if( !mConfigureDialog ) {
01996     mConfigureDialog = new ConfigureDialog( 0, "configure", false );
01997     connect( mConfigureDialog, SIGNAL( configChanged() ),
01998              this, SLOT( slotConfigChanged() ) );
01999   }
02000 
02001   if( KMKernel::getKMMainWidget() == 0 )
02002   {
02003     // ensure that there is a main widget available
02004     // as parts of the configure dialog (identity) rely on this
02005     // and this slot can be called when there is only a KMComposeWin showing
02006     KMMainWin * win = new KMMainWin;
02007     win->show();
02008   }
02009   if( mConfigureDialog->isHidden() )
02010   {
02011     getKMMainWidget()->headers()->writeConfig();
02012     mConfigureDialog->show();
02013   }
02014   else
02015     mConfigureDialog->raise();
02016 }
02017 
02018 void KMKernel::slotConfigChanged()
02019 {
02020   readConfig();
02021   emit configChanged();
02022 }
02023 
02024 //-------------------------------------------------------------------------------
02025 //static
02026 QString KMKernel::localDataPath()
02027 {
02028   return locateLocal( "data", "kmail/" );
02029 }
02030 
02031 //-------------------------------------------------------------------------------
02032 
02033 bool KMKernel::haveSystemTrayApplet()
02034 {
02035   return !systemTrayApplets.isEmpty();
02036 }
02037 
02038 bool KMKernel::registerSystemTrayApplet( const KSystemTray* applet )
02039 {
02040   if ( systemTrayApplets.findIndex( applet ) == -1 ) {
02041     systemTrayApplets.append( applet );
02042     return true;
02043   }
02044   else
02045     return false;
02046 }
02047 
02048 bool KMKernel::unregisterSystemTrayApplet( const KSystemTray* applet )
02049 {
02050   QValueList<const KSystemTray*>::iterator it =
02051     systemTrayApplets.find( applet );
02052   if ( it != systemTrayApplets.end() ) {
02053     systemTrayApplets.remove( it );
02054     return true;
02055   }
02056   else
02057     return false;
02058 }
02059 
02060 void KMKernel::emergencyExit( const QString& reason )
02061 {
02062   QString mesg;
02063   if ( reason.length() == 0 ) {
02064     mesg = i18n("KMail encountered a fatal error and will terminate now");
02065   } else {
02066     mesg = i18n("KMail encountered a fatal error and will "
02067                       "terminate now.\nThe error was:\n%1").arg( reason );
02068   }
02069 
02070   kdWarning() << mesg << endl;
02071   KNotifyClient::userEvent( 0, "<qt>"+mesg+"</qt>", KNotifyClient::Messagebox, KNotifyClient::Error );
02072 
02073   ::exit(1);
02074 }
02075 
02079 bool KMKernel::folderIsDraftOrOutbox(const KMFolder * folder)
02080 {
02081   assert( folder );
02082   if ( folder == the_outboxFolder )
02083     return true;
02084   return folderIsDrafts( folder );
02085 }
02086 
02087 bool KMKernel::folderIsDrafts(const KMFolder * folder)
02088 {
02089   assert( folder );
02090   if ( folder == the_draftsFolder )
02091     return true;
02092 
02093   QString idString = folder->idString();
02094   if ( idString.isEmpty() )
02095     return false;
02096 
02097   // search the identities if the folder matches the drafts-folder
02098   const KPIM::IdentityManager *im = identityManager();
02099   for ( KPIM::IdentityManager::ConstIterator it=im->begin(); it != im->end(); ++it )
02100     if ( (*it).drafts() == idString )
02101       return true;
02102   return false;
02103 }
02104 
02105 bool KMKernel::folderIsTemplates( const KMFolder *folder )
02106 {
02107   assert( folder );
02108   if ( folder == the_templatesFolder )
02109     return true;
02110 
02111   QString idString = folder->idString();
02112   if ( idString.isEmpty() )
02113     return false;
02114 
02115   // search the identities if the folder matches the templates-folder
02116   const KPIM::IdentityManager *im = identityManager();
02117   for ( KPIM::IdentityManager::ConstIterator it=im->begin(); it != im->end(); ++it )
02118     if ( (*it).templates() == idString )
02119       return true;
02120   return false;
02121 }
02122 
02123 bool KMKernel::folderIsTrash(KMFolder * folder)
02124 {
02125   assert(folder);
02126   if (folder == the_trashFolder) return true;
02127   QStringList actList = acctMgr()->getAccounts();
02128   QStringList::Iterator it( actList.begin() );
02129   for( ; it != actList.end() ; ++it ) {
02130     KMAccount* act = acctMgr()->findByName( *it );
02131     if ( act && ( act->trash() == folder->idString() ) )
02132       return true;
02133   }
02134   return false;
02135 }
02136 
02137 bool KMKernel::folderIsSentMailFolder( const KMFolder * folder )
02138 {
02139   assert( folder );
02140   if ( folder == the_sentFolder )
02141     return true;
02142 
02143   QString idString = folder->idString();
02144   if ( idString.isEmpty() ) return false;
02145 
02146   // search the identities if the folder matches the sent-folder
02147   const KPIM::IdentityManager * im = identityManager();
02148   for( KPIM::IdentityManager::ConstIterator it = im->begin(); it != im->end(); ++it )
02149     if ( (*it).fcc() == idString ) return true;
02150   return false;
02151 }
02152 
02153 KPIM::IdentityManager * KMKernel::identityManager() {
02154   if ( !mIdentityManager ) {
02155     kdDebug(5006) << "instantating KPIM::IdentityManager" << endl;
02156     mIdentityManager = new KPIM::IdentityManager( false, this, "mIdentityManager" );
02157   }
02158   return mIdentityManager;
02159 }
02160 
02161 KMMsgIndex *KMKernel::msgIndex()
02162 {
02163     return the_msgIndex;
02164 }
02165 
02166 KMainWindow* KMKernel::mainWin()
02167 {
02168   if (KMainWindow::memberList) {
02169     KMainWindow *kmWin = 0;
02170 
02171     // First look for a KMMainWin.
02172     for (kmWin = KMainWindow::memberList->first(); kmWin;
02173          kmWin = KMainWindow::memberList->next())
02174       if (kmWin->isA("KMMainWin"))
02175         return kmWin;
02176 
02177     // There is no KMMainWin. Use any other KMainWindow instead (e.g. in
02178     // case we are running inside Kontact) because we anyway only need
02179     // it for modal message boxes and for KNotify events.
02180     kmWin = KMainWindow::memberList->first();
02181     if ( kmWin )
02182       return kmWin;
02183   }
02184 
02185   // There's not a single KMainWindow. Create a KMMainWin.
02186   // This could happen if we want to pop up an error message
02187   // while we are still doing the startup wizard and no other
02188   // KMainWindow is running.
02189   mWin = new KMMainWin;
02190   return mWin;
02191 }
02192 
02193 
02197 void KMKernel::slotEmptyTrash()
02198 {
02199   QString title = i18n("Empty Trash");
02200   QString text = i18n("Are you sure you want to empty the trash folders of all accounts?");
02201   if (KMessageBox::warningContinueCancel(0, text, title,
02202                                          KStdGuiItem::cont(), "confirm_empty_trash")
02203       != KMessageBox::Continue)
02204   {
02205     return;
02206   }
02207 
02208   for (KMAccount* acct = acctMgr()->first(); acct; acct = acctMgr()->next())
02209   {
02210     KMFolder* trash = findFolderById(acct->trash());
02211     if (trash)
02212     {
02213       trash->expunge();
02214     }
02215   }
02216 }
02217 
02218 KConfig* KMKernel::config()
02219 {
02220   assert(mySelf);
02221   if (!mySelf->mConfig)
02222   {
02223     mySelf->mConfig = KSharedConfig::openConfig( "kmailrc" );
02224     // Check that all updates have been run on the config file:
02225     KMail::checkConfigUpdates();
02226   }
02227   return mySelf->mConfig;
02228 }
02229 
02230 KMailICalIfaceImpl& KMKernel::iCalIface()
02231 {
02232   assert( mICalIface );
02233   return *mICalIface;
02234 }
02235 
02236 void KMKernel::selectFolder( QString folderPath )
02237 {
02238   kdDebug(5006)<<"Selecting a folder "<<folderPath<<endl;
02239   const QString localPrefix = "/Local";
02240   KMFolder *folder = kmkernel->folderMgr()->getFolderByURL( folderPath );
02241   if ( !folder && folderPath.startsWith( localPrefix ) )
02242     folder = the_folderMgr->getFolderByURL( folderPath.mid( localPrefix.length() ) );
02243   if ( !folder )
02244     folder = kmkernel->imapFolderMgr()->getFolderByURL( folderPath );
02245   if ( !folder )
02246     folder = kmkernel->dimapFolderMgr()->getFolderByURL( folderPath );
02247   Q_ASSERT( folder );
02248 
02249   KMMainWidget *widget = getKMMainWidget();
02250   Q_ASSERT( widget );
02251   if ( !widget )
02252     return;
02253 
02254   KMFolderTree *tree = widget->folderTree();
02255   tree->doFolderSelected( tree->indexOfFolder( folder ) );
02256   tree->ensureItemVisible( tree->indexOfFolder( folder ) );
02257 }
02258 
02259 KMMainWidget *KMKernel::getKMMainWidget()
02260 {
02261   //This could definitely use a speadup
02262   QWidgetList *l = kapp->topLevelWidgets();
02263   QWidgetListIt it( *l );
02264   QWidget *wid;
02265 
02266   while ( ( wid = it.current() ) != 0 ) {
02267     ++it;
02268     QObjectList *l2 = wid->topLevelWidget()->queryList( "KMMainWidget" );
02269     if (l2 && l2->first()) {
02270       KMMainWidget* kmmw = dynamic_cast<KMMainWidget *>( l2->first() );
02271       Q_ASSERT( kmmw );
02272       delete l2;
02273       delete l;
02274       return kmmw;
02275     }
02276     delete l2;
02277   }
02278   delete l;
02279   return 0;
02280 }
02281 
02282 void KMKernel::slotRunBackgroundTasks() // called regularly by timer
02283 {
02284   // Hidden KConfig keys. Not meant to be used, but a nice fallback in case
02285   // a stable kmail release goes out with a nasty bug in CompactionJob...
02286   KConfigGroup generalGroup( config(), "General" );
02287 
02288   if ( generalGroup.readBoolEntry( "auto-expiring", true ) ) {
02289     the_folderMgr->expireAllFolders( false /*scheduled, not immediate*/ );
02290     the_imapFolderMgr->expireAllFolders( false /*scheduled, not immediate*/ );
02291     the_dimapFolderMgr->expireAllFolders( false /*scheduled, not immediate*/ );
02292     // the_searchFolderMgr: no expiry there
02293   }
02294 
02295   if ( generalGroup.readBoolEntry( "auto-compaction", true ) ) {
02296     the_folderMgr->compactAllFolders( false /*scheduled, not immediate*/ );
02297     // the_imapFolderMgr: no compaction
02298     the_dimapFolderMgr->compactAllFolders( false /*scheduled, not immediate*/ );
02299     // the_searchFolderMgr: no compaction
02300   }
02301 
02302 #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h
02303   mBackgroundTasksTimer->start( 60 * 1000, true ); // check again in 1 minute
02304 #else
02305   mBackgroundTasksTimer->start( 4 * 60 * 60 * 1000, true ); // check again in 4 hours
02306 #endif
02307 
02308 }
02309 
02310 void KMKernel::expireAllFoldersNow() // called by the GUI
02311 {
02312   the_folderMgr->expireAllFolders( true /*immediate*/ );
02313   the_imapFolderMgr->expireAllFolders( true /*immediate*/ );
02314   the_dimapFolderMgr->expireAllFolders( true /*immediate*/ );
02315 }
02316 
02317 void KMKernel::compactAllFolders() // called by the GUI
02318 {
02319   the_folderMgr->compactAllFolders( true /*immediate*/ );
02320   //the_imapFolderMgr->compactAllFolders( true /*immediate*/ );
02321   the_dimapFolderMgr->compactAllFolders( true /*immediate*/ );
02322 }
02323 
02324 KMFolder* KMKernel::findFolderById( const QString& idString )
02325 {
02326   KMFolder * folder = the_folderMgr->findIdString( idString );
02327   if ( !folder )
02328     folder = the_imapFolderMgr->findIdString( idString );
02329   if ( !folder )
02330     folder = the_dimapFolderMgr->findIdString( idString );
02331   if ( !folder )
02332     folder = the_searchFolderMgr->findIdString( idString );
02333   return folder;
02334 }
02335 
02336 ::KIMProxy* KMKernel::imProxy()
02337 {
02338   return KIMProxy::instance( kapp->dcopClient() );
02339 }
02340 
02341 void KMKernel::enableMailCheck()
02342 {
02343   mMailCheckAborted = false;
02344 }
02345 
02346 bool KMKernel::mailCheckAborted() const
02347 {
02348   return mMailCheckAborted;
02349 }
02350 
02351 void KMKernel::abortMailCheck()
02352 {
02353   mMailCheckAborted = true;
02354 }
02355 
02356 bool KMKernel::canQueryClose()
02357 {
02358   if ( KMMainWidget::mainWidgetList() &&
02359        KMMainWidget::mainWidgetList()->count() > 1 )
02360     return true;
02361   KMMainWidget *widget = getKMMainWidget();
02362   if ( !widget )
02363     return true;
02364   KMSystemTray* systray = widget->systray();
02365   if ( !systray || GlobalSettings::closeDespiteSystemTray() )
02366       return true;
02367   if ( systray->mode() == GlobalSettings::EnumSystemTrayPolicy::ShowAlways ) {
02368     systray->hideKMail();
02369     return false;
02370   } else if ( ( systray->mode() == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ) && ( systray->hasUnreadMail() )) {
02371     systray->show();
02372     systray->hideKMail();
02373     return false;
02374   }
02375   return true;
02376 }
02377 
02378 void KMKernel::messageCountChanged()
02379 {
02380   mTimeOfLastMessageCountChange = ::time( 0 );
02381 }
02382 
02383 int KMKernel::timeOfLastMessageCountChange() const
02384 {
02385   return mTimeOfLastMessageCountChange;
02386 }
02387 
02388 Wallet *KMKernel::wallet() {
02389   static bool walletOpenFailed = false;
02390   if ( mWallet && mWallet->isOpen() )
02391     return mWallet;
02392 
02393   if ( !Wallet::isEnabled() || walletOpenFailed )
02394     return 0;
02395 
02396   // find an appropriate parent window for the wallet dialog
02397   WId window = 0;
02398   if ( qApp->activeWindow() )
02399     window = qApp->activeWindow()->winId();
02400   else if ( getKMMainWidget() )
02401     window = getKMMainWidget()->topLevelWidget()->winId();
02402 
02403   delete mWallet;
02404   mWallet = Wallet::openWallet( Wallet::NetworkWallet(), window );
02405 
02406   if ( !mWallet ) {
02407     walletOpenFailed = true;
02408     return 0;
02409   }
02410 
02411   if ( !mWallet->hasFolder( "kmail" ) )
02412     mWallet->createFolder( "kmail" );
02413   mWallet->setFolder( "kmail" );
02414   return mWallet;
02415 }
02416 
02417 QValueList< QGuardedPtr<KMFolder> > KMKernel::allFolders()
02418 {
02419   QStringList names;
02420   QValueList<QGuardedPtr<KMFolder> > folders;
02421   folderMgr()->createFolderList(&names, &folders);
02422   imapFolderMgr()->createFolderList(&names, &folders);
02423   dimapFolderMgr()->createFolderList(&names, &folders);
02424   searchFolderMgr()->createFolderList(&names, &folders);
02425 
02426   return folders;
02427 }
02428 
02429 KMFolder *KMKernel::currentFolder() {
02430   KMMainWidget *widget = getKMMainWidget();
02431   KMFolder *folder = 0;
02432   if ( widget && widget->folderTree() ) {
02433     folder = widget->folderTree()->currentFolder();
02434   }
02435   return folder;
02436 }
02437 
02438 // can't be inline, since KMSender isn't known to implement
02439 // KMail::MessageSender outside this .cpp file
02440 KMail::MessageSender * KMKernel::msgSender() { return the_msgSender; }
02441 
02442 #include "kmkernel.moc"