kpilot/kpilot

pilotDaemon.cc

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