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