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       parser.process( NULL, NULL );
00411     }
00412   }
00413   else if (!body.isEmpty())
00414   {
00415     msg->setBody(body.utf8());
00416   }
00417   else
00418   {
00419     TemplateParser parser( msg, TemplateParser::NewMessage );
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     parser.process( NULL, NULL );
00526   }
00527 
00528   bool iCalAutoSend = false;
00529   bool noWordWrap = false;
00530   bool isICalInvitation = false;
00531   KConfigGroup options( config(), "Groupware" );
00532   if ( !attachData.isEmpty() ) {
00533     isICalInvitation = attachName == "cal.ics" &&
00534       attachType == "text" &&
00535       attachSubType == "calendar" &&
00536       attachParamAttr == "method";
00537     // Remove BCC from identity on ical invitations (https://intevation.de/roundup/kolab/issue474)
00538     if ( isICalInvitation && bcc.isEmpty() )
00539       msg->setBcc( "" );
00540     if ( isICalInvitation &&
00541         GlobalSettings::self()->legacyBodyInvites() ) {
00542       // KOrganizer invitation caught and to be sent as body instead
00543       msg->setBody( attachData );
00544       msg->setHeaderField( "Content-Type",
00545                            QString( "text/calendar; method=%1; "
00546                                     "charset=\"utf-8\"" ).
00547                            arg( attachParamValue ) );
00548 
00549       iCalAutoSend = true; // no point in editing raw ICAL
00550       noWordWrap = true; // we shant word wrap inline invitations
00551     } else {
00552       // Just do what we're told to do
00553       msgPart = new KMMessagePart;
00554       msgPart->setName( attachName );
00555       msgPart->setCteStr( attachCte );
00556       msgPart->setBodyEncoded( attachData );
00557       msgPart->setTypeStr( attachType );
00558       msgPart->setSubtypeStr( attachSubType );
00559       msgPart->setParameter( attachParamAttr, attachParamValue );
00560        if( ! GlobalSettings::self()->exchangeCompatibleInvitations() ) {
00561         msgPart->setContentDisposition( attachContDisp );
00562       }
00563       if( !attachCharset.isEmpty() ) {
00564         // kdDebug(5006) << "KMKernel::openComposer set attachCharset to "
00565         // << attachCharset << endl;
00566         msgPart->setCharset( attachCharset );
00567       }
00568       // Don't show the composer window, if the automatic sending is checked
00569       KConfigGroup options( config(), "Groupware" );
00570       iCalAutoSend = options.readBoolEntry( "AutomaticSending", true );
00571     }
00572   }
00573 
00574   KMail::Composer * cWin = KMail::makeComposer();
00575   cWin->setMsg( msg, !isICalInvitation /* mayAutoSign */ );
00576   cWin->setSigningAndEncryptionDisabled( isICalInvitation
00577       && GlobalSettings::self()->legacyBodyInvites() );
00578   cWin->setAutoDelete( true );
00579   if( noWordWrap )
00580     cWin->disableWordWrap();
00581   else
00582     cWin->setCharset( "", true );
00583   if ( msgPart )
00584     cWin->addAttach(msgPart);
00585 
00586   if ( isICalInvitation ) {
00587     cWin->disableRecipientNumberCheck();
00588     cWin->disableForgottenAttachmentsCheck();
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     parser.process( NULL, NULL );
00633   }
00634 
00635   KMail::Composer * cWin = KMail::makeComposer( msg );
00636   cWin->setCharset("", true);
00637   if (!hidden) {
00638     cWin->show();
00639     // Activate window - doing this instead of KWin::activateWindow(cWin->winId());
00640     // so that it also works when called from KMailApplication::newInstance()
00641 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00642     KStartupInfo::setNewStartupId( cWin, kapp->startupId() );
00643 #endif
00644   }
00645 
00646   return DCOPRef( cWin->asMailComposerIFace() );
00647 }
00648 
00649 DCOPRef KMKernel::newMessage(const QString &to,
00650                              const QString &cc,
00651                              const QString &bcc,
00652                              bool hidden,
00653                              bool useFolderId,
00654                              const KURL & /*messageFile*/,
00655                              const KURL &attachURL)
00656 {
00657   KMail::Composer * win = 0;
00658   KMMessage *msg = new KMMessage;
00659   KMFolder *folder = NULL;
00660   uint id;
00661 
00662   if ( useFolderId ) {
00663     //create message with required folder identity
00664     folder = currentFolder();
00665     id = folder ? folder->identity() : 0;
00666     msg->initHeader( id );
00667   } else {
00668     msg->initHeader();
00669   }
00670   msg->setCharset("utf-8");
00671   //set basic headers
00672   if (!to.isEmpty()) msg->setTo(to);
00673   if (!cc.isEmpty()) msg->setCc(cc);
00674   if (!bcc.isEmpty()) msg->setBcc(bcc);
00675 
00676   if ( useFolderId ) {
00677     TemplateParser parser( msg, TemplateParser::NewMessage );
00678     parser.process( NULL, folder );
00679     win = makeComposer( msg, id );
00680   } else {
00681     TemplateParser parser( msg, TemplateParser::NewMessage );
00682     parser.process( NULL, folder );
00683     win = makeComposer( msg );
00684   }
00685 
00686   //Add the attachment if we have one
00687   if(!attachURL.isEmpty() && attachURL.isValid()) {
00688     win->addAttach(attachURL);
00689   }
00690 
00691   //only show window when required
00692   if(!hidden) {
00693     win->show();
00694   }
00695   return DCOPRef( win->asMailComposerIFace() );
00696 }
00697 
00698 int KMKernel::viewMessage( const KURL & messageFile )
00699 {
00700   KMOpenMsgCommand *openCommand = new KMOpenMsgCommand( 0, messageFile );
00701 
00702   openCommand->start();
00703 
00704   return 1;
00705 }
00706 
00707 int KMKernel::sendCertificate( const QString& to, const QByteArray& certData )
00708 {
00709   KMMessage *msg = new KMMessage;
00710   msg->initHeader();
00711   msg->setCharset("utf-8");
00712   msg->setSubject( i18n( "Certificate Signature Request" ) );
00713   if (!to.isEmpty()) msg->setTo(to);
00714   // ### Make this message customizable via KIOSK
00715   msg->setBody( i18n( "Please create a certificate from attachment and return to sender." ).utf8() );
00716 
00717   KMail::Composer * cWin = KMail::makeComposer( msg );
00718   cWin->setCharset("", true);
00719   cWin->slotSetAlwaysSend( true );
00720   if (!certData.isEmpty()) {
00721     KMMessagePart *msgPart = new KMMessagePart;
00722     msgPart->setName("smime.p10");
00723     msgPart->setCteStr("base64");
00724     msgPart->setBodyEncodedBinary(certData);
00725     msgPart->setTypeStr("application");
00726     msgPart->setSubtypeStr("pkcs10");
00727     msgPart->setContentDisposition("attachment; filename=smime.p10");
00728     cWin->addAttach(msgPart);
00729   }
00730 
00731   cWin->show();
00732   return 1;
00733 }
00734 
00735 KMMsgStatus KMKernel::strToStatus(const QString &flags)
00736 {
00737     KMMsgStatus status = 0;
00738     if (!flags.isEmpty()) {
00739         for (uint n = 0; n < flags.length() ; n++) {
00740             switch (flags[n]) {
00741                 case 'N':
00742                     status |= KMMsgStatusNew;
00743                     break;
00744                 case 'U':
00745                     status |= KMMsgStatusUnread;
00746                     break;
00747                 case 'O':
00748                     status |= KMMsgStatusOld;
00749                     break;
00750                 case 'R':
00751                     status |= KMMsgStatusRead;
00752                     break;
00753                 case 'D':
00754                     status |= KMMsgStatusDeleted;
00755                     break;
00756                 case 'A':
00757                     status |= KMMsgStatusReplied;
00758                     break;
00759                 case 'F':
00760                     status |= KMMsgStatusForwarded;
00761                     break;
00762                 case 'Q':
00763                     status |= KMMsgStatusQueued;
00764                     break;
00765                 case 'K':
00766                     status |= KMMsgStatusTodo;
00767                     break;
00768                 case 'S':
00769                     status |= KMMsgStatusSent;
00770                     break;
00771                 case 'G':
00772                     status |= KMMsgStatusFlag;
00773                     break;
00774                 case 'W':
00775                     status |= KMMsgStatusWatched;
00776                     break;
00777                 case 'I':
00778                     status |= KMMsgStatusIgnored;
00779                     break;
00780                 case 'P':
00781                     status |= KMMsgStatusSpam;
00782                     break;
00783                 case 'H':
00784                     status |= KMMsgStatusHam;
00785                     break;
00786                 case 'T':
00787                     status |= KMMsgStatusHasAttach;
00788                     break;
00789                 case 'C':
00790                     status |= KMMsgStatusHasNoAttach;
00791                     break;
00792                 default:
00793                     break;
00794             }
00795         }
00796     }
00797     return status;
00798 }
00799 
00800 int KMKernel::dcopAddMessage( const QString & foldername, const QString & msgUrlString,
00801                               const QString & MsgStatusFlags)
00802 {
00803   return dcopAddMessage(foldername, KURL(msgUrlString), MsgStatusFlags);
00804 }
00805 
00806 int KMKernel::dcopAddMessage( const QString & foldername,const KURL & msgUrl,
00807                               const QString & MsgStatusFlags)
00808 {
00809   kdDebug(5006) << "KMKernel::dcopAddMessage called" << endl;
00810 
00811   if ( foldername.isEmpty() || foldername.startsWith("."))
00812     return -1;
00813 
00814   int retval;
00815   bool readFolderMsgIds = false;
00816   QString _foldername = foldername.stripWhiteSpace();
00817   _foldername = _foldername.replace('\\',""); //try to prevent ESCAPE Sequences
00818 
00819   if ( foldername != mAddMessageLastFolder ) {
00820     mAddMessageMsgIds.clear();
00821     readFolderMsgIds = true;
00822     mAddMessageLastFolder = foldername;
00823   }
00824 
00825   if (!msgUrl.isEmpty() && msgUrl.isLocalFile()) {
00826 
00827     // This is a proposed change by Daniel Andor.
00828     // He proposed to change from the fopen(blah)
00829     // to a KPIM::kFileToString(blah).
00830     // Although it assigns a QString to a QString,
00831     // because of the implicit sharing this poses
00832     // no memory or performance penalty.
00833 
00834     const QCString messageText =
00835       KPIM::kFileToString( msgUrl.path(), true, false );
00836     if ( messageText.isEmpty() )
00837       return -2;
00838 
00839     KMMessage *msg = new KMMessage();
00840     msg->fromString( messageText );
00841 
00842     if (readFolderMsgIds) {
00843       if ( foldername.contains("/")) {
00844         QString tmp_fname = "";
00845         KMFolder *folder = NULL;
00846         KMFolderDir *subfolder;
00847         bool root = true;
00848 
00849         QStringList subFList = QStringList::split("/",_foldername,false);
00850 
00851         for ( QStringList::Iterator it = subFList.begin(); it != subFList.end(); ++it ) {
00852           QString _newFolder = *it;
00853           if(_newFolder.startsWith(".")) return -1;
00854 
00855           if(root) {
00856             folder = the_folderMgr->findOrCreate(*it, false);
00857             if (folder) {
00858               root = false;
00859               tmp_fname = "/" + *it;
00860             }
00861             else return -1;
00862           } else {
00863             subfolder = folder->createChildFolder();
00864             tmp_fname += "/" + *it;
00865             if(!the_folderMgr->getFolderByURL( tmp_fname )) {
00866              folder = the_folderMgr->createFolder(*it, false, folder->folderType(), subfolder);
00867             }
00868 
00869             if(!(folder = the_folderMgr->getFolderByURL( tmp_fname ))) return -1;
00870           }
00871         }
00872 
00873         mAddMsgCurrentFolder = the_folderMgr->getFolderByURL( tmp_fname );
00874         if(!folder) return -1;
00875 
00876       } else {
00877         mAddMsgCurrentFolder = the_folderMgr->findOrCreate(_foldername, false);
00878       }
00879     }
00880 
00881     if ( mAddMsgCurrentFolder ) {
00882       if (readFolderMsgIds) {
00883 
00884         // OLD COMMENT:
00885         // Try to determine if a message already exists in
00886         // the folder. The message id that is searched for, is
00887         // the subject line + the date. This should be quite
00888         // unique. The change that a given date with a given
00889         // subject is in the folder twice is very small.
00890         // If the subject is empty, the fromStrip string
00891         // is taken.
00892 
00893     // NEW COMMENT from Danny Kukawka (danny.kukawka@web.de):
00894     // subject line + the date is only unique if the following
00895     // return a correct unique value:
00896     //  time_t  DT = mb->date();
00897         //  QString dt = ctime(&DT);
00898     // But if the datestring in the Header isn't RFC conform
00899     // subject line + the date isn't unique.
00900     //
00901     // The only uique headerfield is the Message-ID. In some
00902     // cases this could be empty. I then I use the
00903     // subject line + dateStr .
00904 
00905         int i;
00906 
00907         mAddMsgCurrentFolder->open("dcopadd");
00908         for( i=0; i<mAddMsgCurrentFolder->count(); i++) {
00909           KMMsgBase *mb = mAddMsgCurrentFolder->getMsgBase(i);
00910       QString id = mb->msgIdMD5();
00911       if ( id.isEmpty() ) {
00912             id = mb->subject();
00913             if ( id.isEmpty() )
00914               id = mb->fromStrip();
00915             if ( id.isEmpty() )
00916               id = mb->toStrip();
00917 
00918             id += mb->dateStr();
00919       }
00920 
00921           //fprintf(stderr,"%s\n",(const char *) id);
00922           if ( !id.isEmpty() ) {
00923             mAddMessageMsgIds.append(id);
00924           }
00925         }
00926         mAddMsgCurrentFolder->close("dcopadd");
00927       }
00928 
00929       QString msgId = msg->msgIdMD5();
00930       if ( msgId.isEmpty()) {
00931     msgId = msg->subject();
00932     if ( msgId.isEmpty() )
00933           msgId = msg->fromStrip();
00934         if ( msgId.isEmpty() )
00935           msgId = msg->toStrip();
00936 
00937     msgId += msg->dateStr();
00938       }
00939 
00940       int k = mAddMessageMsgIds.findIndex( msgId );
00941       //fprintf(stderr,"find %s = %d\n",(const char *) msgId,k);
00942 
00943       if ( k == -1 ) {
00944         if ( !msgId.isEmpty() ) {
00945           mAddMessageMsgIds.append( msgId );
00946         }
00947 
00948         if ( !MsgStatusFlags.isEmpty() ) {
00949           KMMsgStatus status = strToStatus(MsgStatusFlags);
00950           if (status) msg->setStatus(status);
00951         }
00952 
00953         int index;
00954         if ( mAddMsgCurrentFolder->addMsg( msg, &index ) == 0 ) {
00955           mAddMsgCurrentFolder->unGetMsg( index );
00956           retval = 1;
00957         } else {
00958           retval =- 2;
00959           delete msg;
00960           msg = 0;
00961         }
00962       } else {
00963         //qDebug( "duplicate: " + msgId + "; subj: " + msg->subject() + ", from: " + msgId = msg->fromStrip());
00964     retval = -4;
00965       }
00966     } else {
00967       retval = -1;
00968     }
00969   } else {
00970     retval = -2;
00971   }
00972   return retval;
00973 }
00974 
00975 void KMKernel::dcopResetAddMessage()
00976 {
00977   mAddMessageMsgIds.clear();
00978   mAddMessageLastFolder = QString();
00979 }
00980 
00981 int KMKernel::dcopAddMessage_fastImport( const QString & foldername,
00982                                          const QString & msgUrlString,
00983                                          const QString & MsgStatusFlags)
00984 {
00985   return dcopAddMessage_fastImport(foldername, KURL(msgUrlString), MsgStatusFlags);
00986 }
00987 
00988 int KMKernel::dcopAddMessage_fastImport( const QString & foldername,
00989                                          const KURL & msgUrl,
00990                                          const QString & MsgStatusFlags)
00991 {
00992   // Use this function to import messages without
00993   // search for already existing emails.
00994   kdDebug(5006) << "KMKernel::dcopAddMessage_fastImport called" << endl;
00995 
00996   if ( foldername.isEmpty() || foldername.startsWith("."))
00997     return -1;
00998 
00999   int retval;
01000   bool createNewFolder = false;
01001 
01002   QString _foldername = foldername.stripWhiteSpace();
01003   _foldername = _foldername.replace('\\',""); //try to prevent ESCAPE Sequences
01004 
01005   if ( foldername != mAddMessageLastFolder ) {
01006     createNewFolder = true;
01007     mAddMessageLastFolder = foldername;
01008   }
01009 
01010 
01011   if ( !msgUrl.isEmpty() && msgUrl.isLocalFile() ) {
01012     const QCString messageText =
01013       KPIM::kFileToString( msgUrl.path(), true, false );
01014     if ( messageText.isEmpty() )
01015       return -2;
01016 
01017     KMMessage *msg = new KMMessage();
01018     msg->fromString( messageText );
01019 
01020     if (createNewFolder) {
01021       if ( foldername.contains("/")) {
01022         QString tmp_fname = "";
01023         KMFolder *folder = NULL;
01024         KMFolderDir *subfolder;
01025         bool root = true;
01026 
01027         QStringList subFList = QStringList::split("/",_foldername,false);
01028 
01029         for ( QStringList::Iterator it = subFList.begin(); it != subFList.end(); ++it ) {
01030           QString _newFolder = *it;
01031           if(_newFolder.startsWith(".")) return -1;
01032 
01033           if(root) {
01034             folder = the_folderMgr->findOrCreate(*it, false);
01035             if (folder) {
01036               root = false;
01037               tmp_fname = "/" + *it;
01038             }
01039             else return -1;
01040           } else {
01041             subfolder = folder->createChildFolder();
01042             tmp_fname += "/" + *it;
01043             if(!the_folderMgr->getFolderByURL( tmp_fname )) {
01044               folder = the_folderMgr->createFolder(*it, false, folder->folderType(), subfolder);
01045             }
01046             if(!(folder = the_folderMgr->getFolderByURL( tmp_fname ))) return -1;
01047           }
01048         }
01049 
01050       mAddMsgCurrentFolder = the_folderMgr->getFolderByURL( tmp_fname );
01051       if(!folder) return -1;
01052 
01053       } else {
01054         mAddMsgCurrentFolder = the_folderMgr->findOrCreate(_foldername, false);
01055       }
01056     }
01057 
01058     if ( mAddMsgCurrentFolder ) {
01059       int index;
01060 
01061       if( !MsgStatusFlags.isEmpty() ) {
01062         KMMsgStatus status = strToStatus(MsgStatusFlags);
01063         if (status) msg->setStatus(status);
01064       }
01065 
01066       if ( mAddMsgCurrentFolder->addMsg( msg, &index ) == 0 ) {
01067         mAddMsgCurrentFolder->unGetMsg( index );
01068         retval = 1;
01069       } else {
01070         retval =- 2;
01071         delete msg;
01072         msg = 0;
01073       }
01074     } else {
01075       retval = -1;
01076     }
01077   } else {
01078     retval = -2;
01079   }
01080 
01081   return retval;
01082 }
01083 
01084 void KMKernel::showImportArchiveDialog()
01085 {
01086   KMMainWidget *mainWidget = getKMMainWidget();
01087   KMail::ImportArchiveDialog *importDialog = new KMail::ImportArchiveDialog( mainWidget, WDestructiveClose );
01088   importDialog->setFolder( mainWidget->folderTree()->currentFolder() );
01089   importDialog->show();
01090 }
01091 
01092 QStringList KMKernel::folderList() const
01093 {
01094   QStringList folders;
01095   const QString localPrefix = "/Local";
01096   folders << localPrefix;
01097   the_folderMgr->getFolderURLS( folders, localPrefix );
01098   the_imapFolderMgr->getFolderURLS( folders );
01099   the_dimapFolderMgr->getFolderURLS( folders );
01100   return folders;
01101 }
01102 
01103 DCOPRef KMKernel::getFolder( const QString& vpath )
01104 {
01105   const QString localPrefix = "/Local";
01106   if ( the_folderMgr->getFolderByURL( vpath ) )
01107     return DCOPRef( new FolderIface( vpath ) );
01108   else if ( vpath.startsWith( localPrefix ) &&
01109             the_folderMgr->getFolderByURL( vpath.mid( localPrefix.length() ) ) )
01110     return DCOPRef( new FolderIface( vpath.mid( localPrefix.length() ) ) );
01111   else if ( the_imapFolderMgr->getFolderByURL( vpath ) )
01112     return DCOPRef( new FolderIface( vpath ) );
01113   else if ( the_dimapFolderMgr->getFolderByURL( vpath ) )
01114     return DCOPRef( new FolderIface( vpath ) );
01115   return DCOPRef();
01116 }
01117 
01118 void KMKernel::raise()
01119 {
01120   DCOPRef kmail( "kmail", "kmail" );
01121   kmail.call( "newInstance" );
01122 }
01123 
01124 bool KMKernel::showMail( Q_UINT32 serialNumber, QString /* messageId */ )
01125 {
01126   KMMainWidget *mainWidget = 0;
01127   if (KMainWindow::memberList) {
01128     KMainWindow *win = 0;
01129     QObjectList *l;
01130 
01131     // First look for a KMainWindow.
01132     for (win = KMainWindow::memberList->first(); win;
01133          win = KMainWindow::memberList->next()) {
01134       // Then look for a KMMainWidget.
01135       l = win->queryList("KMMainWidget");
01136       if (l && l->first()) {
01137     mainWidget = dynamic_cast<KMMainWidget *>(l->first());
01138     if (win->isActiveWindow())
01139       break;
01140       }
01141     }
01142   }
01143 
01144   if (mainWidget) {
01145     int idx = -1;
01146     KMFolder *folder = 0;
01147     KMMsgDict::instance()->getLocation(serialNumber, &folder, &idx);
01148     if (!folder || (idx == -1))
01149       return false;
01150     KMFolderOpener openFolder(folder, "showmail");
01151     KMMsgBase *msgBase = folder->getMsgBase(idx);
01152     if (!msgBase)
01153       return false;
01154     bool unGet = !msgBase->isMessage();
01155     KMMessage *msg = folder->getMsg(idx);
01156 
01157     KMReaderMainWin *win = new KMReaderMainWin( false, false );
01158     KMMessage *newMessage = new KMMessage( *msg );
01159     newMessage->setParent( msg->parent() );
01160     newMessage->setMsgSerNum( msg->getMsgSerNum() );
01161     newMessage->setReadyToShow( true );
01162     win->showMsg( GlobalSettings::self()->overrideCharacterEncoding(), newMessage );
01163     win->show();
01164 
01165     if (unGet)
01166       folder->unGetMsg(idx);
01167     return true;
01168   }
01169 
01170   return false;
01171 }
01172 
01173 QString KMKernel::getFrom( Q_UINT32 serialNumber )
01174 {
01175   int idx = -1;
01176   KMFolder *folder = 0;
01177   KMMsgDict::instance()->getLocation(serialNumber, &folder, &idx);
01178   if (!folder || (idx == -1))
01179     return QString::null;
01180   KMFolderOpener openFolder(folder, "getFrom");
01181   KMMsgBase *msgBase = folder->getMsgBase(idx);
01182   if (!msgBase)
01183     return QString::null;
01184   bool unGet = !msgBase->isMessage();
01185   KMMessage *msg = folder->getMsg(idx);
01186   QString result = msg->from();
01187   if (unGet)
01188     folder->unGetMsg(idx);
01189   return result;
01190 }
01191 
01192 QString KMKernel::debugScheduler()
01193 {
01194   QString res = KMail::ActionScheduler::debug();
01195   return res;
01196 }
01197 
01198 QString KMKernel::debugSernum( Q_UINT32 serialNumber )
01199 {
01200   QString res;
01201   if (serialNumber != 0) {
01202     int idx = -1;
01203     KMFolder *folder = 0;
01204     KMMsgBase *msg = 0;
01205     KMMsgDict::instance()->getLocation( serialNumber, &folder, &idx );
01206     // It's possible that the message has been deleted or moved into a
01207     // different folder
01208     if (folder && (idx != -1)) {
01209       // everything is ok
01210       KMFolderOpener openFolder(folder, "debugser");
01211       msg = folder->getMsgBase( idx );
01212       if (msg) {
01213         res.append( QString( " subject %s,\n sender %s,\n date %s.\n" )
01214                              .arg( msg->subject() )
01215                              .arg( msg->fromStrip() )
01216                              .arg( msg->dateStr() ) );
01217       } else {
01218         res.append( QString( "Invalid serial number." ) );
01219       }
01220     } else {
01221       res.append( QString( "Invalid serial number." ) );
01222     }
01223   }
01224   return res;
01225 }
01226 
01227 
01228 void KMKernel::pauseBackgroundJobs()
01229 {
01230   mBackgroundTasksTimer->stop();
01231   mJobScheduler->pause();
01232 }
01233 
01234 void KMKernel::resumeBackgroundJobs()
01235 {
01236   mJobScheduler->resume();
01237   mBackgroundTasksTimer->start( 4 * 60 * 60 * 1000, true );
01238 }
01239 
01240 void KMKernel::stopNetworkJobs()
01241 {
01242   if ( GlobalSettings::self()->networkState() == GlobalSettings::EnumNetworkState::Offline )
01243     return;
01244 
01245   GlobalSettings::setNetworkState( GlobalSettings::EnumNetworkState::Offline );
01246   BroadcastStatus::instance()->setStatusMsg( i18n("KMail is set to be offline; all network jobs are suspended"));
01247   emit onlineStatusChanged( (GlobalSettings::EnumNetworkState::type)GlobalSettings::networkState() );
01248 }
01249 
01250 void KMKernel::resumeNetworkJobs()
01251 {
01252   if ( GlobalSettings::self()->networkState() == GlobalSettings::EnumNetworkState::Online )
01253     return;
01254 
01255   GlobalSettings::setNetworkState( GlobalSettings::EnumNetworkState::Online );
01256   BroadcastStatus::instance()->setStatusMsg( i18n("KMail is set to be online; all network jobs resumed"));
01257   emit onlineStatusChanged( (GlobalSettings::EnumNetworkState::type)GlobalSettings::networkState() );
01258 
01259   if ( kmkernel->msgSender()->sendImmediate() ) {
01260     kmkernel->msgSender()->sendQueued();
01261   }
01262 }
01263 
01264 bool KMKernel::isOffline()
01265 {
01266   if ( GlobalSettings::self()->networkState() == GlobalSettings::EnumNetworkState::Offline )
01267     return true;
01268   else
01269     return false;
01270 }
01271 
01272 bool KMKernel::askToGoOnline()
01273 {
01274   // already asking means we are offline and need to wait anyhow
01275   if ( s_askingToGoOnline ) {
01276     return false;
01277   }
01278 
01279   if ( kmkernel->isOffline() ) {
01280     s_askingToGoOnline = true;
01281     int rc =
01282     KMessageBox::questionYesNo( KMKernel::self()->mainWin(),
01283                                 i18n("KMail is currently in offline mode. "
01284                                      "How do you want to proceed?"),
01285                                 i18n("Online/Offline"),
01286                                 i18n("Work Online"),
01287                                 i18n("Work Offline"));
01288 
01289     s_askingToGoOnline = false;
01290     if( rc == KMessageBox::No ) {
01291       return false;
01292     } else {
01293       kmkernel->resumeNetworkJobs();
01294     }
01295   }
01296   return true;
01297 }
01298 
01299 /********************************************************************/
01300 /*                        Kernel methods                            */
01301 /********************************************************************/
01302 
01303 void KMKernel::quit()
01304 {
01305   // Called when all windows are closed. Will take care of compacting,
01306   // sending... should handle session management too!!
01307 }
01308   /* TODO later:
01309    Asuming that:
01310      - msgsender is nonblocking
01311        (our own, QSocketNotifier based. Pops up errors and sends signal
01312         senderFinished when done)
01313 
01314    o If we are getting mail, stop it (but dont lose something!)
01315          [Done already, see mailCheckAborted]
01316    o If we are sending mail, go on UNLESS this was called by SM,
01317        in which case stop ASAP that too (can we warn? should we continue
01318        on next start?)
01319    o If we are compacting, or expunging, go on UNLESS this was SM call.
01320        In that case stop compacting ASAP and continue on next start, before
01321        touching any folders. [Not needed anymore with CompactionJob]
01322 
01323    KMKernel::quit ()
01324    {
01325      SM call?
01326        if compacting, stop;
01327        if sending, stop;
01328        if receiving, stop;
01329        Windows will take care of themselves (composer should dump
01330         its messages, if any but not in deadMail)
01331        declare us ready for the End of the Session
01332 
01333      No, normal quit call
01334        All windows are off. Anything to do, should compact or sender sends?
01335          Yes, maybe put an icon in panel as a sign of life
01336          if sender sending, connect us to his finished slot, declare us ready
01337                             for quit and wait for senderFinished
01338          if not, Folder manager, go compact sent-mail and outbox
01339 }                (= call slotFinished())
01340 
01341 void KMKernel::slotSenderFinished()
01342 {
01343   good, Folder manager go compact sent-mail and outbox
01344   clean up stage1 (release folders and config, unregister from dcop)
01345     -- another kmail may start now ---
01346   kapp->quit();
01347 }
01348 */
01349 
01350 
01351 /********************************************************************/
01352 /*            Init, Exit, and handler  methods                      */
01353 /********************************************************************/
01354 void KMKernel::testDir(const char *_name)
01355 {
01356   QString foldersPath = QDir::homeDirPath() + QString( _name );
01357   QFileInfo info( foldersPath );
01358   if ( !info.exists() ) {
01359     if ( ::mkdir( QFile::encodeName( foldersPath ) , S_IRWXU ) == -1 ) {
01360       KMessageBox::sorry(0, i18n("KMail could not create folder '%1';\n"
01361                                  "please make sure that you can view and "
01362                                  "modify the content of the folder '%2'.")
01363                             .arg( foldersPath ).arg( QDir::homeDirPath() ) );
01364       ::exit(-1);
01365     }
01366   }
01367   if ( !info.isDir() || !info.isReadable() || !info.isWritable() ) {
01368     KMessageBox::sorry(0, i18n("The permissions of the folder '%1' are "
01369                                "incorrect;\n"
01370                                "please make sure that you can view and modify "
01371                                "the content of this folder.")
01372                           .arg( foldersPath ) );
01373     ::exit(-1);
01374   }
01375 }
01376 
01377 
01378 //-----------------------------------------------------------------------------
01379 // Open a composer for each message found in the dead.letter folder
01380 void KMKernel::recoverDeadLetters()
01381 {
01382   QDir dir( localDataPath() + "autosave/cur" );
01383   if ( !dir.exists() ) {
01384     kdWarning(5006) << "Autosave directory " << dir.path() << " not found!" << endl;
01385     return;
01386   }
01387 
01388   const QStringList entryList = dir.entryList( QDir::Files | QDir::NoSymLinks, QDir::Unsorted );
01389   for ( unsigned int i = 0; i < entryList.size(); i++ ) {
01390     const QString fileName = entryList[i];
01391     QFile file( dir.path() + '/' + fileName );
01392     if ( !file.open( IO_ReadOnly ) ) {
01393       kdWarning(5006) << "Unable to open autosave file " << fileName << endl;
01394       continue;
01395     }
01396     const QByteArray msgData = file.readAll();
01397     file.close();
01398 
01399     if ( msgData.isEmpty() ) {
01400       kdWarning(5006) << "autosave file " << fileName << " is empty!" << endl;
01401       continue;
01402     }
01403 
01404     KMMessage *msg = new KMMessage(); // Composer will take ownership
01405     msg->fromByteArray( msgData );
01406     KMail::Composer * win = KMail::makeComposer();
01407     win->setMsg( msg, false, false, true );
01408     win->setAutoSaveFilename( fileName );
01409     win->show();
01410   }
01411 }
01412 
01413 //-----------------------------------------------------------------------------
01414 void KMKernel::initFolders(KConfig* cfg)
01415 {
01416   QString name;
01417 
01418   name = cfg->readEntry("inboxFolder");
01419 
01420   // Currently the folder manager cannot manage folders which are not
01421   // in the base folder directory.
01422   //if (name.isEmpty()) name = getenv("MAIL");
01423 
01424   if (name.isEmpty()) name = I18N_NOOP("inbox");
01425 
01426   the_inboxFolder  = (KMFolder*)the_folderMgr->findOrCreate(name);
01427 
01428   if (the_inboxFolder->canAccess() != 0) {
01429     emergencyExit( i18n("You do not have read/write permission to your inbox folder.") );
01430   }
01431 
01432   the_inboxFolder->setSystemFolder(true);
01433   if ( the_inboxFolder->userWhoField().isEmpty() )
01434     the_inboxFolder->setUserWhoField( QString::null );
01435   // inboxFolder->open();
01436 
01437   the_outboxFolder = the_folderMgr->findOrCreate(cfg->readEntry("outboxFolder", I18N_NOOP("outbox")));
01438   if (the_outboxFolder->canAccess() != 0) {
01439     emergencyExit( i18n("You do not have read/write permission to your outbox folder.") );
01440   }
01441   the_outboxFolder->setNoChildren(true);
01442 
01443   the_outboxFolder->setSystemFolder(true);
01444   if ( the_outboxFolder->userWhoField().isEmpty() )
01445     the_outboxFolder->setUserWhoField( QString::null );
01446   /* Nuke the oubox's index file, to make sure that no ghost messages are in
01447    * it from a previous crash. Ghost messages happen in the outbox because it
01448    * the only folder where messages enter and leave within 5 seconds, which is
01449    * the leniency period for index invalidation. Since the number of mails in
01450    * this folder is expected to be very small, we can live with regenerating
01451    * the index on each start to be on the save side. */
01452   //if ( the_outboxFolder->folderType() == KMFolderTypeMaildir )
01453   //  unlink( QFile::encodeName( the_outboxFolder->indexLocation() ) );
01454   the_outboxFolder->open("kmkernel");
01455 
01456   the_sentFolder = the_folderMgr->findOrCreate(cfg->readEntry("sentFolder", I18N_NOOP("sent-mail")));
01457   if (the_sentFolder->canAccess() != 0) {
01458     emergencyExit( i18n("You do not have read/write permission to your sent-mail folder.") );
01459   }
01460   the_sentFolder->setSystemFolder(true);
01461   if ( the_sentFolder->userWhoField().isEmpty() )
01462     the_sentFolder->setUserWhoField( QString::null );
01463   // the_sentFolder->open();
01464 
01465   the_trashFolder  = the_folderMgr->findOrCreate(cfg->readEntry("trashFolder", I18N_NOOP("trash")));
01466   if (the_trashFolder->canAccess() != 0) {
01467     emergencyExit( i18n("You do not have read/write permission to your trash folder.") );
01468   }
01469   the_trashFolder->setSystemFolder( true );
01470   if ( the_trashFolder->userWhoField().isEmpty() )
01471     the_trashFolder->setUserWhoField( QString::null );
01472   // the_trashFolder->open();
01473 
01474   the_draftsFolder = the_folderMgr->findOrCreate(cfg->readEntry("draftsFolder", I18N_NOOP("drafts")));
01475   if (the_draftsFolder->canAccess() != 0) {
01476     emergencyExit( i18n("You do not have read/write permission to your drafts folder.") );
01477   }
01478   the_draftsFolder->setSystemFolder( true );
01479   if ( the_draftsFolder->userWhoField().isEmpty() )
01480     the_draftsFolder->setUserWhoField( QString::null );
01481   the_draftsFolder->open("kmkernel");
01482 
01483   the_templatesFolder =
01484     the_folderMgr->findOrCreate( cfg->readEntry( "templatesFolder",
01485                                                  I18N_NOOP("templates") ) );
01486   if ( the_templatesFolder->canAccess() != 0 ) {
01487     emergencyExit( i18n("You do not have read/write permission to your templates folder.") );
01488   }
01489   the_templatesFolder->setSystemFolder( true );
01490   if ( the_templatesFolder->userWhoField().isEmpty() )
01491     the_templatesFolder->setUserWhoField( QString::null );
01492   the_templatesFolder->open("kmkernel");
01493 }
01494 
01495 
01496 void KMKernel::init()
01497 {
01498   the_shuttingDown = false;
01499   the_server_is_ready = false;
01500 
01501   KConfig* cfg = KMKernel::config();
01502 
01503   QDir dir;
01504 
01505   KConfigGroupSaver saver(cfg, "General");
01506   the_firstStart = cfg->readBoolEntry("first-start", true);
01507   cfg->writeEntry("first-start", false);
01508   the_previousVersion = cfg->readEntry("previous-version");
01509   cfg->writeEntry("previous-version", KMAIL_VERSION);
01510   QString foldersPath = cfg->readPathEntry( "folders" );
01511   kdDebug(5006) << k_funcinfo << "foldersPath (from config): '" << foldersPath << "'" << endl;
01512 
01513   if ( foldersPath.isEmpty() ) {
01514     foldersPath = localDataPath() + "mail";
01515     if ( transferMail( foldersPath ) ) {
01516       cfg->writePathEntry( "folders", foldersPath );
01517     }
01518     kdDebug(5006) << k_funcinfo << "foldersPath (after transferMail): '" << foldersPath << "'" << endl;
01519   }
01520 
01521   // moved up here because KMMessage::stripOffPrefixes is used below
01522   KMMessage::readConfig();
01523 
01524   the_undoStack     = new UndoStack(20);
01525   the_folderMgr     = new KMFolderMgr(foldersPath);
01526   the_imapFolderMgr = new KMFolderMgr( KMFolderImap::cacheLocation(), KMImapDir);
01527   the_dimapFolderMgr = new KMFolderMgr( KMFolderCachedImap::cacheLocation(), KMDImapDir);
01528   recreateCorruptIndexFiles();
01529 
01530   the_searchFolderMgr = new KMFolderMgr(locateLocal("data","kmail/search"), KMSearchDir);
01531   KMFolder *lsf = the_searchFolderMgr->find( i18n("Last Search") );
01532   if (lsf)
01533     the_searchFolderMgr->remove( lsf );
01534 
01535   the_acctMgr       = new AccountManager();
01536   the_filterMgr     = new KMFilterMgr();
01537   the_popFilterMgr     = new KMFilterMgr(true);
01538   the_filterActionDict = new KMFilterActionDict;
01539 
01540   initFolders(cfg);
01541   the_acctMgr->readConfig();
01542   the_filterMgr->readConfig();
01543   the_popFilterMgr->readConfig();
01544   cleanupImapFolders();
01545 
01546   the_msgSender = new KMSender;
01547   the_server_is_ready = true;
01548   imProxy()->initialize();
01549   { // area for config group "Composer"
01550     KConfigGroupSaver saver(cfg, "Composer");
01551     if (cfg->readListEntry("pref-charsets").isEmpty())
01552     {
01553       cfg->writeEntry("pref-charsets", "us-ascii,iso-8859-1,locale,utf-8");
01554     }
01555   }
01556   readConfig();
01557   mICalIface->readConfig();
01558   // filterMgr->dump();
01559 #ifdef HAVE_INDEXLIB
01560   the_msgIndex = new KMMsgIndex(this); //create the indexer
01561 #else
01562   the_msgIndex = 0;
01563 #endif
01564 
01565 //#if 0
01566   the_weaver =  new KPIM::ThreadWeaver::Weaver( this );
01567   the_weaverLogger = new KPIM::ThreadWeaver::WeaverThreadLogger(this);
01568   the_weaverLogger->attach (the_weaver);
01569 //#endif
01570 
01571   connect( the_folderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01572            this, SIGNAL( folderRemoved(KMFolder*) ) );
01573   connect( the_dimapFolderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01574            this, SIGNAL( folderRemoved(KMFolder*) ) );
01575   connect( the_imapFolderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01576            this, SIGNAL( folderRemoved(KMFolder*) ) );
01577   connect( the_searchFolderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01578            this, SIGNAL( folderRemoved(KMFolder*) ) );
01579 
01580   mBackgroundTasksTimer = new QTimer( this, "mBackgroundTasksTimer" );
01581   connect( mBackgroundTasksTimer, SIGNAL( timeout() ), this, SLOT( slotRunBackgroundTasks() ) );
01582 #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h
01583   mBackgroundTasksTimer->start( 10000, true ); // 10s, singleshot
01584 #else
01585   mBackgroundTasksTimer->start( 5 * 60000, true ); // 5 minutes, singleshot
01586 #endif
01587 
01588   QTextCodec *codec;
01589   for ( int i = 0; ( codec = QTextCodec::codecForIndex ( i ) ); i++ ) {
01590     const QString asciiString( "azAZ19,.-#+!?=()&" );
01591     const QCString encodedString = codec->fromUnicode( asciiString );
01592     if ( QString::fromAscii( encodedString ) != asciiString ) {
01593       mNonAsciiCompatibleCodecs.append( codec );
01594     }
01595   }
01596 }
01597 
01598 bool KMKernel::isCodecAsciiCompatible( const QTextCodec *codec )
01599 {
01600   return !mNonAsciiCompatibleCodecs.contains( codec );
01601 }
01602 
01603 void KMKernel::readConfig()
01604 {
01605   //Needed here, since this function is also called when the configuration
01606   //changes, and the static variables should be updated then - IOF
01607   KMMessage::readConfig();
01608 }
01609 
01610 void KMKernel::cleanupImapFolders()
01611 {
01612   KMAccount *acct = 0;
01613   KMFolderNode *node = the_imapFolderMgr->dir().first();
01614   while (node)
01615   {
01616     if (node->isDir() || ((acct = the_acctMgr->find(node->id()))
01617               && ( acct->type() == "imap" )) )
01618     {
01619       node = the_imapFolderMgr->dir().next();
01620     } else {
01621       KMFolder* folder = static_cast<KMFolder*>(node);
01622       // delete only local
01623       static_cast<KMFolderImap*>( folder->storage() )->setAlreadyRemoved( true );
01624       the_imapFolderMgr->remove(folder);
01625       node = the_imapFolderMgr->dir().first();
01626     }
01627   }
01628 
01629   node = the_dimapFolderMgr->dir().first();
01630   while (node)
01631   {
01632     if (node->isDir() || ((acct = the_acctMgr->find(node->id()))
01633               && ( acct->type() == "cachedimap" )) )
01634     {
01635       node = the_dimapFolderMgr->dir().next();
01636     } else {
01637       the_dimapFolderMgr->remove(static_cast<KMFolder*>(node));
01638       node = the_dimapFolderMgr->dir().first();
01639     }
01640   }
01641 
01642   the_imapFolderMgr->quiet(true);
01643   for (acct = the_acctMgr->first(); acct; acct = the_acctMgr->next())
01644   {
01645     KMFolderImap *fld;
01646     KMAcctImap *imapAcct;
01647 
01648     if (acct->type() != "imap") continue;
01649     fld = static_cast<KMFolderImap*>(the_imapFolderMgr
01650       ->findOrCreate(QString::number(acct->id()), false, acct->id())->storage());
01651     fld->setNoContent(true);
01652     fld->folder()->setLabel(acct->name());
01653     imapAcct = static_cast<KMAcctImap*>(acct);
01654     fld->setAccount(imapAcct);
01655     imapAcct->setImapFolder(fld);
01656     fld->close( "kernel", true );
01657   }
01658   the_imapFolderMgr->quiet(false);
01659 
01660   the_dimapFolderMgr->quiet( true );
01661   for (acct = the_acctMgr->first(); acct; acct = the_acctMgr->next())
01662   {
01663     KMFolderCachedImap *cfld = 0;
01664     KMAcctCachedImap *cachedImapAcct;
01665 
01666     if (acct->type() != "cachedimap" ) continue;
01667 
01668     KMFolder* fld = the_dimapFolderMgr->find(QString::number(acct->id()));
01669     if( fld )
01670       cfld = static_cast<KMFolderCachedImap*>( fld->storage() );
01671     if (cfld == 0) {
01672       // Folder doesn't exist yet
01673       cfld = static_cast<KMFolderCachedImap*>(the_dimapFolderMgr->createFolder(QString::number(acct->id()),
01674             false, KMFolderTypeCachedImap)->storage());
01675       if (!cfld) {
01676         KMessageBox::error(0,(i18n("Cannot create file `%1' in %2.\nKMail cannot start without it.").arg(acct->name()).arg(the_dimapFolderMgr->basePath())));
01677         exit(-1);
01678       }
01679       cfld->folder()->setId( acct->id() );
01680     }
01681 
01682     cfld->setNoContent(true);
01683     cfld->folder()->setLabel(acct->name());
01684     cachedImapAcct = static_cast<KMAcctCachedImap*>(acct);
01685     cfld->setAccount(cachedImapAcct);
01686     cachedImapAcct->setImapFolder(cfld);
01687     cfld->close("kmkernel");
01688   }
01689   the_dimapFolderMgr->quiet( false );
01690 }
01691 
01692 void KMKernel::recreateCorruptIndexFiles()
01693 {
01694   QValueList<QGuardedPtr<KMFolder> > folders;
01695   QValueList<KMFolderIndex*> foldersWithBrokenIndex;
01696   QStringList strList;
01697   the_folderMgr->createFolderList( &strList, &folders );
01698   the_imapFolderMgr->createFolderList( &strList, &folders );
01699   the_dimapFolderMgr->createFolderList( &strList, &folders );
01700   for ( int i = 0; folders.at(i) != folders.end(); i++ ) {
01701     KMFolder * const folder = *folders.at(i);
01702     if ( !folder || folder->isDir() || folder->isOpened() )
01703       continue;
01704     KMFolderIndex * const index = dynamic_cast<KMFolderIndex*>( folder->storage() );
01705     if ( index && index->indexStatus() != KMFolderIndex::IndexOk ) {
01706       foldersWithBrokenIndex.append( index );
01707     }
01708   }
01709 
01710   if ( !foldersWithBrokenIndex.isEmpty() ) {
01711     QStringList folderNames;
01712     for ( uint i = 0; i < foldersWithBrokenIndex.size(); i++ ) {
01713       folderNames << foldersWithBrokenIndex[i]->label();
01714     }
01715 
01716     KMessageBox::informationList( 0, i18n( "There is a problem with the mail index of the following "
01717                                            "folders, the indices will now be regenerated.\n"
01718                                            "This can happen because the index files are out of date, missing or corrupted.\n"
01719                                            "Contact your administrator if this happens frequently.\n"
01720                                            "Some information, like status flags, might get lost." ),
01721                                   folderNames, i18n( "Problem with mail indices" ) );
01722 
01723     for ( uint i = 0; i < foldersWithBrokenIndex.size(); i++ ) {
01724       foldersWithBrokenIndex[i]->silentlyRecreateIndex();
01725     }
01726   }
01727 }
01728 
01729 bool KMKernel::doSessionManagement()
01730 {
01731 
01732   // Do session management
01733   if (kapp->isRestored()){
01734     int n = 1;
01735     while (KMMainWin::canBeRestored(n)){
01736       //only restore main windows! (Matthias);
01737       if (KMMainWin::classNameOfToplevel(n) == "KMMainWin")
01738         (new KMMainWin)->restore(n);
01739       n++;
01740     }
01741     return true; // we were restored by SM
01742   }
01743   return false;  // no, we were not restored
01744 }
01745 
01746 void KMKernel::closeAllKMailWindows()
01747 {
01748   if (!KMainWindow::memberList) return;
01749   QPtrListIterator<KMainWindow> it(*KMainWindow::memberList);
01750   KMainWindow *window = 0;
01751   while ((window = it.current()) != 0) {
01752     ++it;
01753     if (window->isA("KMMainWindow") ||
01754     window->inherits("KMail::SecondaryWindow"))
01755       window->close( true ); // close and delete the window
01756   }
01757 }
01758 
01759 void KMKernel::cleanup(void)
01760 {
01761   dumpDeadLetters();
01762   the_shuttingDown = true;
01763   closeAllKMailWindows();
01764 
01765   delete the_acctMgr;
01766   the_acctMgr = 0;
01767   delete the_filterMgr;
01768   the_filterMgr = 0;
01769   delete the_msgSender;
01770   the_msgSender = 0;
01771   delete the_filterActionDict;
01772   the_filterActionDict = 0;
01773   delete the_undoStack;
01774   the_undoStack = 0;
01775   delete the_popFilterMgr;
01776   the_popFilterMgr = 0;
01777 
01778 #if 0
01779   delete the_weaver;
01780   the_weaver = 0;
01781 #endif
01782 
01783   KConfig* config =  KMKernel::config();
01784   KConfigGroupSaver saver(config, "General");
01785 
01786   if (the_trashFolder) {
01787 
01788     the_trashFolder->close("kmkernel", true);
01789 
01790     if (config->readBoolEntry("empty-trash-on-exit", true))
01791     {
01792       if ( the_trashFolder->count( true ) > 0 )
01793         the_trashFolder->expunge();
01794     }
01795   }
01796 
01797   mICalIface->cleanup();
01798 
01799   QValueList<QGuardedPtr<KMFolder> > folders;
01800   QStringList strList;
01801   KMFolder *folder;
01802   the_folderMgr->createFolderList(&strList, &folders);
01803   for (int i = 0; folders.at(i) != folders.end(); i++)
01804   {
01805     folder = *folders.at(i);
01806     if (!folder || folder->isDir()) continue;
01807     folder->close("kmkernel", true);
01808   }
01809   strList.clear();
01810   folders.clear();
01811   the_searchFolderMgr->createFolderList(&strList, &folders);
01812   for (int i = 0; folders.at(i) != folders.end(); i++)
01813   {
01814     folder = *folders.at(i);
01815     if (!folder || folder->isDir()) continue;
01816     folder->close("kmkernel", true);
01817   }
01818 
01819   delete the_msgIndex;
01820   the_msgIndex = 0;
01821   delete the_folderMgr;
01822   the_folderMgr = 0;
01823   delete the_imapFolderMgr;
01824   the_imapFolderMgr = 0;
01825   delete the_dimapFolderMgr;
01826   the_dimapFolderMgr = 0;
01827   delete the_searchFolderMgr;
01828   the_searchFolderMgr = 0;
01829   delete mConfigureDialog;
01830   mConfigureDialog = 0;
01831   // do not delete, because mWin may point to an existing window
01832   // delete mWin;
01833   mWin = 0;
01834 
01835   if ( RecentAddresses::exists() )
01836     RecentAddresses::self( config )->save( config );
01837   config->sync();
01838 }
01839 
01840 bool KMKernel::transferMail( QString & destinationDir )
01841 {
01842   QString dir;
01843 
01844   // check whether the user has a ~/KMail folder
01845   QFileInfo fi( QDir::home(), "KMail" );
01846   if ( fi.exists() && fi.isDir() ) {
01847     dir = QDir::homeDirPath() + "/KMail";
01848     // the following two lines can be removed once moving mail is reactivated
01849     destinationDir = dir;
01850     return true;
01851   }
01852 
01853   if ( dir.isEmpty() ) {
01854     // check whether the user has a ~/Mail folder
01855     fi.setFile( QDir::home(), "Mail" );
01856     if ( fi.exists() && fi.isDir() &&
01857          QFile::exists( QDir::homeDirPath() + "/Mail/.inbox.index" ) ) {
01858       // there's a ~/Mail folder which seems to be used by KMail (because of the
01859       // index file)
01860       dir = QDir::homeDirPath() + "/Mail";
01861       // the following two lines can be removed once moving mail is reactivated
01862       destinationDir = dir;
01863       return true;
01864     }
01865   }
01866 
01867   if ( dir.isEmpty() ) {
01868     return true; // there's no old mail folder
01869   }
01870 
01871 #if 0
01872   // disabled for now since moving fails in certain cases (e.g. if symbolic links are involved)
01873   const QString kmailName = kapp->aboutData()->programName();
01874   QString msg;
01875   if ( KIO::NetAccess::exists( destinationDir, true, 0 ) ) {
01876     // if destinationDir exists, we need to warn about possible
01877     // overwriting of files. otherwise, we don't have to
01878     msg = i18n( "%1-%3 is the application name, %4-%7 are folder path",
01879                 "<qt>The <i>%4</i> folder exists. "
01880                 "%1 now uses the <i>%5</i> folder for "
01881                 "its messages.<p>"
01882                 "%2 can move the contents of <i>%6<i> into this folder for "
01883                 "you, though this may replace any existing files with "
01884                 "the same name in <i>%7</i>.<p>"
01885                 "<strong>Would you like %3 to move the mail "
01886                 "files now?</strong></qt>" )
01887           .arg( kmailName, kmailName, kmailName )
01888           .arg( dir, destinationDir, dir, destinationDir );
01889   } else {
01890     msg = i18n( "%1-%3 is the application name, %4-%6 are folder path",
01891                 "<qt>The <i>%4</i> folder exists. "
01892                 "%1 now uses the <i>%5</i> folder for "
01893                 "its messages. %2 can move the contents of <i>%6</i> into "
01894                 "this folder for you.<p>"
01895                 "<strong>Would you like %3 to move the mail "
01896                 "files now?</strong></qt>" )
01897           .arg( kmailName, kmailName, kmailName )
01898           .arg( dir, destinationDir, dir );
01899   }
01900   QString title = i18n( "Migrate Mail Files?" );
01901   QString buttonText = i18n( "Move" );
01902 
01903   if ( KMessageBox::questionYesNo( 0, msg, title, buttonText, i18n("Do Not Move") ) ==
01904        KMessageBox::No ) {
01905     destinationDir = dir;
01906     return true;
01907   }
01908 
01909   if ( !KIO::NetAccess::move( dir, destinationDir ) ) {
01910     kdDebug(5006) << k_funcinfo << "Moving " << dir << " to " << destinationDir << " failed: " << KIO::NetAccess::lastErrorString() << endl;
01911     kdDebug(5006) << k_funcinfo << "Deleting " << destinationDir << endl;
01912     KIO::NetAccess::del( destinationDir, 0 );
01913     destinationDir = dir;
01914     return false;
01915   }
01916 #endif
01917 
01918   return true;
01919 }
01920 
01921 
01922 void KMKernel::ungrabPtrKb(void)
01923 {
01924   if(!KMainWindow::memberList) return;
01925   QWidget* widg = KMainWindow::memberList->first();
01926   Display* dpy;
01927 
01928   if (!widg) return;
01929   dpy = widg->x11Display();
01930   XUngrabKeyboard(dpy, CurrentTime);
01931   XUngrabPointer(dpy, CurrentTime);
01932 }
01933 
01934 
01935 // Message handler
01936 void KMKernel::kmailMsgHandler(QtMsgType aType, const char* aMsg)
01937 {
01938   static int recurse=-1;
01939 
01940   recurse++;
01941 
01942   switch (aType)
01943   {
01944   case QtDebugMsg:
01945   case QtWarningMsg:
01946     kdDebug(5006) << aMsg << endl;
01947     break;
01948 
01949   case QtFatalMsg: // Hm, what about using kdFatal() here?
01950     ungrabPtrKb();
01951     kdDebug(5006) << kapp->caption() << " fatal error "
01952           << aMsg << endl;
01953     KMessageBox::error(0, aMsg);
01954     abort();
01955   }
01956 
01957   recurse--;
01958 }
01959 
01960 
01961 void KMKernel::dumpDeadLetters()
01962 {
01963   if ( shuttingDown() )
01964     return; //All documents should be saved before shutting down is set!
01965 
01966   // make all composer windows autosave their contents
01967   if ( !KMainWindow::memberList )
01968     return;
01969 
01970   for ( QPtrListIterator<KMainWindow> it(*KMainWindow::memberList) ; it.current() != 0; ++it ) {
01971     if ( KMail::Composer * win = ::qt_cast<KMail::Composer*>( it.current() ) ) {
01972       win->autoSaveMessage();
01973       // saving the message has to be finished right here, we are called from a dtor,
01974       // therefore we have no chance to finish this later
01975       // yes, this is ugly and potentially dangerous, but the alternative is losing
01976       // currently composed messages...
01977       while ( win->isComposing() )
01978         qApp->processEvents();
01979     }
01980   }
01981 }
01982 
01983 
01984 
01985 void KMKernel::action(bool mailto, bool check, const QString &to,
01986                       const QString &cc, const QString &bcc,
01987                       const QString &subj, const QString &body,
01988                       const KURL &messageFile,
01989                       const KURL::List &attachURLs,
01990                       const QCStringList &customHeaders)
01991 {
01992   if ( mailto )
01993     openComposer( to, cc, bcc, subj, body, 0, messageFile, attachURLs, customHeaders );
01994   else
01995     openReader( check );
01996 
01997   if ( check )
01998     checkMail();
01999   //Anything else?
02000 }
02001 
02002 void KMKernel::byteArrayToRemoteFile(const QByteArray &aData, const KURL &aURL,
02003   bool overwrite)
02004 {
02005   // ## when KDE 3.3 is out: use KIO::storedPut to remove slotDataReq altogether
02006   KIO::Job *job = KIO::put(aURL, -1, overwrite, false);
02007   putData pd; pd.url = aURL; pd.data = aData; pd.offset = 0;
02008   mPutJobs.insert(job, pd);
02009   connect(job, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
02010     SLOT(slotDataReq(KIO::Job*,QByteArray&)));
02011   connect(job, SIGNAL(result(KIO::Job*)),
02012     SLOT(slotResult(KIO::Job*)));
02013 }
02014 
02015 void KMKernel::slotDataReq(KIO::Job *job, QByteArray &data)
02016 {
02017   // send the data in 64 KB chunks
02018   const int MAX_CHUNK_SIZE = 64*1024;
02019   QMap<KIO::Job*, putData>::Iterator it = mPutJobs.find(job);
02020   assert(it != mPutJobs.end());
02021   int remainingBytes = (*it).data.size() - (*it).offset;
02022   if( remainingBytes > MAX_CHUNK_SIZE )
02023   {
02024     // send MAX_CHUNK_SIZE bytes to the receiver (deep copy)
02025     data.duplicate( (*it).data.data() + (*it).offset, MAX_CHUNK_SIZE );
02026     (*it).offset += MAX_CHUNK_SIZE;
02027     //kdDebug( 5006 ) << "Sending " << MAX_CHUNK_SIZE << " bytes ("
02028     //                << remainingBytes - MAX_CHUNK_SIZE << " bytes remain)\n";
02029   }
02030   else
02031   {
02032     // send the remaining bytes to the receiver (deep copy)
02033     data.duplicate( (*it).data.data() + (*it).offset, remainingBytes );
02034     (*it).data = QByteArray();
02035     (*it).offset = 0;
02036     //kdDebug( 5006 ) << "Sending " << remainingBytes << " bytes\n";
02037   }
02038 }
02039 
02040 void KMKernel::slotResult(KIO::Job *job)
02041 {
02042   QMap<KIO::Job*, putData>::Iterator it = mPutJobs.find(job);
02043   assert(it != mPutJobs.end());
02044   if (job->error())
02045   {
02046     if (job->error() == KIO::ERR_FILE_ALREADY_EXIST)
02047     {
02048       if (KMessageBox::warningContinueCancel(0,
02049         i18n("File %1 exists.\nDo you want to replace it?")
02050         .arg((*it).url.prettyURL()), i18n("Save to File"), i18n("&Replace"))
02051         == KMessageBox::Continue)
02052         byteArrayToRemoteFile((*it).data, (*it).url, true);
02053     }
02054     else job->showErrorDialog();
02055   }
02056   mPutJobs.remove(it);
02057 }
02058 
02059 void KMKernel::slotRequestConfigSync() {
02060   // ### FIXME: delay as promised in the kdoc of this function ;-)
02061   KMKernel::config()->sync();
02062 }
02063 
02064 void KMKernel::slotShowConfigurationDialog()
02065 {
02066   if( !mConfigureDialog ) {
02067     mConfigureDialog = new ConfigureDialog( 0, "configure", false );
02068     connect( mConfigureDialog, SIGNAL( configChanged() ),
02069              this, SLOT( slotConfigChanged() ) );
02070   }
02071 
02072   if( KMKernel::getKMMainWidget() == 0 )
02073   {
02074     // ensure that there is a main widget available
02075     // as parts of the configure dialog (identity) rely on this
02076     // and this slot can be called when there is only a KMComposeWin showing
02077     KMMainWin * win = new KMMainWin;
02078     win->show();
02079   }
02080   if( mConfigureDialog->isHidden() )
02081   {
02082     getKMMainWidget()->headers()->writeConfig();
02083     mConfigureDialog->show();
02084   }
02085   else
02086     mConfigureDialog->raise();
02087 }
02088 
02089 void KMKernel::slotConfigChanged()
02090 {
02091   readConfig();
02092   emit configChanged();
02093 }
02094 
02095 //-------------------------------------------------------------------------------
02096 //static
02097 QString KMKernel::localDataPath()
02098 {
02099   return locateLocal( "data", "kmail/" );
02100 }
02101 
02102 //-------------------------------------------------------------------------------
02103 
02104 bool KMKernel::haveSystemTrayApplet()
02105 {
02106   return !systemTrayApplets.isEmpty();
02107 }
02108 
02109 bool KMKernel::registerSystemTrayApplet( const KSystemTray* applet )
02110 {
02111   if ( systemTrayApplets.findIndex( applet ) == -1 ) {
02112     systemTrayApplets.append( applet );
02113     return true;
02114   }
02115   else
02116     return false;
02117 }
02118 
02119 bool KMKernel::unregisterSystemTrayApplet( const KSystemTray* applet )
02120 {
02121   QValueList<const KSystemTray*>::iterator it =
02122     systemTrayApplets.find( applet );
02123   if ( it != systemTrayApplets.end() ) {
02124     systemTrayApplets.remove( it );
02125     return true;
02126   }
02127   else
02128     return false;
02129 }
02130 
02131 void KMKernel::emergencyExit( const QString& reason )
02132 {
02133   QString mesg;
02134   if ( reason.length() == 0 ) {
02135     mesg = i18n("KMail encountered a fatal error and will terminate now");
02136   } else {
02137     mesg = i18n("KMail encountered a fatal error and will "
02138                       "terminate now.\nThe error was:\n%1").arg( reason );
02139   }
02140 
02141   kdWarning() << mesg << endl;
02142   KNotifyClient::userEvent( 0, "<qt>"+mesg+"</qt>", KNotifyClient::Messagebox, KNotifyClient::Error );
02143 
02144   ::exit(1);
02145 }
02146 
02150 bool KMKernel::folderIsDraftOrOutbox(const KMFolder * folder)
02151 {
02152   assert( folder );
02153   if ( folder == the_outboxFolder )
02154     return true;
02155   return folderIsDrafts( folder );
02156 }
02157 
02158 bool KMKernel::folderIsDrafts(const KMFolder * folder)
02159 {
02160   assert( folder );
02161   if ( folder == the_draftsFolder )
02162     return true;
02163 
02164   QString idString = folder->idString();
02165   if ( idString.isEmpty() )
02166     return false;
02167 
02168   // search the identities if the folder matches the drafts-folder
02169   const KPIM::IdentityManager *im = identityManager();
02170   for ( KPIM::IdentityManager::ConstIterator it=im->begin(); it != im->end(); ++it )
02171     if ( (*it).drafts() == idString )
02172       return true;
02173   return false;
02174 }
02175 
02176 bool KMKernel::folderIsTemplates( const KMFolder *folder )
02177 {
02178   assert( folder );
02179   if ( folder == the_templatesFolder )
02180     return true;
02181 
02182   QString idString = folder->idString();
02183   if ( idString.isEmpty() )
02184     return false;
02185 
02186   // search the identities if the folder matches the templates-folder
02187   const KPIM::IdentityManager *im = identityManager();
02188   for ( KPIM::IdentityManager::ConstIterator it=im->begin(); it != im->end(); ++it )
02189     if ( (*it).templates() == idString )
02190       return true;
02191   return false;
02192 }
02193 
02194 bool KMKernel::folderIsTrash(KMFolder * folder)
02195 {
02196   assert(folder);
02197   if (folder == the_trashFolder) return true;
02198   QStringList actList = acctMgr()->getAccounts();
02199   QStringList::Iterator it( actList.begin() );
02200   for( ; it != actList.end() ; ++it ) {
02201     KMAccount* act = acctMgr()->findByName( *it );
02202     if ( act && ( act->trash() == folder->idString() ) )
02203       return true;
02204   }
02205   return false;
02206 }
02207 
02208 bool KMKernel::folderIsSentMailFolder( const KMFolder * folder )
02209 {
02210   assert( folder );
02211   if ( folder == the_sentFolder )
02212     return true;
02213 
02214   QString idString = folder->idString();
02215   if ( idString.isEmpty() ) return false;
02216 
02217   // search the identities if the folder matches the sent-folder
02218   const KPIM::IdentityManager * im = identityManager();
02219   for( KPIM::IdentityManager::ConstIterator it = im->begin(); it != im->end(); ++it )
02220     if ( (*it).fcc() == idString ) return true;
02221   return false;
02222 }
02223 
02224 KPIM::IdentityManager * KMKernel::identityManager() {
02225   if ( !mIdentityManager ) {
02226     kdDebug(5006) << "instantating KPIM::IdentityManager" << endl;
02227     mIdentityManager = new KPIM::IdentityManager( false, this, "mIdentityManager" );
02228   }
02229   return mIdentityManager;
02230 }
02231 
02232 KMMsgIndex *KMKernel::msgIndex()
02233 {
02234     return the_msgIndex;
02235 }
02236 
02237 KMainWindow* KMKernel::mainWin()
02238 {
02239   if (KMainWindow::memberList) {
02240     KMainWindow *kmWin = 0;
02241 
02242     // First look for a KMMainWin.
02243     for (kmWin = KMainWindow::memberList->first(); kmWin;
02244          kmWin = KMainWindow::memberList->next())
02245       if (kmWin->isA("KMMainWin"))
02246         return kmWin;
02247 
02248     // There is no KMMainWin. Use any other KMainWindow instead (e.g. in
02249     // case we are running inside Kontact) because we anyway only need
02250     // it for modal message boxes and for KNotify events.
02251     kmWin = KMainWindow::memberList->first();
02252     if ( kmWin )
02253       return kmWin;
02254   }
02255 
02256   // There's not a single KMainWindow. Create a KMMainWin.
02257   // This could happen if we want to pop up an error message
02258   // while we are still doing the startup wizard and no other
02259   // KMainWindow is running.
02260   mWin = new KMMainWin;
02261   return mWin;
02262 }
02263 
02264 
02268 void KMKernel::slotEmptyTrash()
02269 {
02270   QString title = i18n("Empty Trash");
02271   QString text = i18n("Are you sure you want to empty the trash folders of all accounts?");
02272   if (KMessageBox::warningContinueCancel(0, text, title,
02273                                          KStdGuiItem::cont(), "confirm_empty_trash")
02274       != KMessageBox::Continue)
02275   {
02276     return;
02277   }
02278 
02279   for (KMAccount* acct = acctMgr()->first(); acct; acct = acctMgr()->next())
02280   {
02281     KMFolder* trash = findFolderById(acct->trash());
02282     if (trash)
02283     {
02284       trash->expunge();
02285     }
02286   }
02287 }
02288 
02289 KConfig* KMKernel::config()
02290 {
02291   assert(mySelf);
02292   if (!mySelf->mConfig)
02293   {
02294     mySelf->mConfig = KSharedConfig::openConfig( "kmailrc" );
02295     // Check that all updates have been run on the config file:
02296     KMail::checkConfigUpdates();
02297   }
02298   return mySelf->mConfig;
02299 }
02300 
02301 KMailICalIfaceImpl& KMKernel::iCalIface()
02302 {
02303   assert( mICalIface );
02304   return *mICalIface;
02305 }
02306 
02307 void KMKernel::selectFolder( QString folderPath )
02308 {
02309   kdDebug(5006)<<"Selecting a folder "<<folderPath<<endl;
02310   const QString localPrefix = "/Local";
02311   KMFolder *folder = kmkernel->folderMgr()->getFolderByURL( folderPath );
02312   if ( !folder && folderPath.startsWith( localPrefix ) )
02313     folder = the_folderMgr->getFolderByURL( folderPath.mid( localPrefix.length() ) );
02314   if ( !folder )
02315     folder = kmkernel->imapFolderMgr()->getFolderByURL( folderPath );
02316   if ( !folder )
02317     folder = kmkernel->dimapFolderMgr()->getFolderByURL( folderPath );
02318   Q_ASSERT( folder );
02319 
02320   KMMainWidget *widget = getKMMainWidget();
02321   Q_ASSERT( widget );
02322   if ( !widget )
02323     return;
02324 
02325   KMFolderTree *tree = widget->folderTree();
02326   tree->doFolderSelected( tree->indexOfFolder( folder ) );
02327   tree->ensureItemVisible( tree->indexOfFolder( folder ) );
02328 }
02329 
02330 KMMainWidget *KMKernel::getKMMainWidget()
02331 {
02332   //This could definitely use a speadup
02333   QWidgetList *l = kapp->topLevelWidgets();
02334   QWidgetListIt it( *l );
02335   QWidget *wid;
02336 
02337   while ( ( wid = it.current() ) != 0 ) {
02338     ++it;
02339     QObjectList *l2 = wid->topLevelWidget()->queryList( "KMMainWidget" );
02340     if (l2 && l2->first()) {
02341       KMMainWidget* kmmw = dynamic_cast<KMMainWidget *>( l2->first() );
02342       Q_ASSERT( kmmw );
02343       delete l2;
02344       delete l;
02345       return kmmw;
02346     }
02347     delete l2;
02348   }
02349   delete l;
02350   return 0;
02351 }
02352 
02353 void KMKernel::slotRunBackgroundTasks() // called regularly by timer
02354 {
02355   // Hidden KConfig keys. Not meant to be used, but a nice fallback in case
02356   // a stable kmail release goes out with a nasty bug in CompactionJob...
02357   KConfigGroup generalGroup( config(), "General" );
02358 
02359   if ( generalGroup.readBoolEntry( "auto-expiring", true ) ) {
02360     the_folderMgr->expireAllFolders( false /*scheduled, not immediate*/ );
02361     the_imapFolderMgr->expireAllFolders( false /*scheduled, not immediate*/ );
02362     the_dimapFolderMgr->expireAllFolders( false /*scheduled, not immediate*/ );
02363     // the_searchFolderMgr: no expiry there
02364   }
02365 
02366   if ( generalGroup.readBoolEntry( "auto-compaction", true ) ) {
02367     the_folderMgr->compactAllFolders( false /*scheduled, not immediate*/ );
02368     // the_imapFolderMgr: no compaction
02369     the_dimapFolderMgr->compactAllFolders( false /*scheduled, not immediate*/ );
02370     // the_searchFolderMgr: no compaction
02371   }
02372 
02373 #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h
02374   mBackgroundTasksTimer->start( 60 * 1000, true ); // check again in 1 minute
02375 #else
02376   mBackgroundTasksTimer->start( 4 * 60 * 60 * 1000, true ); // check again in 4 hours
02377 #endif
02378 
02379 }
02380 
02381 void KMKernel::expireAllFoldersNow() // called by the GUI
02382 {
02383   the_folderMgr->expireAllFolders( true /*immediate*/ );
02384   the_imapFolderMgr->expireAllFolders( true /*immediate*/ );
02385   the_dimapFolderMgr->expireAllFolders( true /*immediate*/ );
02386 }
02387 
02388 void KMKernel::compactAllFolders() // called by the GUI
02389 {
02390   the_folderMgr->compactAllFolders( true /*immediate*/ );
02391   //the_imapFolderMgr->compactAllFolders( true /*immediate*/ );
02392   the_dimapFolderMgr->compactAllFolders( true /*immediate*/ );
02393 }
02394 
02395 KMFolder* KMKernel::findFolderById( const QString& idString )
02396 {
02397   KMFolder * folder = the_folderMgr->findIdString( idString );
02398   if ( !folder )
02399     folder = the_imapFolderMgr->findIdString( idString );
02400   if ( !folder )
02401     folder = the_dimapFolderMgr->findIdString( idString );
02402   if ( !folder )
02403     folder = the_searchFolderMgr->findIdString( idString );
02404   return folder;
02405 }
02406 
02407 ::KIMProxy* KMKernel::imProxy()
02408 {
02409   return KIMProxy::instance( kapp->dcopClient() );
02410 }
02411 
02412 void KMKernel::enableMailCheck()
02413 {
02414   mMailCheckAborted = false;
02415 }
02416 
02417 bool KMKernel::mailCheckAborted() const
02418 {
02419   return mMailCheckAborted;
02420 }
02421 
02422 void KMKernel::abortMailCheck()
02423 {
02424   mMailCheckAborted = true;
02425 }
02426 
02427 bool KMKernel::canQueryClose()
02428 {
02429   if ( KMMainWidget::mainWidgetList() &&
02430        KMMainWidget::mainWidgetList()->count() > 1 )
02431     return true;
02432   KMMainWidget *widget = getKMMainWidget();
02433   if ( !widget )
02434     return true;
02435   KMSystemTray* systray = widget->systray();
02436   if ( !systray || GlobalSettings::closeDespiteSystemTray() )
02437       return true;
02438   if ( systray->mode() == GlobalSettings::EnumSystemTrayPolicy::ShowAlways ) {
02439     systray->hideKMail();
02440     return false;
02441   } else if ( ( systray->mode() == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ) && ( systray->hasUnreadMail() )) {
02442     systray->show();
02443     systray->hideKMail();
02444     return false;
02445   }
02446   return true;
02447 }
02448 
02449 void KMKernel::messageCountChanged()
02450 {
02451   mTimeOfLastMessageCountChange = ::time( 0 );
02452 }
02453 
02454 int KMKernel::timeOfLastMessageCountChange() const
02455 {
02456   return mTimeOfLastMessageCountChange;
02457 }
02458 
02459 Wallet *KMKernel::wallet() {
02460   static bool walletOpenFailed = false;
02461   if ( mWallet && mWallet->isOpen() )
02462     return mWallet;
02463 
02464   if ( !Wallet::isEnabled() || walletOpenFailed )
02465     return 0;
02466 
02467   // find an appropriate parent window for the wallet dialog
02468   WId window = 0;
02469   if ( qApp->activeWindow() )
02470     window = qApp->activeWindow()->winId();
02471   else if ( getKMMainWidget() )
02472     window = getKMMainWidget()->topLevelWidget()->winId();
02473 
02474   delete mWallet;
02475   mWallet = Wallet::openWallet( Wallet::NetworkWallet(), window );
02476 
02477   if ( !mWallet ) {
02478     walletOpenFailed = true;
02479     return 0;
02480   }
02481 
02482   if ( !mWallet->hasFolder( "kmail" ) )
02483     mWallet->createFolder( "kmail" );
02484   mWallet->setFolder( "kmail" );
02485   return mWallet;
02486 }
02487 
02488 QValueList< QGuardedPtr<KMFolder> > KMKernel::allFolders()
02489 {
02490   QStringList names;
02491   QValueList<QGuardedPtr<KMFolder> > folders;
02492   folderMgr()->createFolderList(&names, &folders);
02493   imapFolderMgr()->createFolderList(&names, &folders);
02494   dimapFolderMgr()->createFolderList(&names, &folders);
02495   searchFolderMgr()->createFolderList(&names, &folders);
02496 
02497   return folders;
02498 }
02499 
02500 KMFolder *KMKernel::currentFolder() {
02501   KMMainWidget *widget = getKMMainWidget();
02502   KMFolder *folder = 0;
02503   if ( widget && widget->folderTree() ) {
02504     folder = widget->folderTree()->currentFolder();
02505   }
02506   return folder;
02507 }
02508 
02509 // can't be inline, since KMSender isn't known to implement
02510 // KMail::MessageSender outside this .cpp file
02511 KMail::MessageSender * KMKernel::msgSender() { return the_msgSender; }
02512 
02513 #include "kmkernel.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys