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 
01529   the_searchFolderMgr = new KMFolderMgr(locateLocal("data","kmail/search"), KMSearchDir);
01530   KMFolder *lsf = the_searchFolderMgr->find( i18n("Last Search") );
01531   if (lsf)
01532     the_searchFolderMgr->remove( lsf );
01533 
01534   the_acctMgr       = new AccountManager();
01535   the_filterMgr     = new KMFilterMgr();
01536   the_popFilterMgr     = new KMFilterMgr(true);
01537   the_filterActionDict = new KMFilterActionDict;
01538 
01539   initFolders(cfg);
01540   the_acctMgr->readConfig();
01541   the_filterMgr->readConfig();
01542   the_popFilterMgr->readConfig();
01543   cleanupImapFolders();
01544 
01545   the_msgSender = new KMSender;
01546   the_server_is_ready = true;
01547   imProxy()->initialize();
01548   { // area for config group "Composer"
01549     KConfigGroupSaver saver(cfg, "Composer");
01550     if (cfg->readListEntry("pref-charsets").isEmpty())
01551     {
01552       cfg->writeEntry("pref-charsets", "us-ascii,iso-8859-1,locale,utf-8");
01553     }
01554   }
01555   readConfig();
01556   mICalIface->readConfig();
01557   // filterMgr->dump();
01558 #ifdef HAVE_INDEXLIB
01559   the_msgIndex = new KMMsgIndex(this); //create the indexer
01560 #else
01561   the_msgIndex = 0;
01562 #endif
01563 
01564 //#if 0
01565   the_weaver =  new KPIM::ThreadWeaver::Weaver( this );
01566   the_weaverLogger = new KPIM::ThreadWeaver::WeaverThreadLogger(this);
01567   the_weaverLogger->attach (the_weaver);
01568 //#endif
01569 
01570   connect( the_folderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01571            this, SIGNAL( folderRemoved(KMFolder*) ) );
01572   connect( the_dimapFolderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01573            this, SIGNAL( folderRemoved(KMFolder*) ) );
01574   connect( the_imapFolderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01575            this, SIGNAL( folderRemoved(KMFolder*) ) );
01576   connect( the_searchFolderMgr, SIGNAL( folderRemoved(KMFolder*) ),
01577            this, SIGNAL( folderRemoved(KMFolder*) ) );
01578 
01579   mBackgroundTasksTimer = new QTimer( this, "mBackgroundTasksTimer" );
01580   connect( mBackgroundTasksTimer, SIGNAL( timeout() ), this, SLOT( slotRunBackgroundTasks() ) );
01581 #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h
01582   mBackgroundTasksTimer->start( 10000, true ); // 10s, singleshot
01583 #else
01584   mBackgroundTasksTimer->start( 5 * 60000, true ); // 5 minutes, singleshot
01585 #endif
01586 
01587   QTextCodec *codec;
01588   for ( int i = 0; ( codec = QTextCodec::codecForIndex ( i ) ); i++ ) {
01589     const QString asciiString( "azAZ19,.-#+!?=()&" );
01590     const QCString encodedString = codec->fromUnicode( asciiString );
01591     if ( QString::fromAscii( encodedString ) != asciiString ) {
01592       mNonAsciiCompatibleCodecs.append( codec );
01593     }
01594   }
01595 }
01596 
01597 bool KMKernel::isCodecAsciiCompatible( const QTextCodec *codec )
01598 {
01599   return !mNonAsciiCompatibleCodecs.contains( codec );
01600 }
01601 
01602 void KMKernel::readConfig()
01603 {
01604   //Needed here, since this function is also called when the configuration
01605   //changes, and the static variables should be updated then - IOF
01606   KMMessage::readConfig();
01607 }
01608 
01609 void KMKernel::cleanupImapFolders()
01610 {
01611   KMAccount *acct = 0;
01612   KMFolderNode *node = the_imapFolderMgr->dir().first();
01613   while (node)
01614   {
01615     if (node->isDir() || ((acct = the_acctMgr->find(node->id()))
01616               && ( acct->type() == "imap" )) )
01617     {
01618       node = the_imapFolderMgr->dir().next();
01619     } else {
01620       KMFolder* folder = static_cast<KMFolder*>(node);
01621       // delete only local
01622       static_cast<KMFolderImap*>( folder->storage() )->setAlreadyRemoved( true );
01623       the_imapFolderMgr->remove(folder);
01624       node = the_imapFolderMgr->dir().first();
01625     }
01626   }
01627 
01628   node = the_dimapFolderMgr->dir().first();
01629   while (node)
01630   {
01631     if (node->isDir() || ((acct = the_acctMgr->find(node->id()))
01632               && ( acct->type() == "cachedimap" )) )
01633     {
01634       node = the_dimapFolderMgr->dir().next();
01635     } else {
01636       the_dimapFolderMgr->remove(static_cast<KMFolder*>(node));
01637       node = the_dimapFolderMgr->dir().first();
01638     }
01639   }
01640 
01641   the_imapFolderMgr->quiet(true);
01642   for (acct = the_acctMgr->first(); acct; acct = the_acctMgr->next())
01643   {
01644     KMFolderImap *fld;
01645     KMAcctImap *imapAcct;
01646 
01647     if (acct->type() != "imap") continue;
01648     fld = static_cast<KMFolderImap*>(the_imapFolderMgr
01649       ->findOrCreate(QString::number(acct->id()), false, acct->id())->storage());
01650     fld->setNoContent(true);
01651     fld->folder()->setLabel(acct->name());
01652     imapAcct = static_cast<KMAcctImap*>(acct);
01653     fld->setAccount(imapAcct);
01654     imapAcct->setImapFolder(fld);
01655     fld->close( "kernel", true );
01656   }
01657   the_imapFolderMgr->quiet(false);
01658 
01659   the_dimapFolderMgr->quiet( true );
01660   for (acct = the_acctMgr->first(); acct; acct = the_acctMgr->next())
01661   {
01662     KMFolderCachedImap *cfld = 0;
01663     KMAcctCachedImap *cachedImapAcct;
01664 
01665     if (acct->type() != "cachedimap" ) continue;
01666 
01667     KMFolder* fld = the_dimapFolderMgr->find(QString::number(acct->id()));
01668     if( fld )
01669       cfld = static_cast<KMFolderCachedImap*>( fld->storage() );
01670     if (cfld == 0) {
01671       // Folder doesn't exist yet
01672       cfld = static_cast<KMFolderCachedImap*>(the_dimapFolderMgr->createFolder(QString::number(acct->id()),
01673             false, KMFolderTypeCachedImap)->storage());
01674       if (!cfld) {
01675         KMessageBox::error(0,(i18n("Cannot create file `%1' in %2.\nKMail cannot start without it.").arg(acct->name()).arg(the_dimapFolderMgr->basePath())));
01676         exit(-1);
01677       }
01678       cfld->folder()->setId( acct->id() );
01679     }
01680 
01681     cfld->setNoContent(true);
01682     cfld->folder()->setLabel(acct->name());
01683     cachedImapAcct = static_cast<KMAcctCachedImap*>(acct);
01684     cfld->setAccount(cachedImapAcct);
01685     cachedImapAcct->setImapFolder(cfld);
01686     cfld->close("kmkernel");
01687   }
01688   the_dimapFolderMgr->quiet( false );
01689 }
01690 
01691 bool KMKernel::doSessionManagement()
01692 {
01693 
01694   // Do session management
01695   if (kapp->isRestored()){
01696     int n = 1;
01697     while (KMMainWin::canBeRestored(n)){
01698       //only restore main windows! (Matthias);
01699       if (KMMainWin::classNameOfToplevel(n) == "KMMainWin")
01700         (new KMMainWin)->restore(n);
01701       n++;
01702     }
01703     return true; // we were restored by SM
01704   }
01705   return false;  // no, we were not restored
01706 }
01707 
01708 void KMKernel::closeAllKMailWindows()
01709 {
01710   if (!KMainWindow::memberList) return;
01711   QPtrListIterator<KMainWindow> it(*KMainWindow::memberList);
01712   KMainWindow *window = 0;
01713   while ((window = it.current()) != 0) {
01714     ++it;
01715     if (window->isA("KMMainWindow") ||
01716     window->inherits("KMail::SecondaryWindow"))
01717       window->close( true ); // close and delete the window
01718   }
01719 }
01720 
01721 void KMKernel::cleanup(void)
01722 {
01723   dumpDeadLetters();
01724   the_shuttingDown = true;
01725   closeAllKMailWindows();
01726 
01727   delete the_acctMgr;
01728   the_acctMgr = 0;
01729   delete the_filterMgr;
01730   the_filterMgr = 0;
01731   delete the_msgSender;
01732   the_msgSender = 0;
01733   delete the_filterActionDict;
01734   the_filterActionDict = 0;
01735   delete the_undoStack;
01736   the_undoStack = 0;
01737   delete the_popFilterMgr;
01738   the_popFilterMgr = 0;
01739 
01740 #if 0
01741   delete the_weaver;
01742   the_weaver = 0;
01743 #endif
01744 
01745   KConfig* config =  KMKernel::config();
01746   KConfigGroupSaver saver(config, "General");
01747 
01748   if (the_trashFolder) {
01749 
01750     the_trashFolder->close("kmkernel", true);
01751 
01752     if (config->readBoolEntry("empty-trash-on-exit", true))
01753     {
01754       if ( the_trashFolder->count( true ) > 0 )
01755         the_trashFolder->expunge();
01756     }
01757   }
01758 
01759   mICalIface->cleanup();
01760 
01761   QValueList<QGuardedPtr<KMFolder> > folders;
01762   QStringList strList;
01763   KMFolder *folder;
01764   the_folderMgr->createFolderList(&strList, &folders);
01765   for (int i = 0; folders.at(i) != folders.end(); i++)
01766   {
01767     folder = *folders.at(i);
01768     if (!folder || folder->isDir()) continue;
01769     folder->close("kmkernel", true);
01770   }
01771   strList.clear();
01772   folders.clear();
01773   the_searchFolderMgr->createFolderList(&strList, &folders);
01774   for (int i = 0; folders.at(i) != folders.end(); i++)
01775   {
01776     folder = *folders.at(i);
01777     if (!folder || folder->isDir()) continue;
01778     folder->close("kmkernel", true);
01779   }
01780 
01781   delete the_msgIndex;
01782   the_msgIndex = 0;
01783   delete the_folderMgr;
01784   the_folderMgr = 0;
01785   delete the_imapFolderMgr;
01786   the_imapFolderMgr = 0;
01787   delete the_dimapFolderMgr;
01788   the_dimapFolderMgr = 0;
01789   delete the_searchFolderMgr;
01790   the_searchFolderMgr = 0;
01791   delete mConfigureDialog;
01792   mConfigureDialog = 0;
01793   // do not delete, because mWin may point to an existing window
01794   // delete mWin;
01795   mWin = 0;
01796 
01797   if ( RecentAddresses::exists() )
01798     RecentAddresses::self( config )->save( config );
01799   config->sync();
01800 }
01801 
01802 bool KMKernel::transferMail( QString & destinationDir )
01803 {
01804   QString dir;
01805 
01806   // check whether the user has a ~/KMail folder
01807   QFileInfo fi( QDir::home(), "KMail" );
01808   if ( fi.exists() && fi.isDir() ) {
01809     dir = QDir::homeDirPath() + "/KMail";
01810     // the following two lines can be removed once moving mail is reactivated
01811     destinationDir = dir;
01812     return true;
01813   }
01814 
01815   if ( dir.isEmpty() ) {
01816     // check whether the user has a ~/Mail folder
01817     fi.setFile( QDir::home(), "Mail" );
01818     if ( fi.exists() && fi.isDir() &&
01819          QFile::exists( QDir::homeDirPath() + "/Mail/.inbox.index" ) ) {
01820       // there's a ~/Mail folder which seems to be used by KMail (because of the
01821       // index file)
01822       dir = QDir::homeDirPath() + "/Mail";
01823       // the following two lines can be removed once moving mail is reactivated
01824       destinationDir = dir;
01825       return true;
01826     }
01827   }
01828 
01829   if ( dir.isEmpty() ) {
01830     return true; // there's no old mail folder
01831   }
01832 
01833 #if 0
01834   // disabled for now since moving fails in certain cases (e.g. if symbolic links are involved)
01835   const QString kmailName = kapp->aboutData()->programName();
01836   QString msg;
01837   if ( KIO::NetAccess::exists( destinationDir, true, 0 ) ) {
01838     // if destinationDir exists, we need to warn about possible
01839     // overwriting of files. otherwise, we don't have to
01840     msg = i18n( "%1-%3 is the application name, %4-%7 are folder path",
01841                 "<qt>The <i>%4</i> folder exists. "
01842                 "%1 now uses the <i>%5</i> folder for "
01843                 "its messages.<p>"
01844                 "%2 can move the contents of <i>%6<i> into this folder for "
01845                 "you, though this may replace any existing files with "
01846                 "the same name in <i>%7</i>.<p>"
01847                 "<strong>Would you like %3 to move the mail "
01848                 "files now?</strong></qt>" )
01849           .arg( kmailName, kmailName, kmailName )
01850           .arg( dir, destinationDir, dir, destinationDir );
01851   } else {
01852     msg = i18n( "%1-%3 is the application name, %4-%6 are folder path",
01853                 "<qt>The <i>%4</i> folder exists. "
01854                 "%1 now uses the <i>%5</i> folder for "
01855                 "its messages. %2 can move the contents of <i>%6</i> into "
01856                 "this folder for you.<p>"
01857                 "<strong>Would you like %3 to move the mail "
01858                 "files now?</strong></qt>" )
01859           .arg( kmailName, kmailName, kmailName )
01860           .arg( dir, destinationDir, dir );
01861   }
01862   QString title = i18n( "Migrate Mail Files?" );
01863   QString buttonText = i18n( "Move" );
01864 
01865   if ( KMessageBox::questionYesNo( 0, msg, title, buttonText, i18n("Do Not Move") ) ==
01866        KMessageBox::No ) {
01867     destinationDir = dir;
01868     return true;
01869   }
01870 
01871   if ( !KIO::NetAccess::move( dir, destinationDir ) ) {
01872     kdDebug(5006) << k_funcinfo << "Moving " << dir << " to " << destinationDir << " failed: " << KIO::NetAccess::lastErrorString() << endl;
01873     kdDebug(5006) << k_funcinfo << "Deleting " << destinationDir << endl;
01874     KIO::NetAccess::del( destinationDir, 0 );
01875     destinationDir = dir;
01876     return false;
01877   }
01878 #endif
01879 
01880   return true;
01881 }
01882 
01883 
01884 void KMKernel::ungrabPtrKb(void)
01885 {
01886   if(!KMainWindow::memberList) return;
01887   QWidget* widg = KMainWindow::memberList->first();
01888   Display* dpy;
01889 
01890   if (!widg) return;
01891   dpy = widg->x11Display();
01892   XUngrabKeyboard(dpy, CurrentTime);
01893   XUngrabPointer(dpy, CurrentTime);
01894 }
01895 
01896 
01897 // Message handler
01898 void KMKernel::kmailMsgHandler(QtMsgType aType, const char* aMsg)
01899 {
01900   static int recurse=-1;
01901 
01902   recurse++;
01903 
01904   switch (aType)
01905   {
01906   case QtDebugMsg:
01907   case QtWarningMsg:
01908     kdDebug(5006) << aMsg << endl;
01909     break;
01910 
01911   case QtFatalMsg: // Hm, what about using kdFatal() here?
01912     ungrabPtrKb();
01913     kdDebug(5006) << kapp->caption() << " fatal error "
01914           << aMsg << endl;
01915     KMessageBox::error(0, aMsg);
01916     abort();
01917   }
01918 
01919   recurse--;
01920 }
01921 
01922 
01923 void KMKernel::dumpDeadLetters()
01924 {
01925   if ( shuttingDown() )
01926     return; //All documents should be saved before shutting down is set!
01927 
01928   // make all composer windows autosave their contents
01929   if ( !KMainWindow::memberList )
01930     return;
01931 
01932   for ( QPtrListIterator<KMainWindow> it(*KMainWindow::memberList) ; it.current() != 0; ++it ) {
01933     if ( KMail::Composer * win = ::qt_cast<KMail::Composer*>( it.current() ) ) {
01934       win->autoSaveMessage();
01935       // saving the message has to be finished right here, we are called from a dtor,
01936       // therefore we have no chance to finish this later
01937       // yes, this is ugly and potentially dangerous, but the alternative is losing
01938       // currently composed messages...
01939       while ( win->isComposing() )
01940         qApp->processEvents();
01941     }
01942   }
01943 }
01944 
01945 
01946 
01947 void KMKernel::action(bool mailto, bool check, const QString &to,
01948                       const QString &cc, const QString &bcc,
01949                       const QString &subj, const QString &body,
01950                       const KURL &messageFile,
01951                       const KURL::List &attachURLs,
01952                       const QCStringList &customHeaders)
01953 {
01954   if ( mailto )
01955     openComposer( to, cc, bcc, subj, body, 0, messageFile, attachURLs, customHeaders );
01956   else
01957     openReader( check );
01958 
01959   if ( check )
01960     checkMail();
01961   //Anything else?
01962 }
01963 
01964 void KMKernel::byteArrayToRemoteFile(const QByteArray &aData, const KURL &aURL,
01965   bool overwrite)
01966 {
01967   // ## when KDE 3.3 is out: use KIO::storedPut to remove slotDataReq altogether
01968   KIO::Job *job = KIO::put(aURL, -1, overwrite, false);
01969   putData pd; pd.url = aURL; pd.data = aData; pd.offset = 0;
01970   mPutJobs.insert(job, pd);
01971   connect(job, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
01972     SLOT(slotDataReq(KIO::Job*,QByteArray&)));
01973   connect(job, SIGNAL(result(KIO::Job*)),
01974     SLOT(slotResult(KIO::Job*)));
01975 }
01976 
01977 void KMKernel::slotDataReq(KIO::Job *job, QByteArray &data)
01978 {
01979   // send the data in 64 KB chunks
01980   const int MAX_CHUNK_SIZE = 64*1024;
01981   QMap<KIO::Job*, putData>::Iterator it = mPutJobs.find(job);
01982   assert(it != mPutJobs.end());
01983   int remainingBytes = (*it).data.size() - (*it).offset;
01984   if( remainingBytes > MAX_CHUNK_SIZE )
01985   {
01986     // send MAX_CHUNK_SIZE bytes to the receiver (deep copy)
01987     data.duplicate( (*it).data.data() + (*it).offset, MAX_CHUNK_SIZE );
01988     (*it).offset += MAX_CHUNK_SIZE;
01989     //kdDebug( 5006 ) << "Sending " << MAX_CHUNK_SIZE << " bytes ("
01990     //                << remainingBytes - MAX_CHUNK_SIZE << " bytes remain)\n";
01991   }
01992   else
01993   {
01994     // send the remaining bytes to the receiver (deep copy)
01995     data.duplicate( (*it).data.data() + (*it).offset, remainingBytes );
01996     (*it).data = QByteArray();
01997     (*it).offset = 0;
01998     //kdDebug( 5006 ) << "Sending " << remainingBytes << " bytes\n";
01999   }
02000 }
02001 
02002 void KMKernel::slotResult(KIO::Job *job)
02003 {
02004   QMap<KIO::Job*, putData>::Iterator it = mPutJobs.find(job);
02005   assert(it != mPutJobs.end());
02006   if (job->error())
02007   {
02008     if (job->error() == KIO::ERR_FILE_ALREADY_EXIST)
02009     {
02010       if (KMessageBox::warningContinueCancel(0,
02011         i18n("File %1 exists.\nDo you want to replace it?")
02012         .arg((*it).url.prettyURL()), i18n("Save to File"), i18n("&Replace"))
02013         == KMessageBox::Continue)
02014         byteArrayToRemoteFile((*it).data, (*it).url, true);
02015     }
02016     else job->showErrorDialog();
02017   }
02018   mPutJobs.remove(it);
02019 }
02020 
02021 void KMKernel::slotRequestConfigSync() {
02022   // ### FIXME: delay as promised in the kdoc of this function ;-)
02023   KMKernel::config()->sync();
02024 }
02025 
02026 void KMKernel::slotShowConfigurationDialog()
02027 {
02028   if( !mConfigureDialog ) {
02029     mConfigureDialog = new ConfigureDialog( 0, "configure", false );
02030     connect( mConfigureDialog, SIGNAL( configChanged() ),
02031              this, SLOT( slotConfigChanged() ) );
02032   }
02033 
02034   if( KMKernel::getKMMainWidget() == 0 )
02035   {
02036     // ensure that there is a main widget available
02037     // as parts of the configure dialog (identity) rely on this
02038     // and this slot can be called when there is only a KMComposeWin showing
02039     KMMainWin * win = new KMMainWin;
02040     win->show();
02041   }
02042   if( mConfigureDialog->isHidden() )
02043   {
02044     getKMMainWidget()->headers()->writeConfig();
02045     mConfigureDialog->show();
02046   }
02047   else
02048     mConfigureDialog->raise();
02049 }
02050 
02051 void KMKernel::slotConfigChanged()
02052 {
02053   readConfig();
02054   emit configChanged();
02055 }
02056 
02057 //-------------------------------------------------------------------------------
02058 //static
02059 QString KMKernel::localDataPath()
02060 {
02061   return locateLocal( "data", "kmail/" );
02062 }
02063 
02064 //-------------------------------------------------------------------------------
02065 
02066 bool KMKernel::haveSystemTrayApplet()
02067 {
02068   return !systemTrayApplets.isEmpty();
02069 }
02070 
02071 bool KMKernel::registerSystemTrayApplet( const KSystemTray* applet )
02072 {
02073   if ( systemTrayApplets.findIndex( applet ) == -1 ) {
02074     systemTrayApplets.append( applet );
02075     return true;
02076   }
02077   else
02078     return false;
02079 }
02080 
02081 bool KMKernel::unregisterSystemTrayApplet( const KSystemTray* applet )
02082 {
02083   QValueList<const KSystemTray*>::iterator it =
02084     systemTrayApplets.find( applet );
02085   if ( it != systemTrayApplets.end() ) {
02086     systemTrayApplets.remove( it );
02087     return true;
02088   }
02089   else
02090     return false;
02091 }
02092 
02093 void KMKernel::emergencyExit( const QString& reason )
02094 {
02095   QString mesg;
02096   if ( reason.length() == 0 ) {
02097     mesg = i18n("KMail encountered a fatal error and will terminate now");
02098   } else {
02099     mesg = i18n("KMail encountered a fatal error and will "
02100                       "terminate now.\nThe error was:\n%1").arg( reason );
02101   }
02102 
02103   kdWarning() << mesg << endl;
02104   KNotifyClient::userEvent( 0, "<qt>"+mesg+"</qt>", KNotifyClient::Messagebox, KNotifyClient::Error );
02105 
02106   ::exit(1);
02107 }
02108 
02112 bool KMKernel::folderIsDraftOrOutbox(const KMFolder * folder)
02113 {
02114   assert( folder );
02115   if ( folder == the_outboxFolder )
02116     return true;
02117   return folderIsDrafts( folder );
02118 }
02119 
02120 bool KMKernel::folderIsDrafts(const KMFolder * folder)
02121 {
02122   assert( folder );
02123   if ( folder == the_draftsFolder )
02124     return true;
02125 
02126   QString idString = folder->idString();
02127   if ( idString.isEmpty() )
02128     return false;
02129 
02130   // search the identities if the folder matches the drafts-folder
02131   const KPIM::IdentityManager *im = identityManager();
02132   for ( KPIM::IdentityManager::ConstIterator it=im->begin(); it != im->end(); ++it )
02133     if ( (*it).drafts() == idString )
02134       return true;
02135   return false;
02136 }
02137 
02138 bool KMKernel::folderIsTemplates( const KMFolder *folder )
02139 {
02140   assert( folder );
02141   if ( folder == the_templatesFolder )
02142     return true;
02143 
02144   QString idString = folder->idString();
02145   if ( idString.isEmpty() )
02146     return false;
02147 
02148   // search the identities if the folder matches the templates-folder
02149   const KPIM::IdentityManager *im = identityManager();
02150   for ( KPIM::IdentityManager::ConstIterator it=im->begin(); it != im->end(); ++it )
02151     if ( (*it).templates() == idString )
02152       return true;
02153   return false;
02154 }
02155 
02156 bool KMKernel::folderIsTrash(KMFolder * folder)
02157 {
02158   assert(folder);
02159   if (folder == the_trashFolder) return true;
02160   QStringList actList = acctMgr()->getAccounts();
02161   QStringList::Iterator it( actList.begin() );
02162   for( ; it != actList.end() ; ++it ) {
02163     KMAccount* act = acctMgr()->findByName( *it );
02164     if ( act && ( act->trash() == folder->idString() ) )
02165       return true;
02166   }
02167   return false;
02168 }
02169 
02170 bool KMKernel::folderIsSentMailFolder( const KMFolder * folder )
02171 {
02172   assert( folder );
02173   if ( folder == the_sentFolder )
02174     return true;
02175 
02176   QString idString = folder->idString();
02177   if ( idString.isEmpty() ) return false;
02178 
02179   // search the identities if the folder matches the sent-folder
02180   const KPIM::IdentityManager * im = identityManager();
02181   for( KPIM::IdentityManager::ConstIterator it = im->begin(); it != im->end(); ++it )
02182     if ( (*it).fcc() == idString ) return true;
02183   return false;
02184 }
02185 
02186 KPIM::IdentityManager * KMKernel::identityManager() {
02187   if ( !mIdentityManager ) {
02188     kdDebug(5006) << "instantating KPIM::IdentityManager" << endl;
02189     mIdentityManager = new KPIM::IdentityManager( false, this, "mIdentityManager" );
02190   }
02191   return mIdentityManager;
02192 }
02193 
02194 KMMsgIndex *KMKernel::msgIndex()
02195 {
02196     return the_msgIndex;
02197 }
02198 
02199 KMainWindow* KMKernel::mainWin()
02200 {
02201   if (KMainWindow::memberList) {
02202     KMainWindow *kmWin = 0;
02203 
02204     // First look for a KMMainWin.
02205     for (kmWin = KMainWindow::memberList->first(); kmWin;
02206          kmWin = KMainWindow::memberList->next())
02207       if (kmWin->isA("KMMainWin"))
02208         return kmWin;
02209 
02210     // There is no KMMainWin. Use any other KMainWindow instead (e.g. in
02211     // case we are running inside Kontact) because we anyway only need
02212     // it for modal message boxes and for KNotify events.
02213     kmWin = KMainWindow::memberList->first();
02214     if ( kmWin )
02215       return kmWin;
02216   }
02217 
02218   // There's not a single KMainWindow. Create a KMMainWin.
02219   // This could happen if we want to pop up an error message
02220   // while we are still doing the startup wizard and no other
02221   // KMainWindow is running.
02222   mWin = new KMMainWin;
02223   return mWin;
02224 }
02225 
02226 
02230 void KMKernel::slotEmptyTrash()
02231 {
02232   QString title = i18n("Empty Trash");
02233   QString text = i18n("Are you sure you want to empty the trash folders of all accounts?");
02234   if (KMessageBox::warningContinueCancel(0, text, title,
02235                                          KStdGuiItem::cont(), "confirm_empty_trash")
02236       != KMessageBox::Continue)
02237   {
02238     return;
02239   }
02240 
02241   for (KMAccount* acct = acctMgr()->first(); acct; acct = acctMgr()->next())
02242   {
02243     KMFolder* trash = findFolderById(acct->trash());
02244     if (trash)
02245     {
02246       trash->expunge();
02247     }
02248   }
02249 }
02250 
02251 KConfig* KMKernel::config()
02252 {
02253   assert(mySelf);
02254   if (!mySelf->mConfig)
02255   {
02256     mySelf->mConfig = KSharedConfig::openConfig( "kmailrc" );
02257     // Check that all updates have been run on the config file:
02258     KMail::checkConfigUpdates();
02259   }
02260   return mySelf->mConfig;
02261 }
02262 
02263 KMailICalIfaceImpl& KMKernel::iCalIface()
02264 {
02265   assert( mICalIface );
02266   return *mICalIface;
02267 }
02268 
02269 void KMKernel::selectFolder( QString folderPath )
02270 {
02271   kdDebug(5006)<<"Selecting a folder "<<folderPath<<endl;
02272   const QString localPrefix = "/Local";
02273   KMFolder *folder = kmkernel->folderMgr()->getFolderByURL( folderPath );
02274   if ( !folder && folderPath.startsWith( localPrefix ) )
02275     folder = the_folderMgr->getFolderByURL( folderPath.mid( localPrefix.length() ) );
02276   if ( !folder )
02277     folder = kmkernel->imapFolderMgr()->getFolderByURL( folderPath );
02278   if ( !folder )
02279     folder = kmkernel->dimapFolderMgr()->getFolderByURL( folderPath );
02280   Q_ASSERT( folder );
02281 
02282   KMMainWidget *widget = getKMMainWidget();
02283   Q_ASSERT( widget );
02284   if ( !widget )
02285     return;
02286 
02287   KMFolderTree *tree = widget->folderTree();
02288   tree->doFolderSelected( tree->indexOfFolder( folder ) );
02289   tree->ensureItemVisible( tree->indexOfFolder( folder ) );
02290 }
02291 
02292 KMMainWidget *KMKernel::getKMMainWidget()
02293 {
02294   //This could definitely use a speadup
02295   QWidgetList *l = kapp->topLevelWidgets();
02296   QWidgetListIt it( *l );
02297   QWidget *wid;
02298 
02299   while ( ( wid = it.current() ) != 0 ) {
02300     ++it;
02301     QObjectList *l2 = wid->topLevelWidget()->queryList( "KMMainWidget" );
02302     if (l2 && l2->first()) {
02303       KMMainWidget* kmmw = dynamic_cast<KMMainWidget *>( l2->first() );
02304       Q_ASSERT( kmmw );
02305       delete l2;
02306       delete l;
02307       return kmmw;
02308     }
02309     delete l2;
02310   }
02311   delete l;
02312   return 0;
02313 }
02314 
02315 void KMKernel::slotRunBackgroundTasks() // called regularly by timer
02316 {
02317   // Hidden KConfig keys. Not meant to be used, but a nice fallback in case
02318   // a stable kmail release goes out with a nasty bug in CompactionJob...
02319   KConfigGroup generalGroup( config(), "General" );
02320 
02321   if ( generalGroup.readBoolEntry( "auto-expiring", true ) ) {
02322     the_folderMgr->expireAllFolders( false /*scheduled, not immediate*/ );
02323     the_imapFolderMgr->expireAllFolders( false /*scheduled, not immediate*/ );
02324     the_dimapFolderMgr->expireAllFolders( false /*scheduled, not immediate*/ );
02325     // the_searchFolderMgr: no expiry there
02326   }
02327 
02328   if ( generalGroup.readBoolEntry( "auto-compaction", true ) ) {
02329     the_folderMgr->compactAllFolders( false /*scheduled, not immediate*/ );
02330     // the_imapFolderMgr: no compaction
02331     the_dimapFolderMgr->compactAllFolders( false /*scheduled, not immediate*/ );
02332     // the_searchFolderMgr: no compaction
02333   }
02334 
02335 #ifdef DEBUG_SCHEDULER // for debugging, see jobscheduler.h
02336   mBackgroundTasksTimer->start( 60 * 1000, true ); // check again in 1 minute
02337 #else
02338   mBackgroundTasksTimer->start( 4 * 60 * 60 * 1000, true ); // check again in 4 hours
02339 #endif
02340 
02341 }
02342 
02343 void KMKernel::expireAllFoldersNow() // called by the GUI
02344 {
02345   the_folderMgr->expireAllFolders( true /*immediate*/ );
02346   the_imapFolderMgr->expireAllFolders( true /*immediate*/ );
02347   the_dimapFolderMgr->expireAllFolders( true /*immediate*/ );
02348 }
02349 
02350 void KMKernel::compactAllFolders() // called by the GUI
02351 {
02352   the_folderMgr->compactAllFolders( true /*immediate*/ );
02353   //the_imapFolderMgr->compactAllFolders( true /*immediate*/ );
02354   the_dimapFolderMgr->compactAllFolders( true /*immediate*/ );
02355 }
02356 
02357 KMFolder* KMKernel::findFolderById( const QString& idString )
02358 {
02359   KMFolder * folder = the_folderMgr->findIdString( idString );
02360   if ( !folder )
02361     folder = the_imapFolderMgr->findIdString( idString );
02362   if ( !folder )
02363     folder = the_dimapFolderMgr->findIdString( idString );
02364   if ( !folder )
02365     folder = the_searchFolderMgr->findIdString( idString );
02366   return folder;
02367 }
02368 
02369 ::KIMProxy* KMKernel::imProxy()
02370 {
02371   return KIMProxy::instance( kapp->dcopClient() );
02372 }
02373 
02374 void KMKernel::enableMailCheck()
02375 {
02376   mMailCheckAborted = false;
02377 }
02378 
02379 bool KMKernel::mailCheckAborted() const
02380 {
02381   return mMailCheckAborted;
02382 }
02383 
02384 void KMKernel::abortMailCheck()
02385 {
02386   mMailCheckAborted = true;
02387 }
02388 
02389 bool KMKernel::canQueryClose()
02390 {
02391   if ( KMMainWidget::mainWidgetList() &&
02392        KMMainWidget::mainWidgetList()->count() > 1 )
02393     return true;
02394   KMMainWidget *widget = getKMMainWidget();
02395   if ( !widget )
02396     return true;
02397   KMSystemTray* systray = widget->systray();
02398   if ( !systray || GlobalSettings::closeDespiteSystemTray() )
02399       return true;
02400   if ( systray->mode() == GlobalSettings::EnumSystemTrayPolicy::ShowAlways ) {
02401     systray->hideKMail();
02402     return false;
02403   } else if ( ( systray->mode() == GlobalSettings::EnumSystemTrayPolicy::ShowOnUnread ) && ( systray->hasUnreadMail() )) {
02404     systray->show();
02405     systray->hideKMail();
02406     return false;
02407   }
02408   return true;
02409 }
02410 
02411 void KMKernel::messageCountChanged()
02412 {
02413   mTimeOfLastMessageCountChange = ::time( 0 );
02414 }
02415 
02416 int KMKernel::timeOfLastMessageCountChange() const
02417 {
02418   return mTimeOfLastMessageCountChange;
02419 }
02420 
02421 Wallet *KMKernel::wallet() {
02422   static bool walletOpenFailed = false;
02423   if ( mWallet && mWallet->isOpen() )
02424     return mWallet;
02425 
02426   if ( !Wallet::isEnabled() || walletOpenFailed )
02427     return 0;
02428 
02429   // find an appropriate parent window for the wallet dialog
02430   WId window = 0;
02431   if ( qApp->activeWindow() )
02432     window = qApp->activeWindow()->winId();
02433   else if ( getKMMainWidget() )
02434     window = getKMMainWidget()->topLevelWidget()->winId();
02435 
02436   delete mWallet;
02437   mWallet = Wallet::openWallet( Wallet::NetworkWallet(), window );
02438 
02439   if ( !mWallet ) {
02440     walletOpenFailed = true;
02441     return 0;
02442   }
02443 
02444   if ( !mWallet->hasFolder( "kmail" ) )
02445     mWallet->createFolder( "kmail" );
02446   mWallet->setFolder( "kmail" );
02447   return mWallet;
02448 }
02449 
02450 QValueList< QGuardedPtr<KMFolder> > KMKernel::allFolders()
02451 {
02452   QStringList names;
02453   QValueList<QGuardedPtr<KMFolder> > folders;
02454   folderMgr()->createFolderList(&names, &folders);
02455   imapFolderMgr()->createFolderList(&names, &folders);
02456   dimapFolderMgr()->createFolderList(&names, &folders);
02457   searchFolderMgr()->createFolderList(&names, &folders);
02458 
02459   return folders;
02460 }
02461 
02462 KMFolder *KMKernel::currentFolder() {
02463   KMMainWidget *widget = getKMMainWidget();
02464   KMFolder *folder = 0;
02465   if ( widget && widget->folderTree() ) {
02466     folder = widget->folderTree()->currentFolder();
02467   }
02468   return folder;
02469 }
02470 
02471 // can't be inline, since KMSender isn't known to implement
02472 // KMail::MessageSender outside this .cpp file
02473 KMail::MessageSender * KMKernel::msgSender() { return the_msgSender; }
02474 
02475 #include "kmkernel.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys