kpilot

pilotDaemon.cc

Go to the documentation of this file.
00001 /* KPilot
00002 **
00003 ** Copyright (C) 1998-2001 by Dan Pilone
00004 ** Copyright (C) 2001-2004 by Adriaan de Groot
00005 ** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00006 **
00007 ** This is the KPilot Daemon, which does the actual communication with
00008 ** the Pilot and with the conduits.
00009 */
00010 
00011 /*
00012 ** This program is free software; you can redistribute it and/or modify
00013 ** it under the terms of the GNU General Public License as published by
00014 ** the Free Software Foundation; either version 2 of the License, or
00015 ** (at your option) any later version.
00016 **
00017 ** This program is distributed in the hope that it will be useful,
00018 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00020 ** GNU General Public License for more details.
00021 **
00022 ** You should have received a copy of the GNU General Public License
00023 ** along with this program in a file called COPYING; if not, write to
00024 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00025 ** MA 02110-1301, USA.
00026 */
00027 
00028 /*
00029 ** Bug reports and questions can be sent to kde-pim@kde.org
00030 */
00031 
00032 #include "options.h"
00033 
00034 #include <stdlib.h>
00035 
00036 #include <qtimer.h>
00037 #include <qtooltip.h>
00038 #include <qpixmap.h>
00039 
00040 #include <kuniqueapplication.h>
00041 #include <kaboutapplication.h>
00042 #include <kcmdlineargs.h>
00043 #include <kwin.h>
00044 #include <kurl.h>
00045 #include <kpopupmenu.h>
00046 #include <kiconloader.h>
00047 #include <kdebug.h>
00048 #include <kprocess.h>
00049 #include <dcopclient.h>
00050 #include <kurldrag.h>
00051 #include <kservice.h>
00052 #include <kapplication.h>
00053 #include <khelpmenu.h>
00054 
00055 #include "pilotRecord.h"
00056 
00057 #include "fileInstaller.h"
00058 #include "pilotUser.h"
00059 #include "pilotDatabase.h"
00060 #include "kpilotlink.h"
00061 #include "kpilotdevicelink.h"
00062 #include "actionQueue.h"
00063 #include "actions.h"
00064 
00065 #include "hotSync.h"
00066 #include "internalEditorAction.h"
00067 #include "logFile.h"
00068 
00069 #include "kpilotConfig.h"
00070 
00071 
00072 #include "kpilotDCOP_stub.h"
00073 #include "kpilotDCOP.h"
00074 #include "loggerDCOP_stub.h"
00075 
00076 #include "pilotDaemon.moc"
00077 
00078 static KAboutData *aboutData = 0L;
00079 
00080 PilotDaemonTray::PilotDaemonTray(PilotDaemon * p) :
00081     KSystemTray(0, "pilotDaemon"),
00082     fSyncTypeMenu(0L),
00083     daemon(p),
00084     kap(0L),
00085     fBlinkTimer(0L)
00086 {
00087     FUNCTIONSETUP;
00088     setupWidget();
00089     setAcceptDrops(true);
00090 }
00091 
00092 /* virtual */ void PilotDaemonTray::dragEnterEvent(QDragEnterEvent * e)
00093 {
00094     FUNCTIONSETUP;
00095     e->accept(KURLDrag::canDecode(e));
00096 }
00097 
00098 /* virtual */ void PilotDaemonTray::dropEvent(QDropEvent * e)
00099 {
00100     FUNCTIONSETUP;
00101 
00102     KURL::List list;
00103 
00104     KURLDrag::decode(e, list);
00105 
00106     QStringList files;
00107     for(KURL::List::ConstIterator it = list.begin(); it != list.end(); ++it)
00108     {
00109        if ((*it).isLocalFile())
00110           files << (*it).path();
00111     }
00112 
00113     daemon->addInstallFiles(files);
00114 }
00115 
00116 /* virtual */ void PilotDaemonTray::mousePressEvent(QMouseEvent * e)
00117 {
00118     FUNCTIONSETUP;
00119 
00120     switch (e->button())
00121     {
00122         case RightButton:
00123             {
00124                 KPopupMenu *menu = contextMenu();
00125                 contextMenuAboutToShow(menu);
00126                 menu->popup(e->globalPos());
00127             }
00128             break;
00129         case LeftButton:
00130             if (daemon) daemon->slotRunKPilot();
00131             break;
00132         default:
00133             KSystemTray::mousePressEvent(e);
00134     }
00135 }
00136 
00137 /* virtual */ void PilotDaemonTray::closeEvent(QCloseEvent *)
00138 {
00139     FUNCTIONSETUP;
00140     daemon->quitNow();
00141 }
00142 
00143 void PilotDaemonTray::setupWidget()
00144 {
00145     FUNCTIONSETUP;
00146 
00147     KGlobal::iconLoader()->addAppDir( CSL1("kpilot") );
00148     icons[Normal] = loadIcon( CSL1("kpilotDaemon") );
00149     icons[Busy] = loadIcon( CSL1("busysync") );
00150     icons[NotListening] = loadIcon( CSL1("nosync") );
00151 
00152     slotShowNotListening();
00153     QTimer::singleShot(2000,this,SLOT(slotShowNormal()));
00154 
00155     KPopupMenu *menu = contextMenu();
00156 
00157     menuKPilotItem = menu->insertItem(i18n("Start &KPilot"), daemon,
00158         SLOT(slotRunKPilot()));
00159     menuConfigureConduitsItem = menu->insertItem(i18n("&Configure KPilot..."),
00160         daemon, SLOT(slotRunConfig()));
00161     menu->insertSeparator();
00162 
00163     fSyncTypeMenu = new KPopupMenu(menu,"sync_type_menu");
00164     QString once = i18n("Appended to names of sync types to indicate the sync will happen just one time"," (once)");
00165 #define MI(a) fSyncTypeMenu->insertItem( \
00166         SyncAction::SyncMode::name(SyncAction::SyncMode::a) + once, \
00167         (int)(SyncAction::SyncMode::a));
00168     fSyncTypeMenu->insertItem(i18n("Default (%1)")
00169         .arg(SyncAction::SyncMode::name((SyncAction::SyncMode::Mode)KPilotSettings::syncType())),
00170         0);
00171     fSyncTypeMenu->insertSeparator();
00172 
00173         // Keep this synchronized with kpilotui.rc and kpilot.cc if at all possible.
00174     MI(eHotSync);
00175     MI(eFullSync);
00176     MI(eBackup);
00177     MI(eRestore);
00178     MI(eCopyHHToPC);
00179     MI(eCopyPCToHH);
00180 
00181     fSyncTypeMenu->setCheckable(true);
00182     fSyncTypeMenu->setItemChecked(0,true);
00183 #undef MI
00184     connect(fSyncTypeMenu,SIGNAL(activated(int)),daemon,SLOT(requestSync(int)));
00185     menu->insertItem(i18n("Next &Sync"),fSyncTypeMenu);
00186 
00187     KHelpMenu *help = new KHelpMenu(menu,aboutData);
00188     menu->insertItem(
00189         KGlobal::iconLoader()->loadIconSet(CSL1("help"),KIcon::Small,0,true),
00190         i18n("&Help"),help->menu(),false /* no whatsthis */);
00191 
00192 
00193 
00194 #ifdef DEBUG
00195     DEBUGKPILOT << fname << ": Finished getting icons" << endl;
00196 #endif
00197 }
00198 
00199 void PilotDaemonTray::slotShowAbout()
00200 {
00201     FUNCTIONSETUP;
00202 
00203     if (!kap)
00204     {
00205         kap = new KAboutApplication(0, "kpdab", false);
00206     }
00207 
00208     kap->show();
00209 }
00210 
00211 
00212 void PilotDaemonTray::enableRunKPilot(bool b)
00213 {
00214     FUNCTIONSETUP;
00215     contextMenu()->setItemEnabled(menuKPilotItem, b);
00216     contextMenu()->setItemEnabled(menuConfigureConduitsItem, b);
00217 }
00218 
00219 
00220 void PilotDaemonTray::changeIcon(IconShape i)
00221 {
00222     FUNCTIONSETUP;
00223     if (icons[i].isNull())
00224     {
00225         WARNINGKPILOT << "Icon #"<<i<<" is NULL!" << endl;
00226     }
00227     setPixmap(icons[i]);
00228     fCurrentIcon = i;
00229 }
00230 
00231 void PilotDaemonTray::slotShowNormal()
00232 {
00233     FUNCTIONSETUP;
00234     changeIcon(Normal);
00235 }
00236 
00237 void PilotDaemonTray::slotShowBusy()
00238 {
00239     FUNCTIONSETUP;
00240     changeIcon(Busy);
00241 }
00242 
00243 void PilotDaemonTray::slotShowNotListening()
00244 {
00245     FUNCTIONSETUP;
00246     changeIcon( NotListening );
00247 }
00248 
00249 void PilotDaemonTray::slotBusyTimer()
00250 {
00251     if (fCurrentIcon == Busy) changeIcon(Normal);
00252     else if (fCurrentIcon == Normal) changeIcon(Busy);
00253 }
00254 
00255 void PilotDaemonTray::startHotSync()
00256 {
00257     changeIcon(Busy);
00258     if (!fBlinkTimer)
00259     {
00260         fBlinkTimer = new QTimer(this,"blink timer");
00261     }
00262     if (fBlinkTimer)
00263     {
00264         connect(fBlinkTimer,SIGNAL(timeout()),
00265             this,SLOT(slotBusyTimer()));
00266         fBlinkTimer->start(750,false);
00267     }
00268 }
00269 
00270 void PilotDaemonTray::endHotSync()
00271 {
00272     changeIcon(Normal);
00273     if (fBlinkTimer)
00274     {
00275         fBlinkTimer->stop();
00276     }
00277 }
00278 
00279 
00280 PilotDaemon::PilotDaemon() :
00281     DCOPObject("KPilotDaemonIface"),
00282     fDaemonStatus(INIT),
00283     fPostSyncAction(None),
00284     fPilotLink(0L),
00285     fNextSyncType(SyncAction::SyncMode::eHotSync,true),
00286     fSyncStack(0L),
00287     fTray(0L),
00288     fInstaller(0L),
00289     fLogFile(0L),
00290     fLogStub(new LoggerDCOP_stub("kpilot", "LogIface")),
00291     fLogFileStub(new LoggerDCOP_stub("kpilotDaemon", "LogIface")),
00292     fKPilotStub(new KPilotDCOP_stub("kpilot", "KPilotIface")),
00293     fTempDevice(QString::null)
00294 {
00295     FUNCTIONSETUP;
00296 
00297     setupPilotLink();
00298     reloadSettings();
00299 
00300     if (fDaemonStatus == ERROR)
00301     {
00302         WARNINGKPILOT << "Connecting to device failed." << endl;
00303         return;
00304     }
00305 
00306     fInstaller = new FileInstaller;
00307     fLogFile = new LogFile;
00308     connect(fInstaller, SIGNAL(filesChanged()),
00309         this, SLOT(slotFilesChanged()));
00310 
00311     fNextSyncType.setMode( KPilotSettings::syncType() );
00312 
00313 #ifdef DEBUG
00314     DEBUGKPILOT << fname
00315         << ": The daemon is ready with status "
00316         << statusString() << " (" << (int) fDaemonStatus << ")" << endl;
00317 #endif
00318 }
00319 
00320 PilotDaemon::~PilotDaemon()
00321 {
00322     FUNCTIONSETUP;
00323 
00324     KPILOT_DELETE(fPilotLink);
00325     KPILOT_DELETE(fSyncStack);
00326     KPILOT_DELETE(fInstaller);
00327 
00328     (void) PilotDatabase::instanceCount();
00329 }
00330 
00331 void PilotDaemon::addInstallFiles(const QStringList &l)
00332 {
00333     FUNCTIONSETUP;
00334 
00335     fInstaller->addFiles( l, fTray );
00336 }
00337 
00338 int PilotDaemon::getPilotSpeed()
00339 {
00340     FUNCTIONSETUP;
00341 
00342     int speed = KPilotSettings::pilotSpeed();
00343 
00344     // Translate the speed entry in the
00345     // config file to something we can
00346     // put in the environment (for who?)
00347     //
00348     //
00349     const char *speedname = 0L;
00350 
00351     switch (speed)
00352     {
00353     case 0:
00354         speedname = "PILOTRATE=9600";
00355         break;
00356     case 1:
00357         speedname = "PILOTRATE=19200";
00358         break;
00359     case 2:
00360         speedname = "PILOTRATE=38400";
00361         break;
00362     case 3:
00363         speedname = "PILOTRATE=57600";
00364         break;
00365     case 4:
00366         speedname = "PILOTRATE=115200";
00367         break;
00368     default:
00369         speedname = "PILOTRATE=9600";
00370     }
00371 
00372 #ifdef DEBUG
00373     DEBUGKPILOT << fname
00374         << ": Speed set to "
00375         << speedname << " (" << speed << ")" << endl;
00376 #endif
00377 
00378     putenv((char *) speedname);
00379 
00380     return speed;
00381 }
00382 
00383 
00384 void PilotDaemon::showTray()
00385 {
00386     FUNCTIONSETUP;
00387 
00388     if (!fTray)
00389     {
00390 #ifdef DEBUG
00391         DEBUGKPILOT << fname << ": No tray icon to display!" << endl;
00392 #endif
00393 
00394         return;
00395     }
00396 
00397     // Copied from Klipper
00398     KWin::setSystemTrayWindowFor(fTray->winId(), 0);
00399     fTray->setGeometry(-100, -100, 42, 42);
00400     fTray->show();
00401 
00402 #ifdef DEBUG
00403     DEBUGKPILOT << fname << ": Tray icon displayed." << endl;
00404 #endif
00405 
00406     updateTrayStatus();
00407 }
00408 
00409 /* DCOP ASYNC */ void PilotDaemon::setTempDevice(QString d)
00410 {
00411     if ( !d.isEmpty() ){
00412         fTempDevice = d;
00413         if (fPilotLink)
00414             fPilotLink->setTempDevice( fTempDevice );
00415         reloadSettings();
00416     }
00417 }
00418 
00419 /* DCOP ASYNC */ void PilotDaemon::reloadSettings()
00420 {
00421     FUNCTIONSETUP;
00422 
00423     switch (fDaemonStatus)
00424     {
00425     case INIT:
00426     case HOTSYNC_END:
00427     case ERROR:
00428     case READY:
00429     case NOT_LISTENING:
00430         // It's OK to reload settings in these states.
00431         break;
00432     case HOTSYNC_START:
00433     case FILE_INSTALL_REQ:
00434         // Postpone the reload till the sync finishes.
00435         fPostSyncAction |= ReloadSettings;
00436         return;
00437         break;
00438     }
00439 
00440     // TODO: Is this bunch of calls really necessary to reload the settings???
00441     delete KPilotSettings::self();
00442     KPilotSettings::self()->config()->reparseConfiguration();
00443     KPilotSettings::self()->readConfig();
00444     getPilotSpeed();
00445 
00446     (void) Pilot::setupPilotCodec(KPilotSettings::encoding());
00447 
00448 #ifdef DEBUG
00449     DEBUGKPILOT << fname
00450         << ": Got configuration "
00451         << KPilotSettings::pilotDevice()
00452         << endl;
00453     DEBUGKPILOT << fname
00454         << ": Got conduit list "
00455         << (KPilotSettings::installedConduits().join(CSL1(",")))
00456         << endl;
00457 #endif
00458 
00459     requestSync(0);
00460 
00461 
00462     if (fPilotLink)
00463     {
00464 #ifdef DEBUG
00465         DEBUGKPILOT << fname
00466             << ": Resetting with device "
00467             << KPilotSettings::pilotDevice()
00468             << endl;
00469 #endif
00470 
00471         fPilotLink->reset( KPilotSettings::pilotDevice() );
00472 #ifdef DEBUG
00473         DEBUGKPILOT << fname
00474             << ": Using workarounds "
00475             << KPilotSettings::workarounds()
00476             << endl;
00477 #endif
00478         if ( KPilotSettings::workarounds() == KPilotSettings::eWorkaroundUSB )
00479         {
00480 #ifdef DEBUG
00481             DEBUGKPILOT << fname
00482                 << ": Using Zire31 USB workaround." << endl;
00483 #endif
00484             fPilotLink->setWorkarounds(true);
00485         }
00486     }
00487 
00488     if (KPilotSettings::dockDaemon())
00489     {
00490         if (!fTray)
00491         {
00492             fTray = new PilotDaemonTray(this);
00493             fTray->show();
00494         }
00495         else
00496         {
00497             fTray->show();
00498         }
00499     }
00500     else
00501     {
00502         if (fTray)
00503         {
00504             fTray->hide();
00505             delete fTray;
00506 
00507             fTray = 0L;
00508         }
00509     }
00510 
00511     updateTrayStatus();
00512     logProgress(QString::null,0);
00513 }
00514 
00515 /* DCOP */ void PilotDaemon::stopListening()
00516 {
00517     fIsListening=false;
00518     fTray->changeIcon(PilotDaemonTray::NotListening);
00519     fDaemonStatus=NOT_LISTENING;
00520     fPilotLink->close();
00521 }
00522 
00523 /* DCOP */ void PilotDaemon::startListening()
00524 {
00525     fIsListening=true;
00526     fTray->changeIcon(PilotDaemonTray::Normal);
00527     fDaemonStatus=INIT;
00528     fPilotLink->reset();
00529 }
00530 
00531 /* DCOP */ QString PilotDaemon::statusString()
00532 {
00533     FUNCTIONSETUP;
00534 
00535     QString s = CSL1("PilotDaemon=");
00536     s.append(shortStatusString());
00537 
00538     s.append(CSL1("; NextSync="));
00539     s.append(fNextSyncType.name());
00540 
00541     s.append(CSL1(" ("));
00542     if (fPilotLink)
00543     {
00544         s.append(fPilotLink->statusString());
00545     }
00546     s.append(CSL1(");"));
00547 
00548     return s;
00549 }
00550 
00551 /* DCOP */ QString PilotDaemon::shortStatusString()
00552 {
00553     QString s;
00554 
00555     switch (status())
00556     {
00557     case INIT:
00558         s.append(CSL1("Waiting for sync"));
00559         break;
00560     case READY:
00561         s.append(CSL1("Listening on device"));
00562         break;
00563     case ERROR:
00564         s=CSL1("Error");
00565         break;
00566     case FILE_INSTALL_REQ:
00567         s=CSL1("Installing File");
00568         break;
00569     case HOTSYNC_END:
00570         s=CSL1("End of Hotsync");
00571         break;
00572     case HOTSYNC_START:
00573         s=CSL1("Syncing");
00574         break;
00575     case NOT_LISTENING:
00576         s.append(CSL1("Not Listening (stopped manually)"));
00577         break;
00578     }
00579 
00580     return s;
00581 }
00582 
00583 
00584 
00585 bool PilotDaemon::setupPilotLink()
00586 {
00587     FUNCTIONSETUP;
00588 
00589     KPILOT_DELETE(fPilotLink);
00590     fPilotLink = new KPilotDeviceLink( 0, 0, fTempDevice );
00591     if (!fPilotLink)
00592     {
00593         WARNINGKPILOT << "Can't get pilot link." << endl;
00594         return false;
00595     }
00596 
00597     QObject::connect(fPilotLink, SIGNAL(deviceReady(KPilotLink*)),
00598         this, SLOT(startHotSync(KPilotLink*)));
00599     // connect the signals emitted by the pilotDeviceLink
00600     QObject::connect(fPilotLink, SIGNAL(logError(const QString &)),
00601         this, SLOT(logError(const QString &)));
00602     QObject::connect(fPilotLink, SIGNAL(logMessage(const QString &)),
00603         this, SLOT(logMessage(const QString &)));
00604     QObject::connect(fPilotLink,
00605         SIGNAL(logProgress(const QString &,int)),
00606         this, SLOT(logProgress(const QString &,int)));
00607 
00608 
00609     return true;
00610 }
00611 
00612 
00613 /* DCOP ASYNC */ void PilotDaemon::quitNow()
00614 {
00615     FUNCTIONSETUP;
00616     // Using switch to make sure we cover all the cases.
00617     //
00618     //
00619     switch (fDaemonStatus)
00620     {
00621     case INIT:
00622     case HOTSYNC_END:
00623     case ERROR:
00624     case NOT_LISTENING:
00625         getKPilot().daemonStatus(KPilotDCOP::DaemonQuit);
00626         kapp->quit();
00627         break;
00628     case READY:
00629     case HOTSYNC_START:
00630     case FILE_INSTALL_REQ:
00631         fPostSyncAction |= Quit;
00632         break;
00633     }
00634     emitDCOPSignal( "kpilotDaemonStatusChanged()", QByteArray() );
00635 }
00636 
00637 /* DCOP ASYNC */ void PilotDaemon::requestRegularSyncNext()
00638 {
00639     requestSync(SyncAction::SyncMode::eHotSync);
00640 }
00641 
00642 
00643 /* DCOP ASYNC */ void PilotDaemon::requestSync(int mode)
00644 {
00645     FUNCTIONSETUP;
00646 
00647     if ( 0==mode )
00648     {
00649         mode = KPilotSettings::syncType();
00650     }
00651 
00652     if ( !fNextSyncType.setMode(mode) )
00653     {
00654         WARNINGKPILOT << "Ignored fake sync type " << mode << endl;
00655         return;
00656     }
00657 
00658     updateTrayStatus();
00659 
00660     if (fTray && (fTray->fSyncTypeMenu))
00661     {
00662         for (int i=((int)SyncAction::SyncMode::eHotSync);
00663             i<=((int)SyncAction::SyncMode::eRestore) /* Restore */ ;
00664             ++i)
00665         {
00666             fTray->fSyncTypeMenu->setItemChecked(i,mode==i);
00667         }
00668     }
00669 
00670     getLogger().logMessage(i18n("Next HotSync will be: %1. ").arg(fNextSyncType.name()) +
00671         i18n("Please press the HotSync button."));
00672 }
00673 
00674 /* DCOP ASYNC */ void PilotDaemon::requestSyncType(QString s)
00675 {
00676     FUNCTIONSETUP;
00677 
00678     // This checks unique prefixes of the names of the various sync types.
00679     if (s.startsWith(CSL1("H"))) requestSync(SyncAction::SyncMode::eHotSync);
00680     else if (s.startsWith(CSL1("Fu"))) requestSync(SyncAction::SyncMode::eFullSync);
00681     else if (s.startsWith(CSL1("B"))) requestSync(SyncAction::SyncMode::eBackup);
00682     else if (s.startsWith(CSL1("R"))) requestSync(SyncAction::SyncMode::eRestore);
00683     else if (s.startsWith(CSL1("T"))) { fNextSyncType.setOptions(true,false); }
00684     else if (s.startsWith(CSL1("CopyHHToPC"))) requestSync(SyncAction::SyncMode::eCopyHHToPC);
00685     else if (s.startsWith(CSL1("CopyPCToHH"))) requestSync(SyncAction::SyncMode::eCopyPCToHH);
00686     else if (s.startsWith(CSL1("D"))) requestSync(0);
00687     else
00688     {
00689         WARNINGKPILOT << "Unknown sync type " << ( s.isEmpty() ? CSL1("<none>") : s )
00690             << endl;
00691     }
00692 }
00693 
00694 /* DCOP ASYNC */ void PilotDaemon::requestSyncOptions(bool test, bool local)
00695 {
00696     if ( !fNextSyncType.setOptions(test,local) )
00697     {
00698         WARNINGKPILOT << "Nonsensical request for "
00699             << (test ? "test" : "notest")
00700             << ' '
00701             << (local ? "local" : "nolocal")
00702             << " in mode "
00703             << fNextSyncType.name() << endl;
00704     }
00705 }
00706 
00707 /* DCOP */ int PilotDaemon::nextSyncType() const
00708 {
00709     return fNextSyncType.mode();
00710 }
00711 
00715 QDateTime PilotDaemon::lastSyncDate()
00716 {
00717     return KPilotSettings::lastSyncTime();
00718 }
00719 
00720 
00721 static QDict<QString> *conduitNameMap = 0L;
00722 
00723 static void fillConduitNameMap()
00724 {
00725     if ( !conduitNameMap )
00726     {
00727         conduitNameMap = new QDict<QString>;
00728         conduitNameMap->setAutoDelete(true);
00729     }
00730     conduitNameMap->clear();
00731 
00732     QStringList l = KPilotSettings::installedConduits();
00733     // Fill with internal settings.
00734     if ( l.find( CSL1("internal_fileinstall") ) != l.end() ) {
00735         conduitNameMap->insert( CSL1("internal_fileinstall"),
00736                                 new QString(i18n("File Installer")) );
00737     }
00738 
00739     QStringList::ConstIterator end = l.end();
00740     for (QStringList::ConstIterator i = l.begin(); i != end; ++i)
00741     {
00742         if (!conduitNameMap->find(*i))
00743         {
00744             QString readableName = CSL1("<unknown>");
00745             KSharedPtr < KService > o = KService::serviceByDesktopName(*i);
00746             if (!o)
00747             {
00748                 WARNINGKPILOT << "No service for " << *i << endl;
00749             }
00750             else
00751             {
00752                 readableName = o->name();
00753             }
00754             conduitNameMap->insert( *i, new QString(readableName) );
00755         }
00756     }
00757 }
00758 
00759 
00760 QStringList PilotDaemon::configuredConduitList()
00761 {
00762     fillConduitNameMap();
00763 
00764     QStringList keys;
00765 
00766     QDictIterator<QString> it(*conduitNameMap);
00767     for ( ; *it; ++it)
00768     {
00769         keys << it.currentKey();
00770     }
00771     keys.sort();
00772 
00773     QStringList::ConstIterator end = keys.end();
00774     QStringList result;
00775     for (QStringList::ConstIterator i = keys.begin(); i != end; ++i)
00776     {
00777         result << *(conduitNameMap->find(*i));
00778     }
00779 
00780     return result;
00781 }
00782 
00783 QString PilotDaemon::logFileName()
00784 {
00785     return KPilotSettings::logFileName();
00786 }
00787 
00788 QString PilotDaemon::userName()
00789 {
00790     return KPilotSettings::userName();
00791 }
00792 QString PilotDaemon::pilotDevice()
00793 {
00794     return KPilotSettings::pilotDevice();
00795 }
00796 
00797 bool PilotDaemon::killDaemonOnExit()
00798 {
00799     return KPilotSettings::killDaemonAtExit();
00800 }
00801 
00802 typedef enum { NotLocked=0, Locked=1, DCOPError=2 } KDesktopLockStatus;
00803 static KDesktopLockStatus isKDesktopLockRunning()
00804 {
00805     if (!KPilotSettings::screenlockSecure()) return NotLocked;
00806 
00807     DCOPClient *dcopptr = KApplication::kApplication()->dcopClient();
00808 
00809     // Can't tell, very weird, err on the side of safety.
00810     if (!dcopptr || !dcopptr->isAttached())
00811     {
00812         WARNINGKPILOT << "Could not make DCOP connection. "
00813             << "Assuming screensaver is active." << endl;
00814         return DCOPError;
00815     }
00816 
00817     QByteArray data,returnValue;
00818     QCString returnType;
00819 
00820     if (!dcopptr->call("kdesktop","KScreensaverIface","isBlanked()",
00821         data,returnType,returnValue,true))
00822     {
00823         WARNINGKPILOT << "Check for screensaver failed."
00824             << "Assuming screensaver is active." << endl;
00825         // Err on the side of safety again.
00826         return DCOPError;
00827     }
00828 
00829     if (returnType == "bool")
00830     {
00831         bool b;
00832         QDataStream reply(returnValue,IO_ReadOnly);
00833         reply >> b;
00834         return (b ? Locked : NotLocked);
00835     }
00836     else
00837     {
00838         WARNINGKPILOT << "Strange return value from screensaver. "
00839             << "Assuming screensaver is active." << endl;
00840         // Err on the side of safety.
00841         return DCOPError;
00842     }
00843 }
00844 
00845 
00846 static void informOthers(KPilotDCOP_stub &kpilot,
00847     LoggerDCOP_stub &log,
00848     LoggerDCOP_stub &filelog)
00849 {
00850     kpilot.daemonStatus(KPilotDCOP::StartOfHotSync);
00851     log.logStartSync();
00852     filelog.logStartSync();
00853 }
00854 
00855 static bool isSyncPossible(ActionQueue *fSyncStack,
00856     KPilotLink *pilotLink,
00857     KPilotDCOP_stub &kpilot)
00858 {
00859     FUNCTIONSETUP;
00860 
00867     int kpilotstatus = kpilot.kpilotStatus();
00868     DCOPStub::Status callstatus = kpilot.status();
00869 
00870 #ifdef DEBUG
00871     if (callstatus != DCOPStub::CallSucceeded)
00872     {
00873         DEBUGKPILOT << fname <<
00874             ": Could not call KPilot for status." << endl;
00875     }
00876     else
00877     {
00878         DEBUGKPILOT << fname << ": KPilot status " << kpilotstatus << endl;
00879     }
00880 #endif
00881 
00885     if ((callstatus == DCOPStub::CallSucceeded) &&
00886         (kpilotstatus != KPilotDCOP::WaitingForDaemon))
00887     {
00888         WARNINGKPILOT << "KPilot returned status " << kpilotstatus << endl;
00889 
00890         fSyncStack->queueInit();
00891         fSyncStack->addAction(new SorryAction(pilotLink));
00892         return false;
00893     }
00894 
00895     switch (isKDesktopLockRunning())
00896     {
00897     case NotLocked :
00898         break; /* Fall through to return true below */
00899     case Locked :
00900         fSyncStack->queueInit();
00901         fSyncStack->addAction(new SorryAction(pilotLink,
00902             i18n("HotSync is disabled while the screen is locked.")));
00903         return false;
00904     case DCOPError :
00905         fSyncStack->queueInit();
00906         fSyncStack->addAction(new SorryAction(pilotLink,
00907             i18n("HotSync is disabled because KPilot could not "
00908             "determine the state of the screen saver. You "
00909             "can disable this security feature by unchecking "
00910             "the 'do not sync when screensaver is active' box "
00911             "in the HotSync page of the configuration dialog.")));
00912         return false;
00913     }
00914 
00915     return true;
00916 }
00917 
00918 static void queueInstaller(ActionQueue *fSyncStack,
00919     KPilotLink *pilotLink,
00920     FileInstaller *fInstaller,
00921     const QStringList &c)
00922 {
00923     if (c.findIndex(CSL1("internal_fileinstall")) >= 0)
00924     {
00925         fSyncStack->addAction(new FileInstallAction(pilotLink,fInstaller->dir()));
00926     }
00927 }
00928 
00929 static void queueEditors(ActionQueue *fSyncStack, KPilotLink *pilotLink)
00930 {
00931     if (KPilotSettings::internalEditors())
00932     {
00933         fSyncStack->addAction(new InternalEditorAction(pilotLink));
00934     }
00935 }
00936 
00937 static void queueConduits(ActionQueue *fSyncStack,
00938     const QStringList &conduits,
00939     SyncAction::SyncMode e)
00940 {
00941     if (conduits.count() > 0)
00942     {
00943         fSyncStack->queueConduits(conduits,e);
00944         // QString s = i18n("Conduit flags: ");
00945         // s.append(ConduitProxy::flagsForMode(e).join(CSL1(" ")));
00946         // logMessage(s);
00947     }
00948 }
00949 
00950 bool PilotDaemon::shouldBackup()
00951 {
00952 
00953     FUNCTIONSETUP;
00954 
00955     bool ret = false;
00956     int backupfreq = KPilotSettings::backupFrequency();
00957 
00958 #ifdef DEBUG
00959     DEBUGKPILOT << fname << ": Backup Frequency is: [" << backupfreq <<
00960     "]. " << endl;
00961 #endif
00962 
00963     if ( (fNextSyncType == SyncAction::SyncMode::eHotSync) ||
00964         (fNextSyncType == SyncAction::SyncMode::eFullSync) )
00965     {
00969         if ( backupfreq == SyncAction::eOnRequestOnly )
00970         {
00971 #ifdef DEBUG
00972     DEBUGKPILOT << fname << ": Should not do backup..." << endl;
00973 #endif
00974             ret = false;
00975         }
00976         else if ( backupfreq == SyncAction::eEveryHotSync )
00977         {
00978 #ifdef DEBUG
00979     DEBUGKPILOT << fname << ": Should do backup..." << endl;
00980 #endif
00981             ret = true;
00982         }
00983     }
00984 
00985     return ret;
00986 
00987 }
00988 
00989 
00990 /* slot */ void PilotDaemon::startHotSync(KPilotLink *pilotLink)
00991 {
00992     FUNCTIONSETUP;
00993 
00994     bool pcchanged=false; // If last PC to sync was a different one (implies full sync, normally)
00995     QStringList conduits ; // list of conduits to run
00996     QString s; // a generic string for stuff
00997 
00998 #ifdef DEBUG
00999     DEBUGKPILOT << fname
01000         << ": Starting Sync with type "
01001         << fNextSyncType.name() << endl;
01002     DEBUGKPILOT << fname << ": Status is " << shortStatusString() << endl;
01003     (void) PilotDatabase::instanceCount();
01004 #endif
01005 
01006     fDaemonStatus = HOTSYNC_START ;
01007     if (fTray)
01008     {
01009         fTray->startHotSync();
01010     }
01011     informOthers(getKPilot(),getLogger(),getFileLogger());
01012 
01013 
01014     // Queue to add all the actions for this sync to.
01015     fSyncStack = new ActionQueue(pilotLink);
01016 
01017     // Check if the sync is possible at all.
01018     if (!isSyncPossible(fSyncStack,pilotLink,getKPilot()))
01019     {
01020         // Sync is not possible now, sorry action was added to
01021         // the queue, and we run that -- skipping all the rest of the sync stuff.
01022         goto launch;
01023     }
01024 
01025     // Except when the user has requested a Restore, in which case she knows she doesn't
01026     // want to sync with a blank palm and then back up the result over her stored backup files,
01027     // do a Full Sync when changing the PC or using a different Palm Desktop app.
01028     if (fNextSyncType.mode() != SyncAction::SyncMode::eRestore)
01029     { // Use gethostid to determine , since JPilot uses 1+(2000000000.0*random()/(RAND_MAX+1.0))
01030         // as PC_ID, so using JPilot and KPilot is the same as using two different PCs
01031         KPilotUser &usr = pilotLink->getPilotUser();
01032         pcchanged = usr.getLastSyncPC() !=(unsigned long) gethostid();
01033 
01034         if (pcchanged)
01035         {
01036 #ifdef DEBUG
01037             DEBUGKPILOT << fname << ": PC changed. Last sync PC: [" << usr.getLastSyncPC()
01038                 << "], me: [" << (unsigned long) gethostid() << "]" << endl;
01039 #endif
01040             if ( KPilotSettings::fullSyncOnPCChange() )
01041             {
01042 #ifdef DEBUG
01043                 DEBUGKPILOT << fname << ": Setting sync mode to full sync. " << endl;
01044 #endif
01045                 fNextSyncType = SyncAction::SyncMode::eFullSync;
01046             }
01047             else
01048             {
01049 #ifdef DEBUG
01050                 DEBUGKPILOT << fname << ": Not changing sync mode because of settings. " << endl;
01051 #endif
01052             }
01053         }
01054     }
01055 
01056     // Normal case: regular sync.
01057     fSyncStack->queueInit();
01058     fSyncStack->addAction(new CheckUser(pilotLink));
01059 
01060     conduits = KPilotSettings::installedConduits() ;
01061 
01062     if (fNextSyncType.isTest())
01063     {
01064         fSyncStack->addAction(new TestLink(pilotLink));
01065     }
01066     else
01067     {
01068         switch (fNextSyncType.mode())
01069         {
01070         case SyncAction::SyncMode::eBackup:
01071             if (KPilotSettings::runConduitsWithBackup() && (conduits.count() > 0))
01072             {
01073                 queueConduits(fSyncStack,conduits,fNextSyncType);
01074             }
01075             fSyncStack->addAction(new BackupAction(pilotLink,true));
01076             break;
01077         case SyncAction::SyncMode::eRestore:
01078             fSyncStack->addAction(new RestoreAction(pilotLink));
01079             queueInstaller(fSyncStack,pilotLink,fInstaller,conduits);
01080             break;
01081         case SyncAction::SyncMode::eFullSync:
01082         case SyncAction::SyncMode::eHotSync:
01083             // first install the files, and only then do the conduits
01084             // (conduits might want to sync a database that will be installed
01085             queueInstaller(fSyncStack,pilotLink,fInstaller,conduits);
01086             queueEditors(fSyncStack,pilotLink);
01087             queueConduits(fSyncStack,conduits,fNextSyncType);
01088             // After running the conduits, install new databases
01089             queueInstaller(fSyncStack,pilotLink,fInstaller,conduits);
01090             // And sync the remaining databases if needed.
01091             if (shouldBackup())
01092             {
01093                 fSyncStack->addAction(new BackupAction(pilotLink, (fNextSyncType == SyncAction::SyncMode::eFullSync)));
01094             }
01095             break;
01096         case SyncAction::SyncMode::eCopyPCToHH:
01097             queueConduits(fSyncStack,conduits,SyncAction::SyncMode::eCopyPCToHH);
01098             break;
01099         case SyncAction::SyncMode::eCopyHHToPC:
01100             queueConduits(fSyncStack,conduits,SyncAction::SyncMode::eCopyHHToPC);
01101             break;
01102         }
01103     }
01104 
01105 // Jump here to finalize the connections to the sync action
01106 // queue and start the actual sync.
01107 launch:
01108     fSyncStack->queueCleanup();
01109 
01110     QObject::connect(fSyncStack, SIGNAL(logError(const QString &)),
01111         this, SLOT(logError(const QString &)));
01112     QObject::connect(fSyncStack, SIGNAL(logMessage(const QString &)),
01113         this, SLOT(logMessage(const QString &)));
01114     QObject::connect(fSyncStack,
01115         SIGNAL(logProgress(const QString &,int)),
01116         this, SLOT(logProgress(const QString &,int)));
01117 
01118     QObject::connect(fSyncStack, SIGNAL(syncDone(SyncAction *)),
01119         this, SLOT(endHotSync()));
01120 
01121     QTimer::singleShot(0,fSyncStack,SLOT(execConduit()));
01122 
01123     updateTrayStatus();
01124 }
01125 
01126 /* slot */ void PilotDaemon::logMessage(const QString & s)
01127 {
01128     FUNCTIONSETUPL(2);
01129 
01130     getLogger().logMessage(s);
01131     getFileLogger().logMessage(s);
01132     updateTrayStatus(s);
01133 }
01134 
01135 /* slot */ void PilotDaemon::logError(const QString & s)
01136 {
01137     FUNCTIONSETUP;
01138 
01139     getLogger().logError(s);
01140     getFileLogger().logError(s);
01141     updateTrayStatus(s);
01142 }
01143 
01144 /* slot */ void PilotDaemon::logProgress(const QString & s, int i)
01145 {
01146     FUNCTIONSETUPL(2);
01147 
01148     getLogger().logProgress(s, i);
01149     getFileLogger().logProgress(s, i);
01150     if (!s.isEmpty()) updateTrayStatus(s);
01151 }
01152 
01153 /* slot */ void PilotDaemon::endHotSync()
01154 {
01155     FUNCTIONSETUP;
01156 
01157     if (fTray)
01158     {
01159         fTray->endHotSync();
01160     }
01161 
01162     KPILOT_DELETE(fSyncStack);
01163     fPilotLink->close();
01164 
01165     getLogger().logProgress(i18n("HotSync Completed.<br>"), 100);
01166     getFileLogger().logProgress(i18n("HotSync Completed.<br>"), 100);
01167     getLogger().logEndSync();
01168     getFileLogger().logEndSync();
01169     getKPilot().daemonStatus(KPilotDCOP::EndOfHotSync);
01170     KPilotSettings::setLastSyncTime(QDateTime::currentDateTime());
01171     KPilotSettings::self()->writeConfig();
01172 
01173     fDaemonStatus = HOTSYNC_END;
01174 
01175     if (fPostSyncAction & Quit)
01176     {
01177         getKPilot().daemonStatus(KPilotDCOP::DaemonQuit);
01178         kapp->quit();
01179     }
01180     if (fPostSyncAction & ReloadSettings)
01181     {
01182         reloadSettings();
01183     }
01184     else
01185     {
01186         QTimer::singleShot(10000,fPilotLink,SLOT(reset()));
01187     }
01188 
01189     fPostSyncAction = None;
01190     requestSync(0);
01191 
01192     (void) PilotDatabase::instanceCount();
01193 
01194     updateTrayStatus();
01195 }
01196 
01197 
01198 void PilotDaemon::slotFilesChanged()
01199 {
01200     FUNCTIONSETUP;
01201 }
01202 
01203 void PilotDaemon::slotRunKPilot()
01204 {
01205     FUNCTIONSETUP;
01206 
01207     QString kpilotError;
01208     QCString kpilotDCOP;
01209     int kpilotPID;
01210 
01211     if (KApplication::startServiceByDesktopName(CSL1("kpilot"),
01212             QString::null, &kpilotError, &kpilotDCOP, &kpilotPID
01213 #if (KDE_VERSION >= 220)
01214             // Startup notification added in 2.2
01215             , ""
01216 #endif
01217         ))
01218     {
01219         WARNINGKPILOT << "Couldn't start KPilot! " << kpilotError << endl;
01220     }
01221     else
01222     {
01223 #ifdef DEBUG
01224         DEBUGKPILOT << fname
01225             << ": Started KPilot with DCOP name "
01226             << kpilotDCOP << " (pid " << kpilotPID << ")" << endl;
01227 #endif
01228     }
01229 }
01230 
01231 void PilotDaemon::slotRunConfig()
01232 {
01233     FUNCTIONSETUP;
01234 
01235     // This function tries to send the raise() DCOP call to kpilot.
01236     // If it succeeds, we can assume kpilot is running and then try
01237     // to send the configure() DCOP call.
01238     // If it fails (probably because kpilot isn't running) it tries
01239     // to call kpilot via KProcess (using a command line switch to
01240     // only bring up the configure dialog).
01241     //
01242     // Implementing the function this way catches all cases.
01243     // ie 1 KPilot running with configure dialog open (raise())
01244     //    2 KPilot running with dialog NOT open (configureConduits())
01245     //    3 KPilot NOT running (KProcess)
01246 
01247     DCOPClient *client = kapp->dcopClient();
01248 
01249     // This DCOP call to kpilot's raise function solves the final case
01250     // ie when kpilot already has the dialog open
01251 
01252     if ( client->isApplicationRegistered( "kpilot" ) )
01253     {
01254         client->send("kpilot", "kpilot-mainwindow#1", "raise()",QString::null);
01255         client->send("kpilot", "KPilotIface", "configure()", QString::null);
01256     }
01257     else
01258     {
01259         // KPilot not running
01260         KProcess *p = new KProcess;
01261         *p << "kpilot" << "-s";
01262 
01263         p->start();
01264     }
01265 }
01266 
01267 void PilotDaemon::updateTrayStatus(const QString &s)
01268 {
01269     if (!fTray) return;
01270 
01271     QString tipText = CSL1("<qt>");
01272     tipText.append( s );
01273     tipText.append( CSL1(" ") );
01274     tipText.append( i18n("Next sync is %1.")
01275         .arg( fNextSyncType.name() ) );
01276     tipText.append( CSL1("</qt>") );
01277 
01278     QToolTip::remove(fTray);
01279     QToolTip::add(fTray,tipText);
01280     emitDCOPSignal( "kpilotDaemonStatusChanged()", QByteArray() );
01281     // emit the same dcop signal but including the information needed by Kontact to update its kpilot summary widget
01282     QByteArray data;
01283     QDataStream arg(data, IO_WriteOnly);
01284     arg << lastSyncDate();
01285     arg << shortStatusString();
01286     arg << configuredConduitList();
01287     arg << logFileName();
01288     arg << userName();
01289     arg << pilotDevice();
01290     arg << killDaemonOnExit();
01291     emitDCOPSignal( "kpilotDaemonStatusDetails(QDateTime,QString,QStringList,QString,QString,QString,bool)", data );
01292 }
01293 
01294 static KCmdLineOptions daemonoptions[] = {
01295 #ifdef DEBUG
01296     {"debug <level>", I18N_NOOP("Set debugging level"), "0"},
01297 #endif
01298     { "device <device>", I18N_NOOP("Device to try first"), ""},
01299     {"fail-silently", I18N_NOOP("Exit instead of complaining about bad configuration files"), 0},
01300     KCmdLineLastOption
01301 } ;
01302 
01303 
01304 int main(int argc, char **argv)
01305 {
01306     FUNCTIONSETUP;
01307 
01308     KLocale::setMainCatalogue("kpilot");
01309 
01310     KAboutData about("kpilotDaemon",
01311         I18N_NOOP("KPilot Daemon"),
01312         KPILOT_VERSION,
01313         "KPilot - HotSync software for KDE\n\n",
01314         KAboutData::License_GPL,
01315         "(c) 1998-2000,2001, Dan Pilone (c) 2000-2004, Adriaan de Groot",
01316         0L,
01317         "http://www.kpilot.org/"
01318         );
01319     about.addAuthor("Dan Pilone",
01320         I18N_NOOP("Project Leader"),
01321         "pilone@slac.com");
01322     about.addAuthor("Adriaan de Groot",
01323         I18N_NOOP("Maintainer"),
01324         "groot@kde.org", "http://www.kpilot.org/");
01325     about.addAuthor("Reinhold Kainhofer",
01326         I18N_NOOP("Developer"),
01327         "reinhold@kainhofer.com", "http://reinhold.kainhofer.com/Linux/");
01328     aboutData = &about;
01329 
01330 
01331     KCmdLineArgs::init(argc, argv, &about);
01332     KCmdLineArgs::addCmdLineOptions(daemonoptions,"kpilotconfig");
01333     KUniqueApplication::addCmdLineOptions();
01334     KCmdLineArgs *p = KCmdLineArgs::parsedArgs();
01335 
01336 #ifdef DEBUG
01337     KPilotConfig::getDebugLevel(p);
01338 #endif
01339     if (!KUniqueApplication::start())
01340     {
01341         if (p->isSet("device")){
01342             // tell the running kpilotDaemon to use
01343             // this device now
01344             DCOPClient d;
01345             QString dev(p->getOption("device"));
01346             QByteArray data;
01347             QDataStream arg(data, IO_WriteOnly);
01348             arg << dev;
01349             if (d.attach()){
01350                 d.send("kpilotDaemon", "KPilotDaemonIface", "setTempDevice(QString)", data );
01351                 d.detach();
01352             }
01353         }
01354         return 0;
01355     }
01356     KUniqueApplication a(true, true);
01357 
01358     // A block just to keep variables local.
01359     //
01360     //
01361     {
01362 //      KPilotSettings::self()->config()->setReadOnly(false);
01363 
01364         if (KPilotSettings::configVersion() < KPilotConfig::ConfigurationVersion)
01365         {
01366             WARNINGKPILOT << "Is still not configured for use."
01367                 << endl;
01368             if (!p->isSet("fail-silently"))
01369             {
01370                 KPilotConfig::sorryVersionOutdated(KPilotSettings::configVersion());
01371             }
01372             return 1;
01373         }
01374 
01375 #ifdef DEBUG
01376         DEBUGKPILOT << fname
01377             << ": Configuration version "
01378             << KPilotSettings::configVersion() << endl;
01379 #endif
01380     }
01381 
01382 
01383     PilotDaemon *gPilotDaemon = new PilotDaemon();
01384 
01385     if (p->isSet("device"))
01386         gPilotDaemon->setTempDevice(p->getOption("device"));
01387 
01388     if (gPilotDaemon->status() == PilotDaemon::ERROR)
01389     {
01390         delete gPilotDaemon;
01391 
01392         gPilotDaemon = 0;
01393         WARNINGKPILOT << "Failed to start up daemon "
01394             "due to errors constructing it." << endl;
01395         return 2;
01396     }
01397 
01398     gPilotDaemon->showTray();
01399 
01400     return a.exec();
01401 }
01402 
01403 
01404