To have a working conduit, we still have to implement the synchronization itself. For the AvantGo conduit there already exists a library named "libmal" which does all the synchronization for us. In the section on autoconf and automake I already described the configure commands to check for the library and to link to the library on runtime (the -lmal flag stored in the variable $(MAL_LIB) ).
When using the libmal library, all we have to do is to make some proxy settings, and then call the function malsync( pilotSocket(), pInfo);, which will do the actual sync for us:
void MALConduit::readConfig() {
FUNCTIONSETUP;
QDateTime dt;
KConfigGroupSaver g(fConfig, MALConduitFactory::group());
fLastSync = fConfig->readDateTimeEntry(MALConduitFactory::lastSync(), &dt);
DEBUGCONDUIT<<"Last sync was "<<fLastSync.toString()<<endl;
eSyncTime=fConfig->readNumEntry(MALConduitFactory::syncTime(), 0);
// Proxy settings
eProxyType=fConfig->readNumEntry(MALConduitFactory::proxyType(), 0);
fProxyServer=fConfig->readEntry(MALConduitFactory::proxyServer(), "");
fProxyPort=fConfig->readNumEntry(MALConduitFactory::proxyPort(), 0);
fProxyUser=fConfig->readEntry(MALConduitFactory::proxyUser(), "");
fProxyPassword=fConfig->readEntry(MALConduitFactory::proxyPassword(), "");
}
void MALConduit::saveConfig() {
FUNCTIONSETUP;
KConfigGroupSaver g(fConfig, MALConduitFactory::group());
fConfig->writeEntry(MALConduitFactory::lastSync(), QDateTime::currentDateTime());
}
bool MALConduit::skip() {
QDateTime now=QDateTime::currentDateTime();
if (!fLastSync.isValid() || !now.isValid()) return false;
switch (eSyncTime) {
case eEveryHour:
if ( (fLastSync.secsTo(now)<=3600) && (fLastSync.time().hour()==now.time().hour()) ) return true;
else return false;
case eEveryDay:
if ( fLastSync.date() == now.date() ) return true;
else return false;
case eEveryWeek:
if ( (fLastSync.daysTo(now)<=7) && ( fLastSync.date().dayOfWeek()<=now.date().dayOfWeek()) ) return true;
else return false;
case eEveryMonth:
if ( (fLastSync.daysTo(now)<=31) && (fLastSync.date().month()==now.date().month()) ) return true;
else return false;
case eEverySync:
default:
return false;
}
return false;
}
/* virtual */ bool MALConduit::exec() {
FUNCTIONSETUP;
if (!fConfig) {
kdWarning() << k_funcinfo << ": No config file was set!" << endl;
return false;
}
readConfig();
if (skip()) {
emit logMessage(i18n("Skipping MAL sync, because last synchronization was not long enough ago."));
emit syncDone(this);
return true;
}
// Set all proxy settings
switch (eProxyType) {
case eProxyHTTP:
if (fProxyServer.isEmpty()) break;
setHttpProxy(fProxyServer.latin1());
if (fProxyPort>0 && fProxyPort<65536) setHttpProxyPort( fProxyPort );
else setHttpProxyPort(80);
if (!fProxyUser.isEmpty()) {
setProxyUsername( fProxyUser.latin1() );
if (!fProxyPassword.isEmpty()) setProxyPassword( fProxyPassword.latin1() );
}
break;
case eProxySOCKS:
setSocksProxy( fProxyServer.latin1() );
if (fProxyPort>0 && fProxyPort<65536) setSocksProxyPort( fProxyPort );
else setSocksProxyPort(1080);
break;
default:
break;
}
// Now initiate the sync.
PalmSyncInfo* pInfo=syncInfoNew();
if (!pInfo) {
kdWarning() << k_funcinfo << ": Could not allocate SyncInfo!" << endl;
emit logError(i18n("MAL synchronization failed (no SyncInfo)."));
return false;
}
malsync( pilotSocket(), pInfo);
syncInfoFree(pInfo);
saveConfig();
emit syncDone(this);
return true;
}
When you use an external library to do the sync, the external functions need a reference to the current connection to the handheld. In the pilot-link library, which is the base for all of KPilot's communication with the handheld, this is implemented via an integer identifier, which can be obtained by the funciton pilotSocket() of the SyncAction class.
The libmal also needs some internal data structed "PalmSyncInfo", which is obtained by its own syncInfoNew() function, but this part is libmal-specific.
Another issue is how to propagate log messages from the external library to KPilot's log window. SyncAction provides slots logError, logMessage and logProgress to put messages into KPilot's sync log. All you have to do is to call
emit logMessage(i18n("My own log message"));
The problem with these slots is that they are Qt-specific, while most libraries are written in C, and expect a hook function that will be called whenever a message needs to be written out. Unfortunately you cannot pass a member of your SyncAction-derived class, either, so the way out is to store a pointer to the current conduit instance (only one will be active at any time, anyway) in a static variable, and call the member method from this pointer:
// static pointer to the current conduit instance
static MALConduit *conduitInstance=0L;
// The hook function which will be called by the library
int malconduit_logf(const char *format, ...) {
FUNCTIONSETUP;
va_list val;
int rval;
va_start(val, format);
#define WRITE_MAX_BUF 4096
char msg[WRITE_MAX_BUF];
msg[0]='\0';
rval=vsnprintf(&msg[0], sizeof(msg), format, val);
va_end(val);
if (rval == -1) {
msg[WRITE_MAX_BUF-1] = '\0';
rval=WRITE_MAX_BUF-1;
}
if (conduitInstance) {
conduitInstance->printLogMessage(msg);
} else {
// write out to stderr
kdWarning()<<msg<<endl;
}
return rval;
}
void MALConduit::printLogMessage(QString msg) {
FUNCTIONSETUP;
emit logMessage(msg);
}
// Here we have to set the hooks for libmal to call.
MALConduit::MALConduit(KPilotDeviceLink * o,
const char *n,
const QStringList & a) :
ConduitAction(o, n, a)
{
FUNCTIONSETUP;
register_printStatusHook(malconduit_logf);
register_printErrorHook(malconduit_logf);
conduitInstance=this;
(void) MAL_conduit_id;
}