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