2009-05-11 16:31:17 +02:00
/*
* Copyright ( C ) 2009 Intel Corporation
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) version 3.
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 USA
*/
2009-03-30 21:34:01 +02:00
2009-03-30 17:54:47 +02:00
# ifdef HAVE_CONFIG_H
2009-10-05 14:49:32 +02:00
# include "config.h"
2009-03-30 17:54:47 +02:00
# endif
2009-09-08 19:11:49 +02:00
# include <syncevo/Logging.h>
2010-03-19 02:20:46 +01:00
# include <syncevo/LogStdout.h>
# include <syncevo/LogRedirect.h>
2009-09-08 19:11:49 +02:00
# include <syncevo/util.h>
2009-09-09 12:52:54 +02:00
# include <syncevo/SyncContext.h>
2010-09-07 17:01:07 +02:00
# include <syncevo/TransportAgent.h>
2009-09-14 12:58:22 +02:00
# include <syncevo/SoupTransportAgent.h>
2009-09-22 08:46:35 +02:00
# include <syncevo/SyncSource.h>
2009-11-25 10:01:06 +01:00
# include <syncevo/SyncML.h>
# include <syncevo/FileConfigNode.h>
2010-03-15 02:33:43 +01:00
# include <syncevo/Cmdline.h>
2009-09-08 19:11:49 +02:00
2009-09-24 12:54:36 +02:00
# include <synthesis/san.h>
2009-03-30 17:54:47 +02:00
# include <unistd.h>
# include <stdlib.h>
# include <signal.h>
# include <stdio.h>
2009-03-30 21:34:01 +02:00
# include <string.h>
2009-03-30 17:54:47 +02:00
# include <limits.h>
2009-10-12 08:16:22 +02:00
# include <sys/time.h>
2009-03-30 17:54:47 +02:00
2009-09-08 19:11:49 +02:00
# include <list>
# include <map>
# include <memory>
2009-10-13 16:12:34 +02:00
# include <iostream>
2009-10-09 09:22:13 +02:00
# include <limits>
# include <cmath>
2009-09-08 19:11:49 +02:00
# include <boost/shared_ptr.hpp>
# include <boost/weak_ptr.hpp>
2009-09-14 12:58:22 +02:00
# include <boost/noncopyable.hpp>
2009-04-23 13:13:17 +02:00
2009-09-08 19:11:49 +02:00
# include <glib-object.h>
2010-03-04 02:53:04 +01:00
# include <glib/gi18n.h>
2009-12-08 02:49:51 +01:00
# ifdef USE_GNOME_KEYRING
extern " C " {
# include <gnome-keyring.h>
}
# endif
2009-04-23 13:13:17 +02:00
2010-02-26 07:38:13 +01:00
# ifdef HAS_NOTIFY
# include <libnotify/notify.h>
# endif
2009-11-02 06:42:07 +01:00
class DBusMessage ;
static DBusMessage * SyncEvoHandleException ( DBusMessage * msg ) ;
# define DBUS_CXX_EXCEPTION_HANDLER SyncEvoHandleException
# include "gdbus-cxx-bridge.h"
2009-09-08 19:11:49 +02:00
using namespace SyncEvo ;
2009-04-23 13:13:17 +02:00
2009-09-08 19:11:49 +02:00
static GMainLoop * loop = NULL ;
2009-10-30 07:25:53 +01:00
static bool shutdownRequested = false ;
2010-03-31 20:04:28 +02:00
static LogRedirect * redirectPtr ;
2009-04-23 13:13:17 +02:00
2009-09-08 19:11:49 +02:00
/**
* Anything that can be owned by a client , like a connection
* or session .
*/
class Resource {
public :
virtual ~ Resource ( ) { }
2009-03-30 17:54:47 +02:00
} ;
2009-03-30 21:34:01 +02:00
2009-09-08 19:11:49 +02:00
class Session ;
class Connection ;
class Client ;
2009-09-17 21:07:55 +02:00
class DBusTransportAgent ;
2010-02-01 10:23:43 +01:00
class DBusUserInterface ;
2010-02-03 14:17:37 +01:00
class DBusServer ;
2009-03-30 21:34:01 +02:00
2009-11-02 06:42:07 +01:00
class DBusSyncException : public DBusCXXException , public Exception
{
public :
DBusSyncException ( const std : : string & file ,
int line ,
const std : : string & what ) : Exception ( file , line , what )
{ }
/**
* get exception name , used to convert to dbus error name
* subclasses should override it
*/
virtual std : : string getName ( ) const { return " org.syncevolution.Exception " ; }
virtual const char * getMessage ( ) const { return Exception : : what ( ) ; }
} ;
/**
* exceptions classes deriving from DBusException
* org . syncevolution . NoSuchConfig
*/
class NoSuchConfig : public DBusSyncException
{
public :
NoSuchConfig ( const std : : string & file ,
int line ,
const std : : string & error ) : DBusSyncException ( file , line , error )
{ }
virtual std : : string getName ( ) const { return " org.syncevolution.NoSuchConfig " ; }
} ;
/**
* org . syncevolution . NoSuchSource
*/
class NoSuchSource : public DBusSyncException
{
public :
NoSuchSource ( const std : : string & file ,
int line ,
const std : : string & error ) : DBusSyncException ( file , line , error )
{ }
virtual std : : string getName ( ) const { return " org.syncevolution.NoSuchSource " ; }
} ;
/**
* org . syncevolution . InvalidCall
*/
class InvalidCall : public DBusSyncException
{
public :
InvalidCall ( const std : : string & file ,
int line ,
const std : : string & error ) : DBusSyncException ( file , line , error )
{ }
virtual std : : string getName ( ) const { return " org.syncevolution.InvalidCall " ; }
} ;
2009-11-24 11:42:45 +01:00
/**
* org . syncevolution . SourceUnusable
* CheckSource will use this when the source cannot be used for whatever reason
*/
class SourceUnusable : public DBusSyncException
{
public :
SourceUnusable ( const std : : string & file ,
int line ,
const std : : string & error ) : DBusSyncException ( file , line , error )
{ }
virtual std : : string getName ( ) const { return " org.syncevolution.SourceUnusable " ; }
} ;
2009-11-02 06:42:07 +01:00
/**
* implement syncevolution exception handler
* to cover its default implementation
*/
static DBusMessage * SyncEvoHandleException ( DBusMessage * msg )
{
/** give an opportunity to let syncevolution handle exception */
Exception : : handle ( ) ;
try {
throw ;
} catch ( const dbus_error & ex ) {
2010-08-09 16:10:17 +02:00
return b_dbus_create_error ( msg , ex . dbusName ( ) . c_str ( ) , " %s " , ex . what ( ) ) ;
2009-11-02 06:42:07 +01:00
} catch ( const DBusCXXException & ex ) {
2010-08-09 16:10:17 +02:00
return b_dbus_create_error ( msg , ex . getName ( ) . c_str ( ) , " %s " , ex . getMessage ( ) ) ;
2009-11-02 06:42:07 +01:00
} catch ( const std : : runtime_error & ex ) {
2010-08-09 16:10:17 +02:00
return b_dbus_create_error ( msg , " org.syncevolution.Exception " , " %s " , ex . what ( ) ) ;
2009-11-02 06:42:07 +01:00
} catch ( . . . ) {
2010-08-09 16:10:17 +02:00
return b_dbus_create_error ( msg , " org.syncevolution.Exception " , " unknown " ) ;
2009-11-02 06:42:07 +01:00
}
}
2010-09-30 14:21:49 +02:00
/**
* Utility class which makes it easier to work with g_timeout_add_seconds ( ) .
* Instantiate this class with a specific callback . Use boost : : bind ( )
* to attach specific parameters to that callback . Then activate
* the timeout . Destructing this class will automatically remove
* the timeout and thus ensure that it doesn ' t trigger without
* valid parameters .
*/
class Timeout
{
guint m_tag ;
boost : : function < bool ( ) > m_callback ;
public :
Timeout ( ) :
m_tag ( 0 )
{
}
~ Timeout ( )
{
if ( m_tag ) {
g_source_remove ( m_tag ) ;
}
}
/**
* call the callback at regular intervals until it returns false
*/
void activate ( int seconds ,
const boost : : function < bool ( ) > & callback )
{
m_callback = callback ;
m_tag = g_timeout_add_seconds ( seconds , triggered , static_cast < gpointer > ( this ) ) ;
if ( ! m_tag ) {
SE_THROW ( " g_timeout_add_seconds() failed " ) ;
}
}
/**
* stop calling the callback , drop callback
*/
void deactivate ( )
{
if ( m_tag ) {
g_source_remove ( m_tag ) ;
m_tag = 0 ;
}
m_callback = 0 ;
}
private :
static gboolean triggered ( gpointer data )
{
Timeout * me = static_cast < Timeout * > ( data ) ;
return me - > m_callback ( ) ;
}
} ;
2009-09-09 12:52:54 +02:00
/**
* Implements the read - only methods in a Session and the Server .
* Only data is the server configuration name , everything else
* is created and destroyed inside the methods .
*/
class ReadOperations
{
public :
const std : : string m_configName ;
2010-02-03 14:17:37 +01:00
DBusServer & m_server ;
ReadOperations ( const std : : string & config_name , DBusServer & server ) ;
2009-09-09 12:52:54 +02:00
/** the double dictionary used to represent configurations */
2009-09-27 22:48:04 +02:00
typedef std : : map < std : : string , StringMap > Config_t ;
2009-09-09 12:52:54 +02:00
/** the array of reports filled by getReports() */
2009-09-27 22:48:04 +02:00
typedef std : : vector < StringMap > Reports_t ;
2009-09-09 12:52:54 +02:00
2009-09-27 08:02:03 +02:00
/** the array of databases used by getDatabases() */
typedef SyncSource : : Database SourceDatabase ;
typedef SyncSource : : Databases SourceDatabases_t ;
2009-10-14 17:53:39 +02:00
/** implementation of D-Bus GetConfigs() */
2010-02-03 14:17:37 +01:00
void getConfigs ( bool getTemplates , std : : vector < std : : string > & configNames ) ;
2009-10-14 17:53:39 +02:00
2009-09-09 12:52:54 +02:00
/** implementation of D-Bus GetConfig() for m_configName as server configuration */
void getConfig ( bool getTemplate ,
Config_t & config ) ;
/** implementation of D-Bus GetReports() for m_configName as server configuration */
void getReports ( uint32_t start , uint32_t count ,
Reports_t & reports ) ;
2009-09-27 08:02:03 +02:00
2009-11-11 10:55:15 +01:00
/** Session.CheckSource() */
2009-09-27 08:02:03 +02:00
void checkSource ( const string & sourceName ) ;
2009-11-11 10:55:15 +01:00
/** Session.GetDatabases() */
2009-09-27 08:02:03 +02:00
void getDatabases ( const string & sourceName , SourceDatabases_t & databases ) ;
2009-11-21 18:25:19 +01:00
private :
2009-11-26 09:37:19 +01:00
/**
* This virtual function is used to let subclass set
* filters to config . Only used internally .
2009-12-25 09:17:58 +01:00
* Return true if filters exists and have been set .
* Otherwise , nothing is set to config
2009-11-26 09:37:19 +01:00
*/
2009-12-25 09:17:58 +01:00
virtual bool setFilters ( SyncConfig & config ) { return false ; }
2009-11-26 09:37:19 +01:00
2010-03-30 17:39:36 +02:00
/**
* utility method which constructs a SyncConfig which references a local configuration ( never a template )
*
* In general , the config must exist , except in two cases :
* - configName = @ default ( considered always available )
* - mustExist = false ( used when reading a templates for a context which might not exist yet )
*/
boost : : shared_ptr < DBusUserInterface > getLocalConfig ( const std : : string & configName , bool mustExist = true ) ;
2009-09-09 12:52:54 +02:00
} ;
2009-09-27 08:02:03 +02:00
/**
* dbus_traits for SourceDatabase . Put it here for
* avoiding polluting gxx - dbus - bridge . h
*/
template < > struct dbus_traits < ReadOperations : : SourceDatabase > :
public dbus_struct_traits < ReadOperations : : SourceDatabase ,
dbus_member < ReadOperations : : SourceDatabase , std : : string , & ReadOperations : : SourceDatabase : : m_name ,
dbus_member < ReadOperations : : SourceDatabase , std : : string , & ReadOperations : : SourceDatabase : : m_uri ,
dbus_member_single < ReadOperations : : SourceDatabase , bool , & ReadOperations : : SourceDatabase : : m_isDefault > > > > { } ;
2010-01-13 02:47:42 +01:00
/**
* Automatic termination and track clients
* The dbus server will automatic terminate once it is idle in a given time .
* If any attached clients or connections , it never terminate .
* Once no actives , timer is started to detect the time of idle .
* Note that there will be less - than TERM_INTERVAL inaccuracy in seconds ,
* that ' s because we do check every TERM_INTERVAL seconds .
*/
class AutoTerm {
int m_refs ;
2010-03-25 16:28:21 +01:00
time_t m_interval ;
2010-01-13 02:47:42 +01:00
guint m_checkSource ;
2010-03-23 15:38:32 +01:00
time_t m_lastUsed ;
2010-01-13 02:47:42 +01:00
2010-03-23 15:38:32 +01:00
/**
* This callback is called as soon as we might have to terminate .
* If it finds that the server has been used in the meantime , it
* will simply set another timeout and check again later .
2010-01-13 02:47:42 +01:00
*/
static gboolean checkCallback ( gpointer data ) {
AutoTerm * at = static_cast < AutoTerm * > ( data ) ;
2010-03-23 15:38:32 +01:00
if ( ! at - > m_refs ) {
// currently idle, but also long enough?
time_t now = time ( NULL ) ;
if ( at - > m_lastUsed + at - > m_interval < = now ) {
// yes, shut down event loop and daemon
2010-01-13 02:47:42 +01:00
shutdownRequested = true ;
g_main_loop_quit ( loop ) ;
2010-03-23 15:38:32 +01:00
} else {
// check again later
at - > m_checkSource = g_timeout_add_seconds ( at - > m_lastUsed + at - > m_interval - now ,
checkCallback ,
data ) ;
2010-01-13 02:47:42 +01:00
}
}
2010-03-23 15:38:32 +01:00
// always remove the current timeout, its job is done
return FALSE ;
2010-01-13 02:47:42 +01:00
}
public :
/**
* constructor
* If interval is less than 0 , it means ' unlimited ' and never terminate
*/
2010-03-23 15:38:32 +01:00
AutoTerm ( int interval ) :
m_refs ( 0 ) ,
m_checkSource ( 0 ) ,
m_lastUsed ( 0 )
{
if ( interval < = 0 ) {
2010-03-25 16:28:21 +01:00
m_interval = 0 ;
2010-03-23 15:38:32 +01:00
// increasing reference counts prevents shutdown forever
ref ( ) ;
2010-03-25 16:28:21 +01:00
} else {
m_interval = interval ;
2010-03-23 15:38:32 +01:00
}
reset ( ) ;
}
~ AutoTerm ( )
{
if ( m_checkSource ) {
g_source_remove ( m_checkSource ) ;
2010-01-13 02:47:42 +01:00
}
2010-03-23 15:38:32 +01:00
}
2010-01-13 02:47:42 +01:00
//increase the actives objects
void ref ( int refs = 1 ) {
m_refs + = refs ;
2010-03-23 15:38:32 +01:00
reset ( ) ;
2010-01-13 02:47:42 +01:00
}
//decrease the actives objects
void unref ( int refs = 1 ) {
m_refs - = refs ;
if ( m_refs < = 0 ) {
m_refs = 0 ;
}
2010-03-23 15:38:32 +01:00
reset ( ) ;
2010-01-13 02:47:42 +01:00
}
2010-03-23 15:38:32 +01:00
/**
* To be called each time the server interacts with a client ,
* which includes adding or removing a client . If necessary ,
* this installs a timeout to stop the daemon when it has been
* idle long enough .
*/
void reset ( )
{
if ( m_refs > 0 ) {
// in use, don't need timeout
if ( m_checkSource ) {
g_source_remove ( m_checkSource ) ;
m_checkSource = 0 ;
}
} else {
// An already active timeout will trigger at the chosen time,
// then notice that the server has been used in the meantime and
// reset the timer. Therefore we don't have to remove it.
m_lastUsed = time ( NULL ) ;
if ( ! m_checkSource ) {
m_checkSource = g_timeout_add_seconds ( m_interval ,
checkCallback ,
static_cast < gpointer > ( this ) ) ;
}
}
2010-01-13 02:47:42 +01:00
}
} ;
2009-12-21 03:03:00 +01:00
class InfoReq ;
2009-09-27 08:02:03 +02:00
2010-02-03 14:17:37 +01:00
/**
2010-02-23 05:06:05 +01:00
* Query bluetooth devices from org . bluez
* The basic workflow is :
* 1 ) get default adapter from bluez by calling ' DefaultAdapter ' method of org . bluez . Manager
* 2 ) get all devices of the adapter by calling ' ListDevices ' method of org . bluez . Adapter
* 3 ) iterate all devices and get properties for each one by calling ' GetProperties ' method of org . bluez . Device .
* Then check its UUIDs whether it contains sync services and put it in the sync device list if it is
*
* To track changes of devices dynamically , here also listen signals from bluez :
* org . bluez . Manager - DefaultAdapterChanged : default adapter is changed and thus have to get its devices
* and update sync device list
* org . bluez . Adapter - DeviceCreated , DeviceRemoved : device is created or removed and device list is updated
* org . bluez . Device - PropertyChanged : property is changed and device information is changed and tracked
*
* This class is to manage querying bluetooth devices from org . bluez . Also
* it acts a proxy to org . bluez . Manager .
2010-02-03 14:17:37 +01:00
*/
2010-02-23 02:26:58 +01:00
class BluezManager : public DBusRemoteObject {
2010-02-03 14:17:37 +01:00
public :
2010-02-23 02:26:58 +01:00
BluezManager ( DBusServer & server ) ;
2010-02-03 14:17:37 +01:00
2010-02-23 02:26:58 +01:00
virtual const char * getDestination ( ) const { return " org.bluez " ; }
virtual const char * getPath ( ) const { return " / " ; }
virtual const char * getInterface ( ) const { return " org.bluez.Manager " ; }
virtual DBusConnection * getConnection ( ) const { return m_bluezConn . get ( ) ; }
2010-02-23 05:06:05 +01:00
bool isDone ( ) { return m_done ; }
2010-02-23 02:26:58 +01:00
2010-02-03 14:17:37 +01:00
private :
2010-02-23 02:26:58 +01:00
class BluezDevice ;
2010-02-23 05:06:05 +01:00
/**
* This class acts a proxy to org . bluez . Adapter .
* Call methods of org . bluez . Adapter and listen signals from it
* to get devices list and track its changes
*/
2010-02-23 02:26:58 +01:00
class BluezAdapter : public DBusRemoteObject
2010-02-03 14:17:37 +01:00
{
2010-02-23 02:26:58 +01:00
public :
BluezAdapter ( BluezManager & manager , const string & path ) ;
2010-02-03 14:17:37 +01:00
virtual const char * getDestination ( ) const { return " org.bluez " ; }
virtual const char * getPath ( ) const { return m_path . c_str ( ) ; }
virtual const char * getInterface ( ) const { return " org.bluez.Adapter " ; }
2010-02-23 02:26:58 +01:00
virtual DBusConnection * getConnection ( ) const { return m_manager . getConnection ( ) ; }
void checkDone ( bool forceDone = false )
{
if ( forceDone | | m_devReplies > = m_devNo ) {
m_devReplies = m_devNo = 0 ;
m_manager . setDone ( true ) ;
} else {
m_manager . setDone ( false ) ;
}
}
2010-02-23 05:06:05 +01:00
std : : vector < boost : : shared_ptr < BluezDevice > > & getDevices ( ) { return m_devices ; }
private :
/** callback of 'ListDevices' signal. Used to get all available devices of the adapter */
2010-02-23 02:26:58 +01:00
void listDevicesCb ( const std : : vector < DBusObject_t > & devices , const string & error ) ;
2010-02-23 05:06:05 +01:00
/** callback of 'DeviceRemoved' signal. Used to track a device is removed */
2010-02-23 02:26:58 +01:00
void deviceRemoved ( const DBusObject_t & object ) ;
2010-02-23 05:06:05 +01:00
/** callback of 'DeviceCreated' signal. Used to track a new device is created */
2010-02-23 02:26:58 +01:00
void deviceCreated ( const DBusObject_t & object ) ;
BluezManager & m_manager ;
/** the object path of adapter */
std : : string m_path ;
/** the number of device for the default adapter */
int m_devNo ;
/** the number of devices having reply */
int m_devReplies ;
/** all available devices */
std : : vector < boost : : shared_ptr < BluezDevice > > m_devices ;
2010-02-23 05:06:05 +01:00
/** represents 'DeviceRemoved' signal of org.bluez.Adapter*/
SignalWatch1 < DBusObject_t > m_deviceRemoved ;
/** represents 'DeviceAdded' signal of org.bluez.Adapter*/
SignalWatch1 < DBusObject_t > m_deviceAdded ;
2010-02-23 02:26:58 +01:00
friend class BluezDevice ;
2010-02-03 14:17:37 +01:00
} ;
2010-02-23 05:06:05 +01:00
/**
* This class acts a proxy to org . bluez . Device .
* Call methods of org . bluez . Device and listen signals from it
* to get properties of device and track its changes
*/
2010-02-23 02:26:58 +01:00
class BluezDevice : public DBusRemoteObject
2010-02-03 14:17:37 +01:00
{
2010-02-23 02:26:58 +01:00
public :
2010-02-23 05:06:05 +01:00
typedef map < string , boost : : variant < vector < string > , string > > PropDict ;
2010-02-23 02:26:58 +01:00
BluezDevice ( BluezAdapter & adapter , const string & path ) ;
2010-02-03 14:17:37 +01:00
virtual const char * getDestination ( ) const { return " org.bluez " ; }
virtual const char * getPath ( ) const { return m_path . c_str ( ) ; }
virtual const char * getInterface ( ) const { return " org.bluez.Device " ; }
2010-02-23 02:26:58 +01:00
virtual DBusConnection * getConnection ( ) const { return m_adapter . m_manager . getConnection ( ) ; }
2010-02-23 05:06:05 +01:00
string getMac ( ) { return m_mac ; }
2010-02-23 02:26:58 +01:00
/**
* check whether the current device has sync service
* if yes , put it in the adapter ' s sync devices list
*/
void checkSyncService ( const std : : vector < std : : string > & uuids ) ;
2010-02-23 05:06:05 +01:00
private :
/** callback of 'GetProperties' method. The properties of the device is gotten */
2010-02-23 02:26:58 +01:00
void getPropertiesCb ( const PropDict & props , const string & error ) ;
2010-02-03 14:17:37 +01:00
2010-02-23 05:06:05 +01:00
/** callback of 'PropertyChanged' signal. Changed property is tracked */
2010-02-23 02:26:58 +01:00
void propertyChanged ( const string & name , const boost : : variant < vector < string > , string > & prop ) ;
BluezAdapter & m_adapter ;
/** the object path of the device */
string m_path ;
/** name of the device */
string m_name ;
/** mac address of the device */
string m_mac ;
/** whether the calling of 'GetProperties' is returned */
bool m_reply ;
typedef SignalWatch2 < string , boost : : variant < vector < string > , string > > PropertySignal ;
2010-02-23 05:06:05 +01:00
/** represents 'PropertyChanged' signal of org.bluez.Device */
PropertySignal m_propertyChanged ;
2010-02-23 02:26:58 +01:00
friend class BluezAdapter ;
} ;
2010-02-03 14:17:37 +01:00
/*
* check whether the data is generated . If errors , force initilization done
*/
2010-02-23 02:26:58 +01:00
void setDone ( bool done ) { m_done = done ; }
2010-02-03 14:17:37 +01:00
2010-02-23 05:06:05 +01:00
/** callback of 'DefaultAdapter' method to get the default bluetooth adapter */
2010-02-03 14:17:37 +01:00
void defaultAdapterCb ( const DBusObject_t & adapter , const string & error ) ;
2010-02-23 05:06:05 +01:00
/** callback of 'DefaultAdapterChanged' signal to track changes of the default adapter */
2010-02-23 02:26:58 +01:00
void defaultAdapterChanged ( const DBusObject_t & adapter ) ;
2010-02-03 14:17:37 +01:00
DBusServer & m_server ;
DBusConnectionPtr m_bluezConn ;
2010-02-23 02:26:58 +01:00
boost : : shared_ptr < BluezAdapter > m_adapter ;
2010-02-23 05:06:05 +01:00
/** represents 'DefaultAdapterChanged' signal of org.bluez.Adapter*/
2010-03-26 21:41:07 +01:00
SignalWatch1 < DBusObject_t > m_adapterChanged ;
2010-02-23 02:26:58 +01:00
/** flag to indicate whether the calls are all returned */
2010-02-03 14:17:37 +01:00
bool m_done ;
} ;
2010-03-04 02:53:04 +01:00
/**
* a listener to listen changes of session
* currently only used to track changes of running a sync in a session
*/
class SessionListener
{
public :
/**
* method is called when a sync is successfully started .
* Here ' successfully started ' means the synthesis engine starts
* to access the sources .
*/
virtual void syncSuccessStart ( ) { }
/**
* method is called when a sync is done . Also
* sync status are passed .
*/
virtual void syncDone ( SyncMLStatus status ) { }
virtual ~ SessionListener ( ) { }
} ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
/**
* Manager to manage automatic sync .
* Once a configuration is enabled with automatic sync , possibly http or obex - bt or both , one or more
* tasks for different URLs are added in the task map , grouped by their intervals .
* A task have to be checked whether there is an existing same task in the working queue . Once actived ,
* it is put in the working queue .
*
* At any time , there is at most one session for the first task . Once it is active by DBusServer ,
* we prepare it and make it ready to run . After completion , a new session is created again for the
* next task . And so on .
*
* The DBusServer is in charge of dispatching requests from dbus clients and automatic sync tasks .
* See DBusServer : : run ( ) .
*
* Here there are 3 scenarios which have been considered to do automatic sync right now :
* 1 ) For a config enables autosync , an interval has passed .
* 2 ) Once users log in or resume and an interval has passed . Not implemented yet .
* 3 ) Evolution data server notify any changes . Not implemented yet .
*/
2010-03-04 02:53:04 +01:00
class AutoSyncManager : public SessionListener
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
{
DBusServer & m_server ;
/**
* A single task for automatic sync .
* Each task maintain one task for only one sync URL , which never combines
* more than one sync URLs . The difference from ' syncURL ' property here is
* that different URLs may have different transports with different statuses .
* Another reason is that SyncContext only use the first URL if it has many sync
* URLs when running . So we split , schedule and process them one by one .
* Each task contains one peer name , peer duration and peer url .
* It is created in initialization and may be updated due to config change .
* It is scheduled by AutoSyncManager to be put in the working queue .
*/
class AutoSyncTask
{
public :
/** the peer name of a config */
string m_peer ;
2010-03-25 17:58:54 +01:00
/** the time that the peer must at least have been around (seconds) */
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
unsigned int m_delay ;
/** the 'syncURL' used by synchronization. It always contains only one sync URL. */
string m_url ;
AutoSyncTask ( const string & peer , unsigned int delay , const string & url )
: m_peer ( peer ) , m_delay ( delay ) , m_url ( url )
{ }
/** compare whether two tasks are the same. May refine it later with more information */
bool operator = = ( const AutoSyncTask & right ) const
{
if ( boost : : iequals ( m_peer , right . m_peer ) & &
boost : : iequals ( m_url , right . m_url ) ) {
return true ;
}
return false ;
}
} ;
/**
* AutoSyncTaskList is used to manage sync tasks which are grouped by the
* interval . Each list has one timeout gsource .
*/
class AutoSyncTaskList : public list < AutoSyncTask >
{
AutoSyncManager & m_manager ;
2010-03-25 17:58:54 +01:00
/** the interval used to create timeout source (seconds) */
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
unsigned int m_interval ;
/** timeout gsource */
GLibEvent m_source ;
/** callback of timeout source */
static gboolean taskListTimeoutCb ( gpointer data ) ;
public :
AutoSyncTaskList ( AutoSyncManager & manager , unsigned int interval )
: m_manager ( manager ) , m_interval ( interval ) , m_source ( 0 )
{ }
~ AutoSyncTaskList ( ) {
if ( m_source ) {
g_source_remove ( m_source ) ;
}
}
/** create timeout source once all tasks are added */
void createTimeoutSource ( ) ;
/** check task list and put task into working queue */
void scheduleTaskList ( ) ;
} ;
2010-03-04 02:53:04 +01:00
# ifdef HAS_NOTIFY
/**
* This class is to send notifications to notification server .
* Notifications are sent via ' send ' . Once a new noficication is
* to be sent , the old notification will be closed and a new one
* is created for the new requirement .
*/
class Notification
{
public :
Notification ( ) ;
~ Notification ( ) ;
/** callback of click button(actions) of notification */
static void notifyAction ( NotifyNotification * notify , gchar * action , gpointer userData ) ;
/**
* send a notification in the notification server
* Action for ' view ' may pop up a sync - ui , but needs some
* parameters . ' viewParams ' is the params used by sync - ui .
*/
void send ( const char * summary , const char * body , const char * viewParams = NULL ) ;
private :
/** flag to indicate whether libnotify is initalized successfully */
bool m_init ;
/** flag to indicate whether libnotify accepts actions */
bool m_actions ;
/** the current notification */
NotifyNotification * m_notification ;
} ;
# endif
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
/** init a config and set up auto sync task for it */
void initConfig ( const string & configName ) ;
/** remove tasks from m_peerMap and m_workQueue created from the config */
void remove ( const string & configName ) ;
/** a map to contain all auto sync tasks. All initialized tasks are stored here.
* Tasks here are grouped by auto sync interval */
typedef std : : map < unsigned int , boost : : shared_ptr < AutoSyncTaskList > > PeerMap ;
PeerMap m_peerMap ;
/**
* a working queue that including tasks which are pending for doing sync .
* Tasks here are picked from m_peerMap and scheduled to do auto sync */
list < AutoSyncTask > m_workQueue ;
/**
* the current active task , which may own a session
*/
boost : : shared_ptr < AutoSyncTask > m_activeTask ;
/**
* the only session created for active task and is put in the session queue .
* at most one session at any time no matter how many tasks we actually have
*/
boost : : shared_ptr < Session > m_session ;
2010-03-04 02:53:04 +01:00
/** the current sync of session is successfully started */
bool m_syncSuccessStart ;
2010-02-26 07:38:13 +01:00
# ifdef HAS_NOTIFY
2010-03-04 02:53:04 +01:00
/** used to send notifications */
Notification m_notify ;
2010-02-26 07:38:13 +01:00
# endif
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
/**
* It reads all peers which are enabled to do auto sync and store them in
* the m_peerMap and then add timeout sources in the main loop to schedule
* auto sync tasks .
*/
void init ( ) ;
/** operations on tasks queue */
void clearAllTasks ( ) { m_workQueue . clear ( ) ; }
/** check m_peerMap and put all tasks in it to working queue */
void scheduleAll ( ) ;
/**
* add an auto sync task in the working queue
* Do check before adding a task in the working queue
* Return true if the task is added in the list .
*/
bool addTask ( const AutoSyncTask & syncTask ) ;
/** find an auto sync task in the working queue or is running */
bool findTask ( const AutoSyncTask & syncTask ) ;
/**
* check whether a task is suitable to put in the working queue
* Manager has the information needed to make the decision
*/
bool taskLikelyToRun ( const AutoSyncTask & syncTask ) ;
public :
AutoSyncManager ( DBusServer & server )
2010-03-04 02:53:04 +01:00
: m_server ( server ) , m_syncSuccessStart ( false )
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
{
init ( ) ;
}
2010-03-02 06:34:42 +01:00
/**
* prevent dbus server automatic termination when it has
* any auto sync task enabled in the configs .
* If returning true , prevent automatic termination .
*/
bool preventTerm ( ) { return ! m_peerMap . empty ( ) ; }
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
/**
* called when a config is changed . This causes re - loading the config
*/
void update ( const string & configName ) ;
/* Is there any auto sync task in the queue? */
bool hasTask ( ) { return ! m_workQueue . empty ( ) ; }
/**
* pick the front task from the working queue and create a session for it .
* The session won ' t be used to do sync until it is active so ' prepare ' is
* for calling ' sync ' to make the session ready to run
* If there has been a session for the front task , do nothing
*/
void startTask ( ) ;
/** check whether the active session is owned by Automatic Sync Manger */
bool hasActiveSession ( ) ;
/** set config and run sync to make the session ready to run */
void prepare ( ) ;
2010-03-04 02:53:04 +01:00
/**
* Acts as a session listener to track sync statuses if the session is
* belonged to auto sync manager to do auto sync .
* Two methods to listen to session sync changes .
*/
virtual void syncSuccessStart ( ) ;
virtual void syncDone ( SyncMLStatus status ) ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
} ;
/**
* A timer helper to check whether now is timeout according to
* user ' s setting . Timeout is calculated in milliseconds
*/
class Timer {
timeval m_startTime ; ///< start time
unsigned long m_timeoutMs ; ///< timeout in milliseconds, set by user
/**
* calculate duration between now and start time
* return value is in milliseconds
*/
unsigned long duration ( const timeval & minuend , const timeval & subtrahend )
{
unsigned long result = 0 ;
if ( minuend . tv_sec > subtrahend . tv_sec | |
( minuend . tv_sec = = subtrahend . tv_sec & & minuend . tv_usec > subtrahend . tv_usec ) ) {
result = minuend . tv_sec - subtrahend . tv_sec ;
result * = 1000 ;
result + = ( minuend . tv_usec - subtrahend . tv_usec ) / 1000 ;
}
return result ;
}
public :
/**
* constructor
* @ param timeoutMs timeout in milliseconds
*/
Timer ( unsigned long timeoutMs = 0 ) : m_timeoutMs ( timeoutMs )
{
reset ( ) ;
}
/**
* reset the timer and mark start time as current time
*/
void reset ( ) { gettimeofday ( & m_startTime , NULL ) ; }
/**
* check whether it is timeout
*/
bool timeout ( )
{
return timeout ( m_timeoutMs ) ;
}
/**
* check whether the duration timer records is longer than the given duration
*/
bool timeout ( unsigned long timeoutMs )
{
timeval now ;
gettimeofday ( & now , NULL ) ;
return duration ( now , m_startTime ) > = timeoutMs ;
}
} ;
class PresenceStatus {
bool m_httpPresence ;
bool m_btPresence ;
bool m_initiated ;
DBusServer & m_server ;
/** two timers to record when the statuses of network and bt are changed */
Timer m_httpTimer ;
Timer m_btTimer ;
enum PeerStatus {
/* The transport is not available (local problem) */
NOTRANSPORT ,
/* The peer is not contactable (remote problem) */
UNREACHABLE ,
/* Not for sure whether the peer is presence but likely*/
MIGHTWORK ,
INVALID
} ;
typedef map < string , vector < pair < string , PeerStatus > > > StatusMap ;
typedef pair < const string , vector < pair < string , PeerStatus > > > StatusPair ;
typedef pair < string , PeerStatus > PeerStatusPair ;
StatusMap m_peers ;
static std : : string status2string ( PeerStatus status ) {
switch ( status ) {
case NOTRANSPORT :
return " no transport " ;
break ;
case UNREACHABLE :
return " not present " ;
break ;
case MIGHTWORK :
return " " ;
break ;
case INVALID :
return " invalid transport status " ;
}
// not reached, keep compiler happy
return " " ;
}
public :
PresenceStatus ( DBusServer & server )
: m_httpPresence ( false ) , m_btPresence ( false ) , m_initiated ( false ) , m_server ( server ) ,
m_httpTimer ( ) , m_btTimer ( )
{
}
2010-04-01 04:15:20 +02:00
enum TransportType {
HTTP_TRANSPORT ,
BT_TRANSPORT ,
INVALID_TRANSPORT
} ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
void init ( ) ;
/* Implement DBusServer::checkPresence*/
void checkPresence ( const string & peer , string & status , std : : vector < std : : string > & transport ) ;
void updateConfigPeers ( const std : : string & peer , const ReadOperations : : Config_t & config ) ;
void updatePresenceStatus ( bool httpPresence , bool btPresence ) ;
2010-04-01 04:15:20 +02:00
void updatePresenceStatus ( bool newStatus , TransportType type ) ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
bool getHttpPresence ( ) { return m_httpPresence ; }
bool getBtPresence ( ) { return m_btPresence ; }
Timer & getHttpTimer ( ) { return m_httpTimer ; }
Timer & getBtTimer ( ) { return m_btTimer ; }
} ;
2010-04-01 04:15:20 +02:00
/*
* Implements org . moblin . connman . Manager
* GetProperty : getPropCb
* PropertyChanged : propertyChanged
* */
class ConnmanClient : public DBusRemoteObject
{
public :
ConnmanClient ( DBusServer & server ) ;
virtual const char * getDestination ( ) const { return " org.moblin.connman " ; }
virtual const char * getPath ( ) const { return " / " ; }
virtual const char * getInterface ( ) const { return " org.moblin.connman.Manager " ; }
virtual DBusConnection * getConnection ( ) const { return m_connmanConn . get ( ) ; }
void propertyChanged ( const string & name ,
const boost : : variant < vector < string > , string > & prop ) ;
void getPropCb ( const std : : map < std : : string , boost : : variant < std : : vector < std : : string > > > & props , const string & error ) ;
private :
DBusServer & m_server ;
DBusConnectionPtr m_connmanConn ;
SignalWatch2 < string , boost : : variant < vector < string > , string > > m_propertyChanged ;
} ;
2010-09-30 14:21:49 +02:00
2009-09-08 19:11:49 +02:00
/**
* Implements the main org . syncevolution . Server interface .
*
* All objects created by it get a reference to the creating
* DBusServer instance so that they can call some of its
* methods . Because that instance holds references to all
* of these objects and deletes them before destructing itself ,
* that reference is guaranteed to remain valid .
*/
2010-03-18 06:38:06 +01:00
class DBusServer : public DBusObjectHelper ,
public LoggerBase
2009-09-08 19:11:49 +02:00
{
2009-09-14 12:58:22 +02:00
GMainLoop * m_loop ;
2009-09-08 19:11:49 +02:00
uint32_t m_lastSession ;
typedef std : : list < std : : pair < boost : : shared_ptr < Watch > , boost : : shared_ptr < Client > > > Clients_t ;
Clients_t m_clients ;
2010-02-03 02:50:56 +01:00
/* Event source that regurally pool network manager
* */
GLibEvent m_pollConnman ;
2009-09-08 19:11:49 +02:00
/**
* The session which currently holds the main lock on the server .
* To avoid issues with concurrent modification of data or configs ,
* only one session may make such modifications at a time . A
* plain pointer which is reset by the session ' s deconstructor .
*
2009-09-17 21:03:53 +02:00
* A weak pointer alone did not work because it does not provide access
2009-09-08 19:11:49 +02:00
* to the underlying pointer after the last corresponding shared
* pointer is gone ( which triggers the deconstructing of the session ) .
*/
Session * m_activeSession ;
2009-09-17 21:03:53 +02:00
/**
* The weak pointer that corresponds to m_activeSession .
*/
boost : : weak_ptr < Session > m_activeSessionRef ;
2009-09-08 19:11:49 +02:00
/**
* The running sync session . Having a separate reference to it
* ensures that the object won ' t go away prematurely , even if all
* clients disconnect .
*/
boost : : shared_ptr < Session > m_syncSession ;
typedef std : : list < boost : : weak_ptr < Session > > WorkQueue_t ;
/**
* A queue of pending , idle Sessions . Sorted by priority , most
* important one first . Currently this is used to give client
* requests a boost over remote connections and ( in the future )
* automatic syncs .
*
* Active sessions are removed from this list and then continue
* to exist as long as a client in m_clients references it or
* it is the currently running sync session ( m_syncSession ) .
*/
WorkQueue_t m_workQueue ;
2009-12-21 03:03:00 +01:00
/**
* a hash of pending InfoRequest
*/
typedef std : : map < string , boost : : weak_ptr < InfoReq > > InfoReqMap ;
// hash map of pending info requests
InfoReqMap m_infoReqMap ;
// the index of last info request
uint32_t m_lastInfoReq ;
2010-02-03 14:17:37 +01:00
// a hash to represent matched templates for devices, the key is
// the peer name
typedef std : : map < string , boost : : shared_ptr < SyncConfig : : TemplateDescription > > MatchedTemplates ;
MatchedTemplates m_matchedTempls ;
2010-02-23 02:26:58 +01:00
BluezManager m_bluezManager ;
2010-02-03 14:17:37 +01:00
2010-02-23 05:06:05 +01:00
/** devices which have sync services */
SyncConfig : : DeviceList m_syncDevices ;
2009-09-08 19:11:49 +02:00
/**
* Watch callback for a specific client or connection .
*/
void clientGone ( Client * c ) ;
2010-08-24 21:39:00 +02:00
/** Server.GetCapabilities() */
vector < string > getCapabilities ( ) ;
/** Server.GetVersions() */
StringMap getVersions ( ) ;
2009-11-11 10:55:15 +01:00
/** Server.Attach() */
2009-09-22 15:09:31 +02:00
void attachClient ( const Caller_t & caller ,
const boost : : shared_ptr < Watch > & watch ) ;
2009-11-11 10:55:15 +01:00
/** Server.Detach() */
2009-09-22 15:09:31 +02:00
void detachClient ( const Caller_t & caller ) ;
2010-08-25 14:54:11 +02:00
/** Server.DisableNotifications() */
void disableNotifications ( const Caller_t & caller ,
const string & notifications ) {
setNotifications ( false , caller , notifications ) ;
}
/** Server.EnableNotifications() */
void enableNotifications ( const Caller_t & caller ,
const string & notifications ) {
setNotifications ( true , caller , notifications ) ;
}
/** actual implementation of enable and disable */
void setNotifications ( bool enable ,
const Caller_t & caller ,
const string & notifications ) ;
2009-11-11 10:55:15 +01:00
/** Server.Connect() */
2009-09-08 19:11:49 +02:00
void connect ( const Caller_t & caller ,
const boost : : shared_ptr < Watch > & watch ,
2009-09-27 22:48:04 +02:00
const StringMap & peer ,
2009-09-08 19:11:49 +02:00
bool must_authenticate ,
2009-09-23 09:44:01 +02:00
const std : : string & session ,
2009-09-08 19:11:49 +02:00
DBusObject_t & object ) ;
2009-11-11 10:55:15 +01:00
/** Server.StartSession() */
2009-09-08 19:11:49 +02:00
void startSession ( const Caller_t & caller ,
const boost : : shared_ptr < Watch > & watch ,
const std : : string & server ,
2010-08-25 10:37:58 +02:00
DBusObject_t & object ) {
startSessionWithFlags ( caller , watch , server , std : : vector < std : : string > ( ) , object ) ;
}
/** Server.StartSessionWithFlags() */
void startSessionWithFlags ( const Caller_t & caller ,
const boost : : shared_ptr < Watch > & watch ,
const std : : string & server ,
const std : : vector < std : : string > & flags ,
DBusObject_t & object ) ;
2009-09-08 19:11:49 +02:00
2009-11-11 10:55:15 +01:00
/** Server.GetConfig() */
2009-09-09 12:52:54 +02:00
void getConfig ( const std : : string & config_name ,
bool getTemplate ,
ReadOperations : : Config_t & config )
{
2010-02-03 14:17:37 +01:00
ReadOperations ops ( config_name , * this ) ;
2009-09-09 12:52:54 +02:00
ops . getConfig ( getTemplate , config ) ;
}
2009-11-11 10:55:15 +01:00
/** Server.GetReports() */
2009-09-09 12:52:54 +02:00
void getReports ( const std : : string & config_name ,
uint32_t start , uint32_t count ,
ReadOperations : : Reports_t & reports )
{
2010-02-03 14:17:37 +01:00
ReadOperations ops ( config_name , * this ) ;
2009-09-09 12:52:54 +02:00
ops . getReports ( start , count , reports ) ;
}
2009-11-19 14:07:23 +01:00
/** Server.CheckSource() */
void checkSource ( const std : : string & configName ,
const std : : string & sourceName )
{
2010-02-03 14:17:37 +01:00
ReadOperations ops ( configName , * this ) ;
2009-11-19 14:07:23 +01:00
ops . checkSource ( sourceName ) ;
}
/** Server.GetDatabases() */
void getDatabases ( const std : : string & configName ,
const string & sourceName ,
ReadOperations : : SourceDatabases_t & databases )
{
2010-02-03 14:17:37 +01:00
ReadOperations ops ( configName , * this ) ;
2009-11-19 14:07:23 +01:00
ops . getDatabases ( sourceName , databases ) ;
}
2010-02-03 14:17:37 +01:00
void getConfigs ( bool getTemplates ,
std : : vector < std : : string > & configNames )
{
ReadOperations ops ( " " , * this ) ;
ops . getConfigs ( getTemplates , configNames ) ;
}
2009-11-11 10:55:15 +01:00
/** Server.CheckPresence() */
2009-09-22 15:09:31 +02:00
void checkPresence ( const std : : string & server ,
std : : string & status ,
std : : vector < std : : string > & transports ) ;
2009-11-20 09:36:26 +01:00
/** Server.GetSessions() */
2010-04-01 14:20:05 +02:00
void getSessions ( std : : vector < DBusObject_t > & sessions ) ;
2009-11-20 09:36:26 +01:00
2009-12-21 03:03:00 +01:00
/** Server.InfoResponse() */
void infoResponse ( const Caller_t & caller ,
const std : : string & id ,
const std : : string & state ,
const std : : map < string , string > & response ) ;
friend class InfoReq ;
/** emit InfoRequest */
void emitInfoReq ( const InfoReq & ) ;
/** get the next id of InfoRequest */
std : : string getNextInfoReq ( ) ;
/** remove InfoReq from hash map */
void removeInfoReq ( const InfoReq & req ) ;
2009-11-11 10:55:15 +01:00
/** Server.SessionChanged */
2009-09-08 19:11:49 +02:00
EmitSignal2 < const DBusObject_t & ,
bool > sessionChanged ;
2009-11-11 10:55:15 +01:00
/** Server.PresenceChanged */
2009-09-22 15:09:31 +02:00
EmitSignal3 < const std : : string & ,
const std : : string & ,
const std : : string & > presence ;
2010-02-23 09:56:56 +01:00
/**
* Server . TemplatesChanged , triggered each time m_syncDevices , the
* input for the templates , is changed
*/
EmitSignal0 templatesChanged ;
2010-08-25 21:03:31 +02:00
/**
* Server . ConfigChanged , triggered each time a session ends
* which modified its configuration
*/
EmitSignal0 configChanged ;
2009-12-21 03:03:00 +01:00
/** Server.InfoRequest */
EmitSignal6 < const std : : string & ,
const DBusObject_t & ,
const std : : string & ,
const std : : string & ,
const std : : string & ,
const std : : map < string , string > & > infoRequest ;
2010-03-18 06:38:06 +01:00
/** Server.LogOutput */
EmitSignal3 < const DBusObject_t & ,
2010-03-26 21:41:07 +01:00
string ,
2010-03-18 06:38:06 +01:00
const std : : string & > logOutput ;
2010-02-03 02:50:56 +01:00
friend class Session ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
PresenceStatus m_presence ;
2010-04-01 04:15:20 +02:00
ConnmanClient m_connman ;
2010-02-03 02:50:56 +01:00
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
/** manager to automatic sync */
AutoSyncManager m_autoSync ;
2010-03-02 06:34:42 +01:00
//automatic termination
AutoTerm m_autoTerm ;
2010-03-18 06:38:06 +01:00
//records the parent logger, dbus server acts as logger to
//send signals to clients and put logs in the parent logger.
LoggerBase & m_parentLogger ;
2010-09-30 14:21:49 +02:00
/**
* All active timeouts created by addTimeout ( ) .
* Each timeout which requests to be not called
* again will be removed from this list .
*/
list < boost : : shared_ptr < Timeout > > m_timeouts ;
/**
* called each time a timeout triggers ,
* removes those which are done
*/
bool callTimeout ( const boost : : shared_ptr < Timeout > & timeout , const boost : : function < bool ( ) > & callback ) ;
2009-09-08 19:11:49 +02:00
public :
2010-01-13 02:47:42 +01:00
DBusServer ( GMainLoop * loop , const DBusConnectionPtr & conn , int duration ) ;
2009-09-08 19:11:49 +02:00
~ DBusServer ( ) ;
2009-09-14 12:58:22 +02:00
/** access to the GMainLoop reference used by this DBusServer instance */
GMainLoop * getLoop ( ) { return m_loop ; }
/** process D-Bus calls until the server is ready to quit */
void run ( ) ;
2009-09-08 19:11:49 +02:00
/**
* look up client by its ID
*/
boost : : shared_ptr < Client > findClient ( const Caller_t & ID ) ;
/**
* find client by its ID or create one anew
*/
boost : : shared_ptr < Client > addClient ( const DBusConnectionPtr & conn ,
const Caller_t & ID ,
const boost : : shared_ptr < Watch > & watch ) ;
2009-09-17 21:07:55 +02:00
/** detach this resource from all clients which own it */
void detach ( Resource * resource ) ;
2009-09-08 19:11:49 +02:00
/**
* Enqueue a session . Might also make it ready immediately ,
* if nothing else is first in the queue . To be called
* by the creator of the session , * after * the session is
* ready to run .
*/
void enqueue ( const boost : : shared_ptr < Session > & session ) ;
2009-11-09 21:10:10 +01:00
/**
* Remove all sessions with this device ID from the
* queue . If the active session also has this ID ,
* the session will be aborted and / or deactivated .
*/
int killSessions ( const std : : string & peerDeviceID ) ;
2009-09-08 19:11:49 +02:00
/**
* Remove a session from the work queue . If it is running a sync ,
* it will keep running and nothing will change . Otherwise , if it
* is " ready " ( = holds a lock on its configuration ) , then release
* that lock .
*/
void dequeue ( Session * session ) ;
/**
* Checks whether the server is ready to run another session
* and if so , activates the first one in the queue .
*/
void checkQueue ( ) ;
2009-12-21 03:03:00 +01:00
2010-09-30 14:21:49 +02:00
/**
* Invokes the given callback once in the given amount of seconds .
* Keeps a copy of the callback . If the DBusServer is destructed
* before that time , then the callback will be deleted without
* being called .
*/
void addTimeout ( const boost : : function < bool ( ) > & callback ,
int seconds ) ;
2009-12-21 03:03:00 +01:00
boost : : shared_ptr < InfoReq > createInfoReq ( const string & type ,
const std : : map < string , string > & parameters ,
const Session * session ) ;
2010-01-13 02:47:42 +01:00
void autoTermRef ( int counts = 1 ) { m_autoTerm . ref ( counts ) ; }
void autoTermUnref ( int counts = 1 ) { m_autoTerm . unref ( counts ) ; }
2009-12-21 03:03:00 +01:00
2010-01-13 02:47:42 +01:00
/** callback to reset for auto termination checking */
void autoTermCallback ( ) { m_autoTerm . reset ( ) ; }
2010-02-03 02:50:56 +01:00
/** poll_nm callback for connman, used for presence detection*/
2010-02-03 14:21:54 +01:00
void connmanCallback ( const std : : map < std : : string , boost : : variant < std : : vector < std : : string > > > & props , const string & error ) ;
2010-02-03 02:50:56 +01:00
PresenceStatus & getPresenceStatus ( ) { return m_presence ; }
2010-02-23 05:51:42 +01:00
void clearPeerTempls ( ) { m_matchedTempls . clear ( ) ; }
2010-02-03 14:17:37 +01:00
void addPeerTempl ( const string & templName , const boost : : shared_ptr < SyncConfig : : TemplateDescription > peerTempl ) ;
boost : : shared_ptr < SyncConfig : : TemplateDescription > getPeerTempl ( const string & peer ) ;
2010-02-23 05:06:05 +01:00
/**
* methods to operate device list . See DeviceList definition .
* The device id here is the identifier of device , the same as definition in DeviceList .
* In bluetooth devices , it refers to actually the mac address of the bluetooth .
* The finger print and match mode is used to match templates .
*/
/** get sync devices */
void getDeviceList ( SyncConfig : : DeviceList & devices ) ;
/** get a device according to device id. If not found, return false. */
bool getDevice ( const string & deviceId , SyncConfig : : DeviceDescription & device ) ;
/** add a device */
void addDevice ( const SyncConfig : : DeviceDescription & device ) ;
/** remove a device by device id. If not found, do nothing */
void removeDevice ( const string & deviceId ) ;
/** update a device with the given device information. If not found, do nothing */
void updateDevice ( const string & deviceId , const SyncConfig : : DeviceDescription & device ) ;
2009-03-30 17:54:47 +02:00
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
/** emit a presence signal */
void emitPresence ( const string & server , const string & status , const string & transport )
{
presence ( server , status , transport ) ;
}
/**
* Returns new unique session ID . Implemented with a running
* counter . Checks for overflow , but not currently for active
* sessions .
*/
std : : string getNextSession ( ) ;
AutoSyncManager & getAutoSyncManager ( ) { return m_autoSync ; }
2010-03-18 06:38:06 +01:00
2010-08-25 14:54:11 +02:00
/**
* false if any client requested suppression of notifications
*/
bool notificationsEnabled ( ) ;
2010-03-18 06:38:06 +01:00
/**
* implement virtual method from LogStdout .
* Not only print the message in the console
* but also send them as signals to clients
*/
virtual void messagev ( Level level ,
const char * prefix ,
const char * file ,
int line ,
const char * function ,
const char * format ,
va_list args ) ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
} ;
2009-03-30 21:34:01 +02:00
2009-09-08 19:11:49 +02:00
/**
* Tracks a single client and all sessions and connections that it is
* connected to . Referencing them ensures that they stay around as
* long as needed .
*/
class Client
{
2010-09-30 14:27:02 +02:00
DBusServer & m_server ;
2009-09-08 19:11:49 +02:00
typedef std : : list < boost : : shared_ptr < Resource > > Resources_t ;
Resources_t m_resources ;
2010-08-25 14:54:11 +02:00
/** counts how often a client has called Attach() without Detach() */
int m_attachCount ;
/** current client setting for notifications (see HAS_NOTIFY) */
bool m_notificationsEnabled ;
2010-09-30 14:27:02 +02:00
/** called 1 minute after last client detached from a session */
static bool sessionExpired ( const boost : : shared_ptr < Session > & session ) ;
2009-09-08 19:11:49 +02:00
public :
const Caller_t m_ID ;
2010-09-30 14:27:02 +02:00
Client ( DBusServer & server ,
const Caller_t & ID ) :
m_server ( server ) ,
2010-08-25 14:54:11 +02:00
m_attachCount ( 0 ) ,
m_notificationsEnabled ( true ) ,
2009-09-08 19:11:49 +02:00
m_ID ( ID )
{ }
2010-09-30 14:27:02 +02:00
~ Client ( ) ;
2010-08-25 14:54:11 +02:00
void increaseAttachCount ( ) { + + m_attachCount ; }
void decreaseAttachCount ( ) { - - m_attachCount ; }
int getAttachCount ( ) const { return m_attachCount ; }
void setNotificationsEnabled ( bool enabled ) { m_notificationsEnabled = enabled ; }
bool getNotificationsEnabled ( ) const { return m_notificationsEnabled ; }
2009-09-08 19:11:49 +02:00
/**
* Attach a specific resource to this client . As long as the
* resource is attached , it cannot be freed . Can be called
* multiple times , which means that detach ( ) also has to be called
* the same number of times to finally detach the resource .
*/
void attach ( boost : : shared_ptr < Resource > resource )
{
m_resources . push_back ( resource ) ;
}
/**
* Detach once from the given resource . Has to be called as
* often as attach ( ) to really remove all references to the
* session . It ' s an error to call detach ( ) more often than
* attach ( ) .
*/
2010-09-30 14:27:02 +02:00
void detach ( Resource * resource ) ;
2009-09-08 19:11:49 +02:00
void detach ( boost : : shared_ptr < Resource > resource )
{
detach ( resource . get ( ) ) ;
}
2009-09-17 21:07:55 +02:00
/**
* Remove all references to the given resource , regardless whether
* it was referenced not at all or multiple times .
*/
void detachAll ( Resource * resource ) {
Resources_t : : iterator it = m_resources . begin ( ) ;
while ( it ! = m_resources . end ( ) ) {
if ( it - > get ( ) = = resource ) {
it = m_resources . erase ( it ) ;
} else {
+ + it ;
}
}
}
void detachAll ( boost : : shared_ptr < Resource > resource )
{
detachAll ( resource . get ( ) ) ;
}
2009-09-08 19:11:49 +02:00
/**
* return corresponding smart pointer for a certain resource ,
* empty pointer if not found
*/
boost : : shared_ptr < Resource > findResource ( Resource * resource )
{
for ( Resources_t : : iterator it = m_resources . begin ( ) ;
it ! = m_resources . end ( ) ;
+ + it ) {
if ( it - > get ( ) = = resource ) {
// got it
return * it ;
}
}
return boost : : shared_ptr < Resource > ( ) ;
}
} ;
2009-04-04 22:39:11 +02:00
2009-09-14 12:58:22 +02:00
struct SourceStatus
{
SourceStatus ( ) :
m_mode ( " none " ) ,
m_status ( " idle " ) ,
m_error ( 0 )
{ }
2009-10-09 09:22:13 +02:00
void set ( const std : : string & mode , const std : : string & status , uint32_t error )
{
m_mode = mode ;
m_status = status ;
m_error = error ;
}
2009-09-14 12:58:22 +02:00
std : : string m_mode ;
std : : string m_status ;
uint32_t m_error ;
} ;
template < > struct dbus_traits < SourceStatus > :
public dbus_struct_traits < SourceStatus ,
dbus_member < SourceStatus , std : : string , & SourceStatus : : m_mode ,
dbus_member < SourceStatus , std : : string , & SourceStatus : : m_status ,
dbus_member_single < SourceStatus , uint32_t , & SourceStatus : : m_error > > > >
{ } ;
struct SourceProgress
{
SourceProgress ( ) :
m_phase ( " " ) ,
m_prepareCount ( - 1 ) , m_prepareTotal ( - 1 ) ,
m_sendCount ( - 1 ) , m_sendTotal ( - 1 ) ,
m_receiveCount ( - 1 ) , m_receiveTotal ( - 1 )
{ }
std : : string m_phase ;
int32_t m_prepareCount , m_prepareTotal ;
int32_t m_sendCount , m_sendTotal ;
int32_t m_receiveCount , m_receiveTotal ;
} ;
template < > struct dbus_traits < SourceProgress > :
public dbus_struct_traits < SourceProgress ,
dbus_member < SourceProgress , std : : string , & SourceProgress : : m_phase ,
dbus_member < SourceProgress , int32_t , & SourceProgress : : m_prepareCount ,
dbus_member < SourceProgress , int32_t , & SourceProgress : : m_prepareTotal ,
dbus_member < SourceProgress , int32_t , & SourceProgress : : m_sendCount ,
dbus_member < SourceProgress , int32_t , & SourceProgress : : m_sendTotal ,
dbus_member < SourceProgress , int32_t , & SourceProgress : : m_receiveCount ,
dbus_member_single < SourceProgress , int32_t , & SourceProgress : : m_receiveTotal > > > > > > > >
{ } ;
2010-02-01 10:23:43 +01:00
/**
* This class is mainly to implement two virtual functions ' askPassword '
* and ' savePassword ' of ConfigUserInterface . The main functionality is
* to only get and save passwords in the gnome keyring .
*/
class DBusUserInterface : public SyncContext
{
public :
DBusUserInterface ( const std : : string & config ) ;
/*
* Ask password from gnome keyring , if not found , empty string
* is returned
*/
string askPassword ( const string & passwordName ,
const string & descr ,
const ConfigPasswordKey & key ) ;
//save password to gnome keyring, if not successful, false is returned.
bool savePassword ( const string & passwordName ,
const string & password ,
const ConfigPasswordKey & key ) ;
2010-06-11 15:41:31 +02:00
/**
* Read stdin via InfoRequest / Response .
*/
void readStdin ( string & content ) ;
2010-02-01 10:23:43 +01:00
} ;
2009-09-14 12:58:22 +02:00
/**
* A running sync engine which keeps answering on D - Bus whenever
* possible and updates the Session while the sync runs .
*/
2010-02-01 10:23:43 +01:00
class DBusSync : public DBusUserInterface
2009-09-14 12:58:22 +02:00
{
Session & m_session ;
public :
2009-09-14 21:54:43 +02:00
DBusSync ( const std : : string & config ,
2009-09-14 12:58:22 +02:00
Session & session ) ;
2009-09-17 21:07:55 +02:00
~ DBusSync ( ) { }
2009-09-14 12:58:22 +02:00
2009-10-30 07:25:53 +01:00
protected :
2009-09-14 12:58:22 +02:00
virtual boost : : shared_ptr < TransportAgent > createTransportAgent ( ) ;
virtual void displaySyncProgress ( sysync : : TProgressEventEnum type ,
int32_t extra1 , int32_t extra2 , int32_t extra3 ) ;
virtual void displaySourceProgress ( sysync : : TProgressEventEnum type ,
SyncSource & source ,
int32_t extra1 , int32_t extra2 , int32_t extra3 ) ;
2009-12-07 07:26:01 +01:00
virtual void reportStepCmd ( sysync : : uInt16 stepCmd ) ;
2010-03-04 02:53:04 +01:00
/** called when a sync is successfully started */
virtual void syncSuccessStart ( ) ;
2009-10-15 09:41:14 +02:00
/**
2009-10-30 07:25:53 +01:00
* Implement checkForSuspend and checkForAbort .
2009-10-15 09:41:14 +02:00
* They will check whether dbus clients suspend
2009-10-30 07:25:53 +01:00
* or abort the session in addition to checking
* whether suspend / abort were requested via
* signals , using SyncContext ' s signal handling .
2009-10-15 09:41:14 +02:00
*/
virtual bool checkForSuspend ( ) ;
virtual bool checkForAbort ( ) ;
virtual int sleep ( int intervals ) ;
2009-12-08 02:49:51 +01:00
/**
2010-02-01 10:23:43 +01:00
* Implement askPassword to retrieve password in gnome - keyring .
* If not found , then ask it from dbus clients .
2009-12-08 02:49:51 +01:00
*/
string askPassword ( const string & passwordName ,
const string & descr ,
const ConfigPasswordKey & key ) ;
2009-09-14 12:58:22 +02:00
} ;
2009-10-09 09:22:13 +02:00
/**
* Hold progress info and try to estimate current progress
*/
class ProgressData {
public :
/**
* big steps , each step contains many operations , such as
* data prepare , message send / receive .
* The partitions of these steps are based on profiling data
* for many usage scenarios and different sync modes
*/
enum ProgressStep {
/** an invalid step */
PRO_SYNC_INVALID = 0 ,
/**
* sync prepare step : do some preparations and checkings ,
* such as source preparation , engine preparation
*/
PRO_SYNC_PREPARE ,
/**
* session init step : transport connection set up ,
* start a session , authentication and dev info generation
* normally it needs one time syncML messages send - receive .
* Sometimes it may need messages send / receive many times to
* handle authentication
*/
PRO_SYNC_INIT ,
/**
* prepare sync data and send data , also receive data from server .
* Also may need send / receive messages more than one time if too
* much data .
* assume 5 items to be sent by default
*/
PRO_SYNC_DATA ,
/**
* item receive handling , send client ' s status to server and
* close the session
* assume 5 items to be received by default
*/
PRO_SYNC_UNINIT ,
/** number of sync steps */
PRO_SYNC_TOTAL
} ;
/**
* internal mode to represent whether it is possible that data is sent to
* server or received from server . This could help remove some incorrect
* hypothesis . For example , if only to client , then it is no data item
* sending to server .
*/
enum InternalMode {
INTERNAL_NONE = 0 ,
INTERNAL_ONLY_TO_CLIENT = 1 ,
INTERNAL_ONLY_TO_SERVER = 1 < < 1 ,
INTERNAL_TWO_WAY = 1 + ( 1 < < 1 )
} ;
/**
* treat a one - time send - receive without data items
* as an internal standard unit .
* below are ratios of other operations compared to one
* standard unit .
* These ratios might be dynamicall changed in the future .
*/
/** PRO_SYNC_PREPARE step ratio to standard unit */
static const float PRO_SYNC_PREPARE_RATIO = 0.2 ;
/** data prepare for data items to standard unit. All are combined by profiling data */
static const float DATA_PREPARE_RATIO = 0.10 ;
/** one data item send's ratio to standard unit */
static const float ONEITEM_SEND_RATIO = 0.05 ;
/** one data item receive&parse's ratio to standard unit */
static const float ONEITEM_RECEIVE_RATIO = 0.05 ;
/** connection setup to standard unit */
static const float CONN_SETUP_RATIO = 0.5 ;
/** assume the number of data items */
static const int DEFAULT_ITEMS = 5 ;
/** default times of message send/receive in each step */
static const int MSG_SEND_RECEIVE_TIMES = 1 ;
ProgressData ( int32_t & progress ) ;
/**
* change the big step
*/
void setStep ( ProgressStep step ) ;
/**
* calc progress when a message is sent
*/
void sendStart ( ) ;
/**
* calc progress when a message is received from server
*/
void receiveEnd ( ) ;
/**
* re - calc progress proportions according to syncmode hint
* typically , if only refresh - from - client , then
* client won ' t receive data items .
*/
void addSyncMode ( SyncMode mode ) ;
/**
* calc progress when data prepare for sending
*/
void itemPrepare ( ) ;
/**
* calc progress when a data item is received
*/
void itemReceive ( const string & source , int count , int total ) ;
private :
/** update progress data */
void updateProg ( float ratio ) ;
/** dynamically adapt the proportion of each step by their current units */
void recalc ( ) ;
/** internally check sync mode */
void checkInternalMode ( ) ;
/** get total units of current step and remaining steps */
float getRemainTotalUnits ( ) ;
/** get default units of given step */
static float getDefaultUnits ( ProgressStep step ) ;
private :
/** a reference of progress percentage */
int32_t & m_progress ;
/** current big step */
ProgressStep m_step ;
/** count of message send/receive in current step. Cleared in the start of a new step */
int m_sendCounts ;
/** internal sync mode combinations */
int m_internalMode ;
/** proportions when each step is end */
float m_syncProp [ PRO_SYNC_TOTAL ] ;
/** remaining units of each step according to current step */
float m_syncUnits [ PRO_SYNC_TOTAL ] ;
/** proportion of a standard unit, may changes dynamically */
float m_propOfUnit ;
/** current sync source */
string m_source ;
} ;
2010-03-19 02:20:46 +01:00
class CmdlineWrapper ;
2009-09-08 19:11:49 +02:00
/**
* Represents and implements the Session interface . Use
* boost : : shared_ptr to track it and ensure that there are references
* to it as long as the connection is needed .
*/
2009-09-14 12:58:22 +02:00
class Session : public DBusObjectHelper ,
public Resource ,
2009-11-26 09:37:19 +01:00
private ReadOperations ,
2009-09-14 12:58:22 +02:00
private boost : : noncopyable
2009-04-04 22:39:11 +02:00
{
2009-09-08 19:11:49 +02:00
DBusServer & m_server ;
2010-08-25 10:37:58 +02:00
std : : vector < std : : string > m_flags ;
2009-09-27 22:48:04 +02:00
const std : : string m_sessionID ;
2009-11-09 21:10:10 +01:00
std : : string m_peerDeviceID ;
2009-09-27 22:48:04 +02:00
bool m_serverMode ;
SharedBuffer m_initialMessage ;
string m_initialMessageType ;
2009-09-08 19:11:49 +02:00
boost : : weak_ptr < Connection > m_connection ;
2009-09-17 21:07:55 +02:00
std : : string m_connectionError ;
bool m_useConnection ;
2009-04-04 22:39:11 +02:00
2009-09-14 22:33:30 +02:00
/** temporary config changes */
FilterConfigNode : : ConfigFilter m_syncFilter ;
FilterConfigNode : : ConfigFilter m_sourceFilter ;
2009-11-26 09:37:19 +01:00
typedef std : : map < std : : string , FilterConfigNode : : ConfigFilter > SourceFilters_t ;
SourceFilters_t m_sourceFilters ;
/** whether dbus clients set temporary configs */
bool m_tempConfig ;
2009-09-14 22:33:30 +02:00
2010-08-25 21:03:31 +02:00
/**
* whether the dbus clients updated , removed or cleared configs ,
* ignoring temporary configuration changes
*/
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
bool m_setConfig ;
2009-09-09 12:52:54 +02:00
/**
* True while clients are allowed to make calls other than Detach ( ) ,
* which is always allowed . Some calls are not allowed while this
* session runs a sync , which is indicated by a non - NULL m_sync
* pointer .
*/
2009-09-08 19:11:49 +02:00
bool m_active ;
2009-09-09 12:52:54 +02:00
2010-09-30 14:27:02 +02:00
/**
* True once done ( ) was called .
*/
bool m_done ;
2010-03-12 09:34:28 +01:00
/**
* Indicates whether this session was initiated by the peer or locally .
*/
bool m_remoteInitiated ;
2009-09-09 12:52:54 +02:00
/**
* The SyncEvolution instance which currently prepares or runs a sync .
*/
2009-09-14 12:58:22 +02:00
boost : : shared_ptr < DBusSync > m_sync ;
2009-10-13 09:56:32 +02:00
/**
* the sync status for session
*/
enum SyncStatus {
2009-11-19 13:44:22 +01:00
SYNC_QUEUEING , ///< waiting to become ready for use
SYNC_IDLE , ///< ready, session is initiated but sync not started
2009-10-13 09:56:32 +02:00
SYNC_RUNNING , ///< sync is running
SYNC_ABORT , ///< sync is aborting
SYNC_SUSPEND , ///< sync is suspending
SYNC_DONE , ///< sync is done
SYNC_ILLEGAL
} ;
2009-09-14 12:58:22 +02:00
2009-10-13 09:56:32 +02:00
/** current sync status */
SyncStatus m_syncStatus ;
2009-09-09 12:52:54 +02:00
2009-12-07 07:26:01 +01:00
/** step info: whether engine is waiting for something */
bool m_stepIsWaiting ;
2009-09-09 12:52:54 +02:00
/**
* Priority which determines position in queue .
* Lower is more important . PRI_DEFAULT is zero .
*/
2009-09-08 19:11:49 +02:00
int m_priority ;
2009-04-04 22:39:11 +02:00
2009-09-14 12:58:22 +02:00
int32_t m_progress ;
2009-10-09 09:22:13 +02:00
/** progress data, holding progress calculation related info */
ProgressData m_progData ;
2009-09-14 12:58:22 +02:00
typedef std : : map < std : : string , SourceStatus > SourceStatuses_t ;
SourceStatuses_t m_sourceStatus ;
uint32_t m_error ;
typedef std : : map < std : : string , SourceProgress > SourceProgresses_t ;
SourceProgresses_t m_sourceProgress ;
2009-10-12 08:16:22 +02:00
/** timer for fire status/progress usages */
Timer m_statusTimer ;
Timer m_progressTimer ;
2009-12-14 06:59:02 +01:00
/** restore used */
string m_restoreDir ;
bool m_restoreBefore ;
2009-12-22 09:47:31 +01:00
/** the total number of sources to be restored */
int m_restoreSrcTotal ;
/** the number of sources that have been restored */
int m_restoreSrcEnd ;
2009-12-14 06:59:02 +01:00
enum RunOperation {
OP_SYNC = 0 ,
2010-03-15 02:33:43 +01:00
OP_RESTORE = 1 ,
OP_CMDLINE = 2 ,
OP_NULL
2009-12-14 06:59:02 +01:00
} ;
static string runOpToString ( RunOperation op ) ;
RunOperation m_runOperation ;
2010-03-04 02:53:04 +01:00
/** listener to listen to changes of sync */
SessionListener * m_listener ;
2010-03-15 02:33:43 +01:00
/** Cmdline to execute command line args */
boost : : shared_ptr < CmdlineWrapper > m_cmdline ;
2010-09-30 10:32:13 +02:00
/** Session.Attach() */
void attach ( const Caller_t & caller ) ;
2009-11-11 10:55:15 +01:00
/** Session.Detach() */
2009-09-14 12:58:22 +02:00
void detach ( const Caller_t & caller ) ;
2009-04-04 22:39:11 +02:00
2009-11-11 10:55:15 +01:00
/** Session.GetStatus() */
2009-09-14 12:58:22 +02:00
void getStatus ( std : : string & status ,
uint32_t & error ,
SourceStatuses_t & sources ) ;
2009-11-11 10:55:15 +01:00
/** Session.GetProgress() */
2009-09-14 12:58:22 +02:00
void getProgress ( int32_t & progress ,
SourceProgresses_t & sources ) ;
2009-12-14 06:59:02 +01:00
/** Session.Restore() */
void restore ( const string & dir , bool before , const std : : vector < std : : string > & sources ) ;
2010-02-03 02:50:56 +01:00
/** Session.checkPresence() */
void checkPresence ( string & status ) ;
2010-03-15 02:33:43 +01:00
/** Session.Execute() */
2010-04-15 10:26:41 +02:00
void execute ( const vector < string > & args , const map < string , string > & vars ) ;
2010-03-15 02:33:43 +01:00
2009-09-14 12:58:22 +02:00
/**
* Must be called each time that properties changing the
* overall status are changed . Ensures that the corresponding
* D - Bus signal is sent .
*
* Doesn ' t always send the signal immediately , because often it is
* likely that more status changes will follow shortly . To ensure
* that the " final " status is sent , call with flush = true .
*
* @ param flush force sending the current status
*/
void fireStatus ( bool flush = false ) ;
/** like fireStatus() for progress information */
void fireProgress ( bool flush = false ) ;
2009-11-11 10:55:15 +01:00
/** Session.StatusChanged */
2009-09-14 12:58:22 +02:00
EmitSignal3 < const std : : string & ,
uint32_t ,
const SourceStatuses_t & > emitStatus ;
2009-11-11 10:55:15 +01:00
/** Session.ProgressChanged */
2009-09-14 12:58:22 +02:00
EmitSignal2 < int32_t ,
const SourceProgresses_t & > emitProgress ;
2009-10-13 09:56:32 +02:00
static string syncStatusToString ( SyncStatus state ) ;
2009-09-08 19:11:49 +02:00
public :
2010-09-30 10:32:13 +02:00
/**
* Sessions must always be held in a shared pointer
* because some operations depend on that . This
* constructor function here ensures that and
* also adds a weak pointer to the instance itself ,
* so that it can create more shared pointers as
* needed .
*/
static boost : : shared_ptr < Session > createSession ( DBusServer & server ,
const std : : string & peerDeviceID ,
const std : : string & config_name ,
const std : : string & session ,
const std : : vector < std : : string > & flags = std : : vector < std : : string > ( ) ) ;
2010-09-30 14:27:02 +02:00
/**
* automatically marks the session as completed before deleting it
*/
2010-09-30 10:32:13 +02:00
~ Session ( ) ;
2010-09-30 14:27:02 +02:00
/** explicitly mark the session as completed, even if it doesn't get deleted yet */
void done ( ) ;
2010-09-30 10:32:13 +02:00
private :
2009-09-08 19:11:49 +02:00
Session ( DBusServer & server ,
2009-11-09 21:10:10 +01:00
const std : : string & peerDeviceID ,
2009-09-09 12:52:54 +02:00
const std : : string & config_name ,
2010-08-25 10:37:58 +02:00
const std : : string & session ,
const std : : vector < std : : string > & flags = std : : vector < std : : string > ( ) ) ;
2010-09-30 10:32:13 +02:00
boost : : weak_ptr < Session > m_me ;
2009-04-04 22:39:11 +02:00
2010-09-30 10:32:13 +02:00
public :
2009-09-08 19:11:49 +02:00
enum {
2010-03-15 02:33:43 +01:00
PRI_CMDLINE = - 10 ,
2009-09-08 19:11:49 +02:00
PRI_DEFAULT = 0 ,
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
PRI_CONNECTION = 10 ,
PRI_AUTOSYNC = 20
2009-09-08 19:11:49 +02:00
} ;
2009-07-08 22:44:13 +02:00
2009-09-08 19:11:49 +02:00
/**
* Default priority is 0. Higher means less important .
*/
void setPriority ( int priority ) { m_priority = priority ; }
int getPriority ( ) const { return m_priority ; }
2009-04-24 12:21:18 +02:00
2009-09-27 22:48:04 +02:00
void initServer ( SharedBuffer data , const std : : string & messageType ) ;
2009-09-17 21:07:55 +02:00
void setConnection ( const boost : : shared_ptr < Connection > c ) { m_connection = c ; m_useConnection = c ; }
2009-09-08 19:11:49 +02:00
boost : : weak_ptr < Connection > getConnection ( ) { return m_connection ; }
2009-09-17 21:07:55 +02:00
bool useConnection ( ) { return m_useConnection ; }
/**
* After the connection closes , the Connection instance is
* destructed immediately . This is necessary so that the
* corresponding cleanup can remove all other classes
* only referenced by the Connection .
*
* This leads to the problem that an active sync cannot
* query the final error code of the connection . This
* is solved by setting a generic error code here when
* the sync starts and overwriting it when the connection
* closes .
*/
void setConnectionError ( const std : : string error ) { m_connectionError = error ; }
std : : string getConnectionError ( ) { return m_connectionError ; }
2009-04-24 12:21:18 +02:00
2009-09-14 12:58:22 +02:00
DBusServer & getServer ( ) { return m_server ; }
2009-11-26 09:37:19 +01:00
std : : string getConfigName ( ) { return m_configName ; }
2009-11-09 21:10:10 +01:00
std : : string getSessionID ( ) const { return m_sessionID ; }
std : : string getPeerDeviceID ( ) const { return m_peerDeviceID ; }
2009-09-14 12:58:22 +02:00
/**
* TRUE if the session is ready to take over control
*/
2010-03-15 02:33:43 +01:00
bool readyToRun ( ) { return ( m_syncStatus ! = SYNC_DONE ) & & ( m_runOperation ! = OP_NULL ) ; }
2009-09-14 12:58:22 +02:00
/**
* transfer control to the session for the duration of the sync ,
* returns when the sync is done ( successfully or unsuccessfully )
*/
void run ( ) ;
2009-09-08 19:11:49 +02:00
/**
* called when the session is ready to run ( true ) or
* lost the right to make changes ( false )
*/
void setActive ( bool active ) ;
2009-09-14 12:58:22 +02:00
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
bool getActive ( ) { return m_active ; }
2009-09-14 12:58:22 +02:00
void syncProgress ( sysync : : TProgressEventEnum type ,
int32_t extra1 , int32_t extra2 , int32_t extra3 ) ;
void sourceProgress ( sysync : : TProgressEventEnum type ,
SyncSource & source ,
int32_t extra1 , int32_t extra2 , int32_t extra3 ) ;
2009-12-24 08:20:25 +01:00
string askPassword ( const string & passwordName ,
const string & descr ,
const ConfigPasswordKey & key ) ;
2009-09-17 21:07:55 +02:00
2010-08-25 10:37:58 +02:00
/** Session.GetFlags() */
std : : vector < std : : string > getFlags ( ) { return m_flags ; }
2010-08-25 15:21:08 +02:00
/** Session.GetConfigName() */
std : : string getNormalConfigName ( ) { return SyncConfig : : normalizeConfigString ( m_configName ) ; }
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
/** Session.SetConfig() */
void setConfig ( bool update , bool temporary ,
const ReadOperations : : Config_t & config ) ;
2009-09-27 22:48:04 +02:00
typedef StringMap SourceModes_t ;
2009-11-11 10:55:15 +01:00
/** Session.Sync() */
void sync ( const std : : string & mode , const SourceModes_t & source_modes ) ;
/** Session.Abort() */
2009-09-17 21:07:55 +02:00
void abort ( ) ;
2009-11-11 10:55:15 +01:00
/** Session.Suspend() */
2009-09-17 21:07:55 +02:00
void suspend ( ) ;
2009-10-15 09:41:14 +02:00
2009-10-30 07:25:53 +01:00
bool isSuspend ( ) { return m_syncStatus = = SYNC_SUSPEND ; }
bool isAbort ( ) { return m_syncStatus = = SYNC_ABORT ; }
2009-11-26 09:37:19 +01:00
2009-12-07 07:26:01 +01:00
/**
* step info for engine : whether the engine is blocked by something
* If yes , ' waiting ' will be appended as specifiers in the status string .
* see GetStatus documentation .
*/
void setStepInfo ( bool isWaiting ) ;
2010-03-04 02:53:04 +01:00
/** sync is successfully started */
void syncSuccessStart ( ) ;
/**
* add a listener of the session . Old set listener is returned
*/
SessionListener * addListener ( SessionListener * listener ) ;
2010-03-12 09:34:28 +01:00
void setRemoteInitiated ( bool remote ) { m_remoteInitiated = remote ; }
2009-11-26 09:37:19 +01:00
private :
/** set m_syncFilter and m_sourceFilters to config */
2009-12-25 09:17:58 +01:00
virtual bool setFilters ( SyncConfig & config ) ;
2009-09-08 19:11:49 +02:00
} ;
2009-04-16 17:16:49 +02:00
2010-03-19 02:20:46 +01:00
/**
* a wrapper to maintain the execution of command line
* arguments from dbus clients . It is in charge of
* redirecting output of cmd line to logging system .
*/
class CmdlineWrapper
{
/**
* inherit from stream buf to redirect the output .
* Set a log until we gets a ' \n ' separator since we know
* the command line message often ends with ' \n ' . The reason
* is to avoid setting less characters in one log and thus
* sending many signals to dbus clients .
*/
class CmdlineStreamBuf : public std : : streambuf
{
public :
virtual ~ CmdlineStreamBuf ( )
{
//flush cached characters
if ( ! m_str . empty ( ) ) {
SE_LOG ( LoggerBase : : SHOW , NULL , NULL , " %s " , m_str . c_str ( ) ) ;
}
}
protected :
/**
* inherit from std : : streambuf , all characters are cached in m_str
* until a character ' \n ' is reached .
*/
virtual int_type overflow ( int_type ch ) {
if ( ch = = ' \n ' ) {
//don't append this character for logging system will append it
SE_LOG ( LoggerBase : : SHOW , NULL , NULL , " %s " , m_str . c_str ( ) ) ;
m_str . clear ( ) ;
} else if ( ch ! = EOF ) {
m_str + = ch ;
}
return ch ;
}
/** the cached output characters */
string m_str ;
} ;
/** streambuf used for m_cmdlineOutStream */
CmdlineStreamBuf m_outStreamBuf ;
/** stream for command line out and err arguments */
std : : ostream m_cmdlineOutStream ;
2010-03-15 02:33:43 +01:00
/**
* implement factory method to create DBusSync instances
* This can check ' abort ' and ' suspend ' command from clients .
*/
class DBusCmdline : public Cmdline {
Session & m_session ;
public :
DBusCmdline ( Session & session ,
const vector < string > & args ,
ostream & out ,
ostream & err )
: Cmdline ( args , out , err ) , m_session ( session )
{ }
SyncContext * createSyncClient ( ) {
return new DBusSync ( m_server , m_session ) ;
}
} ;
2010-03-19 02:20:46 +01:00
/** instance to run command line arguments */
2010-03-15 02:33:43 +01:00
DBusCmdline m_cmdline ;
2010-04-15 10:26:41 +02:00
/** environment variables passed from client */
map < string , string > m_envVars ;
2010-03-19 02:20:46 +01:00
public :
/**
* constructor to create cmdline instance .
* Here just one stream is used and error message in
* command line is output to this stream for it is
* different from Logger : : ERROR .
*/
2010-04-15 10:26:41 +02:00
CmdlineWrapper ( Session & session ,
const vector < string > & args ,
const map < string , string > & vars )
2010-03-19 02:20:46 +01:00
: m_cmdlineOutStream ( & m_outStreamBuf ) ,
2010-04-15 10:26:41 +02:00
m_cmdline ( session , args , m_cmdlineOutStream , m_cmdlineOutStream ) ,
m_envVars ( vars )
2010-03-19 02:20:46 +01:00
{ }
2010-04-06 04:59:01 +02:00
bool parse ( ) { return m_cmdline . parse ( ) ; }
2010-03-26 22:05:48 +01:00
void run ( )
{
2010-04-15 10:26:41 +02:00
//temporarily set environment variables and restore them after running
list < boost : : shared_ptr < ScopedEnvChange > > changes ;
BOOST_FOREACH ( const StringPair & var , m_envVars ) {
changes . push_back ( boost : : shared_ptr < ScopedEnvChange > ( new ScopedEnvChange ( var . first , var . second ) ) ) ;
}
2010-03-26 22:05:48 +01:00
// exceptions must be handled (= printed) before returning,
// so that our client gets the output
try {
2010-05-18 05:19:02 +02:00
if ( ! m_cmdline . run ( ) ) {
SE_THROW_EXCEPTION ( DBusSyncException , " command line execution failure " ) ;
}
2010-03-26 22:05:48 +01:00
} catch ( . . . ) {
2010-04-14 10:38:05 +02:00
redirectPtr - > flush ( ) ;
throw ;
2010-03-26 22:05:48 +01:00
}
2010-03-31 20:04:28 +02:00
// always forward all currently pending redirected output
// before closing the session
redirectPtr - > flush ( ) ;
2010-03-26 22:05:48 +01:00
}
2010-08-25 21:03:31 +02:00
bool configWasModified ( ) { return m_cmdline . configWasModified ( ) ; }
2010-03-19 02:20:46 +01:00
} ;
2009-04-23 13:13:17 +02:00
2009-09-08 19:11:49 +02:00
/**
* Represents and implements the Connection interface .
*
* The connection interacts with a Session by creating the Session and
* exchanging data with it . For that , the connection registers itself
* with the Session and unregisters again when it goes away .
*
* In contrast to clients , the Session only keeps a weak_ptr , which
* becomes invalid when the referenced object gets deleted . Typically
* this means the Session has to abort , unless reconnecting is
* supported .
*/
class Connection : public DBusObjectHelper , public Resource
{
DBusServer & m_server ;
2009-09-27 22:48:04 +02:00
StringMap m_peer ;
2009-09-08 19:11:49 +02:00
bool m_mustAuthenticate ;
enum {
SETUP , /**< ready for first message */
PROCESSING , /**< received message, waiting for engine's reply */
WAITING , /**< waiting for next follow-up message */
FINAL , /**< engine has sent final reply, wait for ACK by peer */
DONE , /**< peer has closed normally after the final reply */
FAILED /**< in a failed state, no further operation possible */
} m_state ;
std : : string m_failure ;
2009-09-24 12:54:36 +02:00
/** first parameter for Session::sync() */
std : : string m_syncMode ;
/** second parameter for Session::sync() */
Session : : SourceModes_t m_sourceModes ;
2009-09-23 09:44:01 +02:00
const std : : string m_sessionID ;
2009-09-08 19:11:49 +02:00
boost : : shared_ptr < Session > m_session ;
2009-09-17 21:07:55 +02:00
/**
* main loop that our DBusTransportAgent is currently waiting in ,
* NULL if not waiting
*/
GMainLoop * m_loop ;
2009-10-30 07:25:53 +01:00
/**
* get our peer session out of the DBusTransportAgent ,
* if it is currently waiting for us ( indicated via m_loop )
*/
void wakeupSession ( ) ;
2009-09-17 21:07:55 +02:00
/**
* buffer for received data , waiting here for engine to ask
* for it via DBusTransportAgent : : getReply ( ) .
*/
SharedBuffer m_incomingMsg ;
std : : string m_incomingMsgType ;
2010-03-12 02:16:45 +01:00
struct SANContent {
std : : vector < string > m_syncType ;
std : : vector < uint32_t > m_contentType ;
std : : vector < string > m_serverURI ;
} ;
/**
* The content of a parsed SAN package to be processed via
* connection . ready
*/
boost : : shared_ptr < SANContent > m_SANContent ;
std : : string m_peerBtAddr ;
2009-09-08 19:11:49 +02:00
/**
* records the reason for the failure , sends Abort signal and puts
* the connection into the FAILED state .
*/
void failed ( const std : : string & reason ) ;
/**
* returns " <description> (<ID> via <transport> <transport_description>) "
*/
2009-09-27 22:48:04 +02:00
static std : : string buildDescription ( const StringMap & peer ) ;
2009-09-08 19:11:49 +02:00
2009-11-11 10:55:15 +01:00
/** Connection.Process() */
2009-09-08 19:11:49 +02:00
void process ( const Caller_t & caller ,
const std : : pair < size_t , const uint8_t * > & message ,
const std : : string & message_type ) ;
2009-11-11 10:55:15 +01:00
/** Connection.Close() */
2009-09-08 19:11:49 +02:00
void close ( const Caller_t & caller ,
bool normal ,
const std : : string & error ) ;
2009-11-11 10:55:15 +01:00
/** wrapper around sendAbort */
2009-11-09 21:07:32 +01:00
void abort ( ) ;
2009-11-11 10:55:15 +01:00
/** Connection.Abort */
2009-11-05 17:55:02 +01:00
EmitSignal0 sendAbort ;
bool m_abortSent ;
2009-11-11 10:55:15 +01:00
/** Connection.Reply */
2009-09-08 19:11:49 +02:00
EmitSignal5 < const std : : pair < size_t , const uint8_t * > & ,
const std : : string & ,
2009-09-27 22:48:04 +02:00
const StringMap & ,
2009-09-08 19:11:49 +02:00
bool ,
2009-09-23 09:44:01 +02:00
const std : : string & > reply ;
2009-09-08 19:11:49 +02:00
2009-09-17 21:07:55 +02:00
friend class DBusTransportAgent ;
2009-09-08 19:11:49 +02:00
public :
const std : : string m_description ;
Connection ( DBusServer & server ,
const DBusConnectionPtr & conn ,
2009-09-23 09:44:01 +02:00
const std : : string & session_num ,
2009-09-27 22:48:04 +02:00
const StringMap & peer ,
2009-09-08 19:11:49 +02:00
bool must_authenticate ) ;
~ Connection ( ) ;
2009-09-17 21:07:55 +02:00
/** session requested by us is ready to run a sync */
2009-09-08 19:11:49 +02:00
void ready ( ) ;
2009-09-17 21:07:55 +02:00
/** connection is no longer needed, ensure that it gets deleted */
void shutdown ( ) ;
2009-11-11 10:55:15 +01:00
/** peer is not trusted, must authenticate as part of SyncML */
bool mustAuthenticate ( ) const { return m_mustAuthenticate ; }
2009-09-17 21:07:55 +02:00
} ;
/**
* A proxy for a Connection instance . The Connection instance can go
* away ( weak pointer , must be locked and and checked each time it is
* needed ) . The agent must remain available as long as the engine
* needs and basically becomes unusuable once the connection dies .
*
* Reconnecting is not currently supported .
*/
class DBusTransportAgent : public TransportAgent
{
GMainLoop * m_loop ;
Session & m_session ;
boost : : weak_ptr < Connection > m_connection ;
std : : string m_url ;
std : : string m_type ;
2010-01-14 12:03:40 +01:00
/*
* When the callback is invoked , we always abort the current
* transmission . If it is invoked while we are not in the wait ( )
* of this transport , then we remember that in m_eventTriggered
* and return from wait ( ) right away . The main loop is only
* quit when the transport is waiting in it . This is a precaution
* to not interfere with other parts of the code .
*/
2009-09-17 21:07:55 +02:00
TransportCallback m_callback ;
void * m_callbackData ;
int m_callbackInterval ;
2010-01-14 12:03:40 +01:00
GLibEvent m_eventSource ;
bool m_eventTriggered ;
bool m_waiting ;
2009-09-17 21:07:55 +02:00
SharedBuffer m_incomingMsg ;
std : : string m_incomingMsgType ;
void doWait ( boost : : shared_ptr < Connection > & connection ) ;
2010-01-14 12:03:40 +01:00
static gboolean timeoutCallback ( gpointer transport ) ;
2009-09-17 21:07:55 +02:00
public :
DBusTransportAgent ( GMainLoop * loop ,
Session & session ,
boost : : weak_ptr < Connection > connection ) ;
~ DBusTransportAgent ( ) ;
virtual void setURL ( const std : : string & url ) { m_url = url ; }
virtual void setContentType ( const std : : string & type ) { m_type = type ; }
virtual void send ( const char * data , size_t len ) ;
virtual void cancel ( ) { }
virtual void shutdown ( ) ;
virtual Status wait ( bool noReply = false ) ;
virtual void setCallback ( TransportCallback cb , void * udata , int interval )
{
m_callback = cb ;
m_callbackData = udata ;
m_callbackInterval = interval ;
2010-01-14 12:03:40 +01:00
m_eventSource = 0 ;
2009-09-17 21:07:55 +02:00
}
virtual void getReply ( const char * & data , size_t & len , std : : string & contentType ) ;
2009-09-08 19:11:49 +02:00
} ;
2009-03-30 17:54:47 +02:00
2009-12-21 03:03:00 +01:00
/**
* A wrapper for handling info request and response .
*/
class InfoReq {
public :
typedef std : : map < string , string > InfoMap ;
// status of current request
enum Status {
ST_RUN , // request is running
ST_OK , // ok, response is gotten
ST_TIMEOUT , // timeout
ST_CANCEL // request is cancelled
} ;
/**
* constructor
* The default timeout is 120 seconds
*/
InfoReq ( DBusServer & server ,
const string & type ,
const InfoMap & parameters ,
const Session * session ,
uint32_t timeout = 120 ) ;
~ InfoReq ( ) ;
/**
* check whether the request is ready . Also give an opportunity
* to poll the sources and then check the response is ready
* @ return the state of the request
*/
Status check ( ) ;
/**
* wait the response until timeout , abort or suspend . It may be blocked .
* The response is returned though the parameter ' response ' when the Status is
* ' ST_OK ' . Otherwise , corresponding statuses are returned .
* @ param response the received response if gotten
* @ param interval the interval to check abort , suspend and timeout , in seconds
* @ return the current status
*/
Status wait ( InfoMap & response , uint32_t interval = 3 ) ;
/**
* get response when it is ready . If false , nothing will be set in response
*/
bool getResponse ( InfoMap & response ) ;
/** cancel the request. If request is done, cancel won't do anything */
void cancel ( ) ;
/** get current status in string format */
string getStatusStr ( ) const { return statusToString ( m_status ) ; }
private :
static string statusToString ( Status status ) ;
enum InfoState {
IN_REQ , //request
IN_WAIT , // waiting
IN_DONE // done
} ;
static string infoStateToString ( InfoState state ) ;
/** callback for the timemout source */
static gboolean checkCallback ( gpointer data ) ;
/** check whether the request is timeout */
bool checkTimeout ( ) ;
friend class DBusServer ;
/** set response from dbus clients */
void setResponse ( const Caller_t & caller , const string & state , const InfoMap & response ) ;
/** send 'done' state if needed */
void done ( ) ;
string getId ( ) const { return m_id ; }
string getSessionPath ( ) const { return m_session ? m_session - > getPath ( ) : " " ; }
string getInfoStateStr ( ) const { return infoStateToString ( m_infoState ) ; }
string getHandler ( ) const { return m_handler ; }
string getType ( ) const { return m_type ; }
const InfoMap & getParam ( ) const { return m_param ; }
DBusServer & m_server ;
/** caller's session, might be NULL */
const Session * m_session ;
/** unique id of this info request */
string m_id ;
/** info req state defined in dbus api */
InfoState m_infoState ;
/** status to indicate the info request is timeout, ok, abort, etc */
Status m_status ;
/** the handler of the responsed dbus client */
Caller_t m_handler ;
/** the type of the info request */
string m_type ;
/** parameters from info request callers */
InfoMap m_param ;
/** response returned from dbus clients */
InfoMap m_response ;
/** default timeout is 120 seconds */
uint32_t m_timeout ;
/** a timer */
Timer m_timer ;
} ;
2010-09-30 14:27:02 +02:00
/***************** Client implementation ****************/
Client : : ~ Client ( )
{
SE_LOG_DEBUG ( NULL , NULL , " D-Bus client %s is destructing " , m_ID . c_str ( ) ) ;
// explicitly detach all resources instead of just freeing the
// list, so that the special behavior for sessions in detach() is
// triggered
while ( ! m_resources . empty ( ) ) {
detach ( m_resources . front ( ) . get ( ) ) ;
}
}
bool Client : : sessionExpired ( const boost : : shared_ptr < Session > & session )
{
SE_LOG_DEBUG ( NULL , NULL , " session %s expired " ,
session - > getSessionID ( ) . c_str ( ) ) ;
// don't call me again
return false ;
}
void Client : : detach ( Resource * resource )
{
for ( Resources_t : : iterator it = m_resources . begin ( ) ;
it ! = m_resources . end ( ) ;
+ + it ) {
if ( it - > get ( ) = = resource ) {
if ( it - > unique ( ) ) {
boost : : shared_ptr < Session > session = boost : : dynamic_pointer_cast < Session > ( * it ) ;
if ( session ) {
// Special behavior for sessions: keep them
// around for another minute after the last
// client detaches. This allows another client
// to attach and/or get information about the
// session.
// This is implemented as a timeout which holds
// a reference to the session. Once the timeout
// fires, it is called and then removed, which
// removes the reference.
m_server . addTimeout ( boost : : bind ( & Client : : sessionExpired ,
session ) ,
60 /* 1 minute */ ) ;
// allow other sessions to start
session - > done ( ) ;
}
}
// this will trigger removal of the resource if
// the client was the last remaining owner
m_resources . erase ( it ) ;
return ;
}
}
SE_THROW_EXCEPTION ( InvalidCall , " cannot detach from resource that client is not attached to " ) ;
}
2009-09-09 12:52:54 +02:00
/***************** ReadOperations implementation ****************/
2010-02-03 14:17:37 +01:00
ReadOperations : : ReadOperations ( const std : : string & config_name , DBusServer & server ) :
m_configName ( config_name ) , m_server ( server )
2009-09-09 12:52:54 +02:00
{ }
2009-10-14 17:53:39 +02:00
void ReadOperations : : getConfigs ( bool getTemplates , std : : vector < std : : string > & configNames )
{
if ( getTemplates ) {
2010-02-03 14:17:37 +01:00
// get device list from dbus server, currently only bluetooth devices
Configuration templates matching: match templates based on metadata
Introduced TemplateConfig to abstracting the template configuration structure,
the template metadata used for matching is also parsed here.
The fields introduced in the metadata are:
PeerIsClient: identify whether this is a server side configuration or a client
side configuration.
Fingerprint: the matching string for this template, it is a comma separated string
with each string modeled as: "Manufacture_Model". The first substring is also
used as the name to identify this template so that user can select the template
by this name.
eg:
Nokia 7210c: Nokia_7210c
SyncEvolution server: SyncEvolutionServer, SyncEvolution
ScheduleWorld: ScheduleWorld,default
SyncEvolution client: SyncEvolutionClient, SyncEvolution
Description: this is a just a descriptive string not used for matching.
GetServerTemplates is changed to add another "devices" parameter to identify
it is asking for templates for a list of "devices". Each device is a tuple
<matchstring (devicename), matchMode (server/client/all)>.
TemplateList as the return type, which is a list of class TemplateDescription
so that we can also return enough information for corresponding templates. This
list is sorted by the 3-tuple <finger, rank, name>.
Add MatchServerTemplates method which will iterating all templates inside the
folder and match against the input parameter and finally return a sorted
list of matched templates.
The atcually fuzzy match algorithm is based on a LCS (added in the following
commit).
Cmdline interface is changed accordingly:
--template ? is changed to --template ?[string], so that user use the former
case to match all templates for a tradiontial SyncML client and the latter case
to match templates related to an input string.
SyncConfig API is also renamed (Server -> Peer) because both server/client
configuration/template are handled.
The original configuration template (Funambol and ScheduleWorld) has been moved
to the new template structure (under servers), they also have a .template.ini
file added so that they can be matched and picked up. All templates for
supported servers still have built-in template support in the code as before.
Templates for SyncEvolution based server is also added.
Server side templates are added (Nokia default, Nokia_7210c and SyncEvolutionServer).
Add unit test for the new template match use case.
2010-01-19 08:01:05 +01:00
SyncConfig : : DeviceList devices ;
2010-02-03 14:17:37 +01:00
m_server . getDeviceList ( devices ) ;
2010-02-23 05:51:42 +01:00
//clear existing templates in dbus server
m_server . clearPeerTempls ( ) ;
Configuration templates matching: match templates based on metadata
Introduced TemplateConfig to abstracting the template configuration structure,
the template metadata used for matching is also parsed here.
The fields introduced in the metadata are:
PeerIsClient: identify whether this is a server side configuration or a client
side configuration.
Fingerprint: the matching string for this template, it is a comma separated string
with each string modeled as: "Manufacture_Model". The first substring is also
used as the name to identify this template so that user can select the template
by this name.
eg:
Nokia 7210c: Nokia_7210c
SyncEvolution server: SyncEvolutionServer, SyncEvolution
ScheduleWorld: ScheduleWorld,default
SyncEvolution client: SyncEvolutionClient, SyncEvolution
Description: this is a just a descriptive string not used for matching.
GetServerTemplates is changed to add another "devices" parameter to identify
it is asking for templates for a list of "devices". Each device is a tuple
<matchstring (devicename), matchMode (server/client/all)>.
TemplateList as the return type, which is a list of class TemplateDescription
so that we can also return enough information for corresponding templates. This
list is sorted by the 3-tuple <finger, rank, name>.
Add MatchServerTemplates method which will iterating all templates inside the
folder and match against the input parameter and finally return a sorted
list of matched templates.
The atcually fuzzy match algorithm is based on a LCS (added in the following
commit).
Cmdline interface is changed accordingly:
--template ? is changed to --template ?[string], so that user use the former
case to match all templates for a tradiontial SyncML client and the latter case
to match templates related to an input string.
SyncConfig API is also renamed (Server -> Peer) because both server/client
configuration/template are handled.
The original configuration template (Funambol and ScheduleWorld) has been moved
to the new template structure (under servers), they also have a .template.ini
file added so that they can be matched and picked up. All templates for
supported servers still have built-in template support in the code as before.
Templates for SyncEvolution based server is also added.
Server side templates are added (Nokia default, Nokia_7210c and SyncEvolutionServer).
Add unit test for the new template match use case.
2010-01-19 08:01:05 +01:00
SyncConfig : : TemplateList list = SyncConfig : : getPeerTemplates ( devices ) ;
2010-02-03 14:17:37 +01:00
std : : map < std : : string , int > numbers ;
BOOST_FOREACH ( const boost : : shared_ptr < SyncConfig : : TemplateDescription > peer , list ) {
//if it is not a template for device
if ( peer - > m_fingerprint . empty ( ) ) {
2010-03-25 04:26:13 +01:00
configNames . push_back ( peer - > m_templateId ) ;
2010-02-03 14:17:37 +01:00
} else {
string templName = " Bluetooth_ " ;
2010-03-25 04:26:13 +01:00
templName + = peer - > m_deviceId ;
2010-02-03 14:17:37 +01:00
templName + = " _ " ;
2010-03-25 04:26:13 +01:00
std : : map < std : : string , int > : : iterator it = numbers . find ( peer - > m_deviceId ) ;
2010-02-03 14:17:37 +01:00
if ( it = = numbers . end ( ) ) {
2010-03-25 04:26:13 +01:00
numbers . insert ( std : : make_pair ( peer - > m_deviceId , 1 ) ) ;
2010-02-03 14:17:37 +01:00
templName + = " 1 " ;
} else {
it - > second + + ;
stringstream seq ;
seq < < it - > second ;
templName + = seq . str ( ) ;
}
configNames . push_back ( templName ) ;
m_server . addPeerTempl ( templName , peer ) ;
}
Configuration templates matching: match templates based on metadata
Introduced TemplateConfig to abstracting the template configuration structure,
the template metadata used for matching is also parsed here.
The fields introduced in the metadata are:
PeerIsClient: identify whether this is a server side configuration or a client
side configuration.
Fingerprint: the matching string for this template, it is a comma separated string
with each string modeled as: "Manufacture_Model". The first substring is also
used as the name to identify this template so that user can select the template
by this name.
eg:
Nokia 7210c: Nokia_7210c
SyncEvolution server: SyncEvolutionServer, SyncEvolution
ScheduleWorld: ScheduleWorld,default
SyncEvolution client: SyncEvolutionClient, SyncEvolution
Description: this is a just a descriptive string not used for matching.
GetServerTemplates is changed to add another "devices" parameter to identify
it is asking for templates for a list of "devices". Each device is a tuple
<matchstring (devicename), matchMode (server/client/all)>.
TemplateList as the return type, which is a list of class TemplateDescription
so that we can also return enough information for corresponding templates. This
list is sorted by the 3-tuple <finger, rank, name>.
Add MatchServerTemplates method which will iterating all templates inside the
folder and match against the input parameter and finally return a sorted
list of matched templates.
The atcually fuzzy match algorithm is based on a LCS (added in the following
commit).
Cmdline interface is changed accordingly:
--template ? is changed to --template ?[string], so that user use the former
case to match all templates for a tradiontial SyncML client and the latter case
to match templates related to an input string.
SyncConfig API is also renamed (Server -> Peer) because both server/client
configuration/template are handled.
The original configuration template (Funambol and ScheduleWorld) has been moved
to the new template structure (under servers), they also have a .template.ini
file added so that they can be matched and picked up. All templates for
supported servers still have built-in template support in the code as before.
Templates for SyncEvolution based server is also added.
Server side templates are added (Nokia default, Nokia_7210c and SyncEvolutionServer).
Add unit test for the new template match use case.
2010-01-19 08:01:05 +01:00
}
2009-10-21 09:02:00 +02:00
} else {
Configuration templates matching: match templates based on metadata
Introduced TemplateConfig to abstracting the template configuration structure,
the template metadata used for matching is also parsed here.
The fields introduced in the metadata are:
PeerIsClient: identify whether this is a server side configuration or a client
side configuration.
Fingerprint: the matching string for this template, it is a comma separated string
with each string modeled as: "Manufacture_Model". The first substring is also
used as the name to identify this template so that user can select the template
by this name.
eg:
Nokia 7210c: Nokia_7210c
SyncEvolution server: SyncEvolutionServer, SyncEvolution
ScheduleWorld: ScheduleWorld,default
SyncEvolution client: SyncEvolutionClient, SyncEvolution
Description: this is a just a descriptive string not used for matching.
GetServerTemplates is changed to add another "devices" parameter to identify
it is asking for templates for a list of "devices". Each device is a tuple
<matchstring (devicename), matchMode (server/client/all)>.
TemplateList as the return type, which is a list of class TemplateDescription
so that we can also return enough information for corresponding templates. This
list is sorted by the 3-tuple <finger, rank, name>.
Add MatchServerTemplates method which will iterating all templates inside the
folder and match against the input parameter and finally return a sorted
list of matched templates.
The atcually fuzzy match algorithm is based on a LCS (added in the following
commit).
Cmdline interface is changed accordingly:
--template ? is changed to --template ?[string], so that user use the former
case to match all templates for a tradiontial SyncML client and the latter case
to match templates related to an input string.
SyncConfig API is also renamed (Server -> Peer) because both server/client
configuration/template are handled.
The original configuration template (Funambol and ScheduleWorld) has been moved
to the new template structure (under servers), they also have a .template.ini
file added so that they can be matched and picked up. All templates for
supported servers still have built-in template support in the code as before.
Templates for SyncEvolution based server is also added.
Server side templates are added (Nokia default, Nokia_7210c and SyncEvolutionServer).
Add unit test for the new template match use case.
2010-01-19 08:01:05 +01:00
SyncConfig : : ConfigList list = SyncConfig : : getConfigs ( ) ;
BOOST_FOREACH ( const SyncConfig : : ConfigList : : value_type & server , list ) {
configNames . push_back ( server . first ) ;
}
2009-10-14 17:53:39 +02:00
}
}
2010-03-30 17:39:36 +02:00
boost : : shared_ptr < DBusUserInterface > ReadOperations : : getLocalConfig ( const string & configName , bool mustExist )
2009-11-21 18:25:19 +01:00
{
string peer , context ;
SyncConfig : : splitConfigString ( SyncConfig : : normalizeConfigString ( configName ) ,
peer , context ) ;
2010-02-01 10:23:43 +01:00
boost : : shared_ptr < DBusUserInterface > syncConfig ( new DBusUserInterface ( configName ) ) ;
2009-11-21 18:25:19 +01:00
2009-12-25 09:17:58 +01:00
/** if config was not set temporarily */
if ( ! setFilters ( * syncConfig ) ) {
// the default configuration can always be opened for reading,
// everything else must exist
if ( ( context ! = " default " | | peer ! = " " ) & &
2010-03-30 17:39:36 +02:00
mustExist & &
! syncConfig - > exists ( ) ) {
2009-12-25 09:17:58 +01:00
SE_THROW_EXCEPTION ( NoSuchConfig , " No configuration ' " + configName + " ' found " ) ;
}
}
2009-11-21 18:25:19 +01:00
return syncConfig ;
}
2009-09-09 12:52:54 +02:00
void ReadOperations : : getConfig ( bool getTemplate ,
Config_t & config )
{
2009-09-22 08:46:35 +02:00
map < string , string > localConfigs ;
2010-02-01 10:23:43 +01:00
boost : : shared_ptr < SyncConfig > dbusConfig ;
boost : : shared_ptr < DBusUserInterface > dbusUI ;
SyncConfig * syncConfig ;
2010-02-03 14:17:37 +01:00
string syncURL ;
2009-09-22 08:46:35 +02:00
/** get server template */
if ( getTemplate ) {
2009-11-21 18:25:19 +01:00
string peer , context ;
2010-02-03 14:17:37 +01:00
boost : : shared_ptr < SyncConfig : : TemplateDescription > peerTemplate =
m_server . getPeerTempl ( m_configName ) ;
if ( peerTemplate ) {
2010-03-25 04:26:13 +01:00
SyncConfig : : splitConfigString ( SyncConfig : : normalizeConfigString ( peerTemplate - > m_templateId ) ,
2010-02-03 14:17:37 +01:00
peer , context ) ;
dbusConfig = SyncConfig : : createPeerTemplate ( peerTemplate - > m_path ) ;
// if we have cached template information, add match information for it
localConfigs . insert ( pair < string , string > ( " description " , peerTemplate - > m_description ) ) ;
stringstream score ;
score < < peerTemplate - > m_rank ;
localConfigs . insert ( pair < string , string > ( " score " , score . str ( ) ) ) ;
// Actually this fingerprint is transferred by getConfigs, which refers to device name
localConfigs . insert ( pair < string , string > ( " deviceName " , peerTemplate - > m_fingerprint ) ) ;
// This is the fingerprint of the template
localConfigs . insert ( pair < string , string > ( " fingerPrint " , peerTemplate - > m_matchedModel ) ) ;
2010-03-25 04:26:13 +01:00
// This is the template name presented to UI (or device class)
if ( ! peerTemplate - > m_templateName . empty ( ) ) {
localConfigs . insert ( pair < string , string > ( " templateName " , peerTemplate - > m_templateName ) ) ;
}
2010-02-03 14:17:37 +01:00
// if the peer is client, then replace syncURL with bluetooth
// MAC address
syncURL = " obex-bt:// " ;
2010-03-25 04:26:13 +01:00
syncURL + = peerTemplate - > m_deviceId ;
2010-02-03 14:17:37 +01:00
} else {
SyncConfig : : splitConfigString ( SyncConfig : : normalizeConfigString ( m_configName ) ,
peer , context ) ;
dbusConfig = SyncConfig : : createPeerTemplate ( peer ) ;
}
2010-02-01 10:23:43 +01:00
if ( ! dbusConfig . get ( ) ) {
2009-11-02 06:42:07 +01:00
SE_THROW_EXCEPTION ( NoSuchConfig , " No template ' " + m_configName + " ' found " ) ;
2009-09-22 08:46:35 +02:00
}
2009-11-21 18:25:19 +01:00
// use the shared properties from the right context as filter
// so that the returned template preserves existing properties
2010-03-30 17:39:36 +02:00
boost : : shared_ptr < DBusUserInterface > shared = getLocalConfig ( string ( " @ " ) + context , false ) ;
2009-11-21 18:25:19 +01:00
ConfigProps props ;
shared - > getProperties ( ) - > readProperties ( props ) ;
2010-02-01 10:23:43 +01:00
dbusConfig - > setConfigFilter ( true , " " , props ) ;
2009-11-21 18:25:19 +01:00
BOOST_FOREACH ( std : : string source , shared - > getSyncSources ( ) ) {
SyncSourceNodes nodes = shared - > getSyncSourceNodes ( source , " " ) ;
props . clear ( ) ;
nodes . getProperties ( ) - > readProperties ( props ) ;
2010-04-01 03:32:43 +02:00
// Special case "type" property: the value in the context
// is not preserved. Every new peer must ensure that
// its own value is compatible (= same backend) with
// the other peers.
props . erase ( " type " ) ;
2010-02-01 10:23:43 +01:00
dbusConfig - > setConfigFilter ( false , source , props ) ;
2009-09-22 08:46:35 +02:00
}
2010-02-01 10:23:43 +01:00
syncConfig = dbusConfig . get ( ) ;
2009-11-21 18:25:19 +01:00
} else {
2010-02-01 10:23:43 +01:00
dbusUI = getLocalConfig ( m_configName ) ;
//try to check password and read password from gnome keyring if possible
ConfigPropertyRegistry & registry = SyncConfig : : getRegistry ( ) ;
BOOST_FOREACH ( const ConfigProperty * prop , registry ) {
prop - > checkPassword ( * dbusUI , m_configName , * dbusUI - > getProperties ( ) ) ;
}
list < string > configuredSources = dbusUI - > getSyncSources ( ) ;
BOOST_FOREACH ( const string & sourceName , configuredSources ) {
ConfigPropertyRegistry & registry = SyncSourceConfig : : getRegistry ( ) ;
SyncSourceNodes sourceNodes = dbusUI - > getSyncSourceNodes ( sourceName ) ;
BOOST_FOREACH ( const ConfigProperty * prop , registry ) {
prop - > checkPassword ( * dbusUI , m_configName , * dbusUI - > getProperties ( ) ,
sourceName , sourceNodes . getProperties ( ) ) ;
}
}
syncConfig = dbusUI . get ( ) ;
2009-09-22 08:46:35 +02:00
}
/** get sync properties and their values */
ConfigPropertyRegistry & syncRegistry = SyncConfig : : getRegistry ( ) ;
BOOST_FOREACH ( const ConfigProperty * prop , syncRegistry ) {
bool isDefault = false ;
2009-11-13 14:52:00 +01:00
string value = prop - > getProperty ( * syncConfig - > getProperties ( ) , & isDefault ) ;
2010-02-03 14:17:37 +01:00
if ( boost : : iequals ( prop - > getName ( ) , " syncURL " ) & & ! syncURL . empty ( ) ) {
localConfigs . insert ( pair < string , string > ( prop - > getName ( ) , syncURL ) ) ;
} else if ( ! isDefault ) {
2009-09-22 08:46:35 +02:00
localConfigs . insert ( pair < string , string > ( prop - > getName ( ) , value ) ) ;
}
}
2010-03-26 18:03:42 +01:00
// insert 'configName' of the chosen config (m_configName is not normalized)
localConfigs . insert ( pair < string , string > ( " configName " , syncConfig - > getConfigName ( ) ) ) ;
2010-03-15 02:33:43 +01:00
2009-09-22 08:46:35 +02:00
config . insert ( pair < string , map < string , string > > ( " " , localConfigs ) ) ;
/* get configurations from sources */
list < string > sources = syncConfig - > getSyncSources ( ) ;
BOOST_FOREACH ( const string & name , sources ) {
localConfigs . clear ( ) ;
SyncSourceNodes sourceNodes = syncConfig - > getSyncSourceNodes ( name ) ;
ConfigPropertyRegistry & sourceRegistry = SyncSourceConfig : : getRegistry ( ) ;
BOOST_FOREACH ( const ConfigProperty * prop , sourceRegistry ) {
bool isDefault = false ;
2009-11-13 14:52:00 +01:00
string value = prop - > getProperty ( * sourceNodes . getProperties ( ) , & isDefault ) ;
2009-09-22 08:46:35 +02:00
if ( ! isDefault ) {
localConfigs . insert ( pair < string , string > ( prop - > getName ( ) , value ) ) ;
}
}
config . insert ( pair < string , map < string , string > > ( " source/ " + name , localConfigs ) ) ;
}
2009-09-09 12:52:54 +02:00
}
void ReadOperations : : getReports ( uint32_t start , uint32_t count ,
Reports_t & reports )
{
2009-09-21 04:55:37 +02:00
SyncContext client ( m_configName , false ) ;
std : : vector < string > dirs ;
client . getSessions ( dirs ) ;
uint32_t index = 0 ;
2009-12-03 10:37:00 +01:00
// newest report firstly
for ( int i = dirs . size ( ) - 1 ; i > = 0 ; - - i ) {
2009-09-21 04:55:37 +02:00
/** if start plus count is bigger than actual size, then return actual - size reports */
2009-11-18 10:22:51 +01:00
if ( index > = start & & index - start < count ) {
2009-12-03 10:37:00 +01:00
const string & dir = dirs [ i ] ;
2009-09-21 04:55:37 +02:00
std : : map < string , string > aReport ;
2009-12-15 04:11:38 +01:00
// insert a 'dir' as an ID for the current report
aReport . insert ( pair < string , string > ( " dir " , dir ) ) ;
2009-09-21 04:55:37 +02:00
SyncReport report ;
2009-12-03 10:37:00 +01:00
// peerName is also extracted from the dir
string peerName = client . readSessionInfo ( dir , report ) ;
2010-01-20 11:12:53 +01:00
boost : : shared_ptr < SyncConfig > config ( new SyncConfig ( m_configName ) ) ;
string storedPeerName = config - > getPeerName ( ) ;
//if can't find peer name, use the peer name from the log dir
if ( ! storedPeerName . empty ( ) ) {
peerName = storedPeerName ;
}
2009-11-25 10:01:06 +01:00
/** serialize report to ConfigProps and then copy them to reports */
HashFileConfigNode node ( " /dev/null " , " " , true ) ;
node < < report ;
ConfigProps props ;
node . readProperties ( props ) ;
BOOST_FOREACH ( const ConfigProps : : value_type & entry , props ) {
aReport . insert ( entry ) ;
2009-09-21 04:55:37 +02:00
}
2009-12-03 10:37:00 +01:00
// a new key-value pair <"peer", [peer name]> is transferred
aReport . insert ( pair < string , string > ( " peer " , peerName ) ) ;
2009-09-21 04:55:37 +02:00
reports . push_back ( aReport ) ;
}
index + + ;
}
2009-09-09 12:52:54 +02:00
}
2009-09-27 08:02:03 +02:00
void ReadOperations : : checkSource ( const std : : string & sourceName )
{
boost : : shared_ptr < SyncConfig > config ( new SyncConfig ( m_configName ) ) ;
2009-11-26 09:37:19 +01:00
setFilters ( * config ) ;
2009-09-27 08:02:03 +02:00
list < std : : string > sourceNames = config - > getSyncSources ( ) ;
list < std : : string > : : iterator it ;
for ( it = sourceNames . begin ( ) ; it ! = sourceNames . end ( ) ; + + it ) {
if ( * it = = sourceName ) {
break ;
}
}
if ( it = = sourceNames . end ( ) ) {
2009-11-13 05:47:21 +01:00
SE_THROW_EXCEPTION ( NoSuchSource , " ' " + m_configName + " ' has no ' " + sourceName + " ' source " ) ;
2009-09-27 08:02:03 +02:00
}
2009-12-02 15:05:36 +01:00
bool checked = false ;
2009-09-27 08:02:03 +02:00
try {
2009-12-02 15:05:36 +01:00
// this can already throw exceptions when the config is invalid
SyncSourceParams params ( sourceName , config - > getSyncSourceNodes ( sourceName ) ) ;
2010-02-08 15:04:59 +01:00
auto_ptr < SyncSource > syncSource ( SyncSource : : createSource ( params , false , config . get ( ) ) ) ;
2009-12-02 15:05:36 +01:00
2009-09-27 08:02:03 +02:00
if ( syncSource . get ( ) ) {
syncSource - > open ( ) ;
2009-12-02 15:05:36 +01:00
// success!
checked = true ;
2009-09-27 08:02:03 +02:00
}
} catch ( . . . ) {
2009-12-02 15:05:36 +01:00
Exception : : handle ( ) ;
}
if ( ! checked ) {
2009-11-24 11:42:45 +01:00
SE_THROW_EXCEPTION ( SourceUnusable , " The source ' " + sourceName + " ' is not usable " ) ;
2009-09-27 08:02:03 +02:00
}
}
void ReadOperations : : getDatabases ( const string & sourceName , SourceDatabases_t & databases )
{
boost : : shared_ptr < SyncConfig > config ( new SyncConfig ( m_configName ) ) ;
2009-11-26 09:37:19 +01:00
setFilters ( * config ) ;
2009-11-13 14:52:00 +01:00
SyncSourceParams params ( sourceName , config - > getSyncSourceNodes ( sourceName ) ) ;
2009-09-27 08:02:03 +02:00
const SourceRegistry & registry ( SyncSource : : getSourceRegistry ( ) ) ;
BOOST_FOREACH ( const RegisterSyncSource * sourceInfo , registry ) {
SyncSource * source = sourceInfo - > m_create ( params ) ;
2009-11-20 15:29:25 +01:00
if ( ! source ) {
continue ;
} else if ( source = = RegisterSyncSource : : InactiveSource ) {
SE_THROW_EXCEPTION ( NoSuchSource , " ' " + m_configName + " ' backend of source ' " + sourceName + " ' is not supported " ) ;
} else {
2009-09-27 08:02:03 +02:00
auto_ptr < SyncSource > autoSource ( source ) ;
databases = autoSource - > getDatabases ( ) ;
2009-11-20 15:29:25 +01:00
return ;
2009-09-27 08:02:03 +02:00
}
}
2009-11-20 15:29:25 +01:00
SE_THROW_EXCEPTION ( NoSuchSource , " ' " + m_configName + " ' has no ' " + sourceName + " ' source " ) ;
2009-09-27 08:02:03 +02:00
}
2010-02-01 10:23:43 +01:00
/***************** DBusUserInterface implementation **********************/
DBusUserInterface : : DBusUserInterface ( const std : : string & config ) :
SyncContext ( config , true )
{
}
inline const char * passwdStr ( const std : : string & str )
{
return str . empty ( ) ? NULL : str . c_str ( ) ;
}
string DBusUserInterface : : askPassword ( const string & passwordName ,
const string & descr ,
const ConfigPasswordKey & key )
{
string password ;
# ifdef USE_GNOME_KEYRING
/** here we use server sync url without protocol prefix and
* user account name as the key in the keyring */
/* It is possible to let CmdlineSyncClient decide which of fields in ConfigPasswordKey it would use
* but currently only use passed key instead */
GnomeKeyringResult result ;
GList * list ;
result = gnome_keyring_find_network_password_sync ( passwdStr ( key . user ) ,
passwdStr ( key . domain ) ,
passwdStr ( key . server ) ,
passwdStr ( key . object ) ,
passwdStr ( key . protocol ) ,
passwdStr ( key . authtype ) ,
key . port ,
& list ) ;
/** if find password stored in gnome keyring */
if ( result = = GNOME_KEYRING_RESULT_OK & & list & & list - > data ) {
GnomeKeyringNetworkPasswordData * key_data ;
key_data = ( GnomeKeyringNetworkPasswordData * ) list - > data ;
password = key_data - > password ;
gnome_keyring_network_password_list_free ( list ) ;
return password ;
}
# endif
//if not found, return empty
return " " ;
}
bool DBusUserInterface : : savePassword ( const string & passwordName ,
const string & password ,
const ConfigPasswordKey & key )
{
# ifdef USE_GNOME_KEYRING
/* It is possible to let CmdlineSyncClient decide which of fields in ConfigPasswordKey it would use
* but currently only use passed key instead */
guint32 itemId ;
GnomeKeyringResult result ;
// write password to keyring
result = gnome_keyring_set_network_password_sync ( NULL ,
passwdStr ( key . user ) ,
passwdStr ( key . domain ) ,
passwdStr ( key . server ) ,
passwdStr ( key . object ) ,
passwdStr ( key . protocol ) ,
passwdStr ( key . authtype ) ,
key . port ,
password . c_str ( ) ,
& itemId ) ;
/* if set operation is failed */
if ( result ! = GNOME_KEYRING_RESULT_OK ) {
# ifdef GNOME_KEYRING_220
SyncContext : : throwError ( " Try to save " + passwordName + " in gnome-keyring but get an error. " + gnome_keyring_result_to_message ( result ) ) ;
# else
/** if gnome-keyring version is below 2.20, it doesn't support 'gnome_keyring_result_to_message'. */
stringstream value ;
value < < ( int ) result ;
SyncContext : : throwError ( " Try to save " + passwordName + " in gnome-keyring but get an error. The gnome-keyring error code is " + value . str ( ) + " . " ) ;
# endif
}
return true ;
# else
/** if no support of gnome-keyring, don't save anything */
return false ;
# endif
}
2010-06-11 15:41:31 +02:00
void DBusUserInterface : : readStdin ( string & content )
{
throwError ( " reading stdin in D-Bus server not supported, use --daemon=no in command line " ) ;
}
2010-02-01 10:23:43 +01:00
2009-09-14 12:58:22 +02:00
/***************** DBusSync implementation **********************/
2009-09-14 21:54:43 +02:00
DBusSync : : DBusSync ( const std : : string & config ,
2009-09-14 12:58:22 +02:00
Session & session ) :
2010-02-01 10:23:43 +01:00
DBusUserInterface ( config ) ,
2009-09-14 12:58:22 +02:00
m_session ( session )
{
}
boost : : shared_ptr < TransportAgent > DBusSync : : createTransportAgent ( )
{
2009-09-17 21:07:55 +02:00
if ( m_session . useConnection ( ) ) {
// use the D-Bus Connection to send and receive messages
2010-04-01 14:49:41 +02:00
boost : : shared_ptr < TransportAgent > agent ( new DBusTransportAgent ( m_session . getServer ( ) . getLoop ( ) ,
m_session ,
m_session . getConnection ( ) ) ) ;
// We don't know whether we'll run as client or server.
// But we as we cannot resend messages via D-Bus even if running as
// client (API not designed for it), let's use the hard timeout
// from RetryDuration here.
int timeout = getRetryDuration ( ) ;
if ( timeout ) {
agent - > setCallback ( transport_cb ,
reinterpret_cast < void * > ( static_cast < uintptr_t > ( timeout ) ) ,
timeout ) ;
}
return agent ;
2009-09-17 21:07:55 +02:00
} else {
// no connection, use HTTP via libsoup/GMainLoop
GMainLoop * loop = m_session . getServer ( ) . getLoop ( ) ;
2010-02-09 16:43:57 +01:00
boost : : shared_ptr < TransportAgent > agent = SyncContext : : createTransportAgent ( loop ) ;
2009-09-17 21:07:55 +02:00
return agent ;
}
2009-09-14 12:58:22 +02:00
}
void DBusSync : : displaySyncProgress ( sysync : : TProgressEventEnum type ,
int32_t extra1 , int32_t extra2 , int32_t extra3 )
{
SyncContext : : displaySyncProgress ( type , extra1 , extra2 , extra3 ) ;
m_session . syncProgress ( type , extra1 , extra2 , extra3 ) ;
}
void DBusSync : : displaySourceProgress ( sysync : : TProgressEventEnum type ,
SyncSource & source ,
int32_t extra1 , int32_t extra2 , int32_t extra3 )
{
SyncContext : : displaySourceProgress ( type , source , extra1 , extra2 , extra3 ) ;
m_session . sourceProgress ( type , source , extra1 , extra2 , extra3 ) ;
}
2009-12-07 07:26:01 +01:00
void DBusSync : : reportStepCmd ( sysync : : uInt16 stepCmd )
{
switch ( stepCmd ) {
case sysync : : STEPCMD_SENDDATA :
case sysync : : STEPCMD_RESENDDATA :
case sysync : : STEPCMD_NEEDDATA :
//sending or waiting data
m_session . setStepInfo ( true ) ;
break ;
default :
// otherwise, processing
m_session . setStepInfo ( false ) ;
break ;
}
}
2010-03-04 02:53:04 +01:00
void DBusSync : : syncSuccessStart ( )
{
m_session . syncSuccessStart ( ) ;
}
2009-10-15 09:41:14 +02:00
bool DBusSync : : checkForSuspend ( )
{
2009-10-30 07:25:53 +01:00
return m_session . isSuspend ( ) | | SyncContext : : checkForSuspend ( ) ;
2009-10-15 09:41:14 +02:00
}
bool DBusSync : : checkForAbort ( )
{
2009-10-30 07:25:53 +01:00
return m_session . isAbort ( ) | | SyncContext : : checkForAbort ( ) ;
2009-10-15 09:41:14 +02:00
}
int DBusSync : : sleep ( int intervals )
{
time_t start = time ( NULL ) ;
while ( true ) {
g_main_context_iteration ( NULL , false ) ;
time_t now = time ( NULL ) ;
if ( checkForSuspend ( ) | | checkForAbort ( ) ) {
return ( intervals - now + start ) ;
}
if ( intervals - now + start < = 0 ) {
return intervals - now + start ;
}
}
}
2009-12-08 02:49:51 +01:00
string DBusSync : : askPassword ( const string & passwordName ,
const string & descr ,
const ConfigPasswordKey & key )
{
2010-02-01 10:23:43 +01:00
string password = DBusUserInterface : : askPassword ( passwordName , descr , key ) ;
2009-12-08 02:49:51 +01:00
2010-02-01 10:23:43 +01:00
if ( password . empty ( ) ) {
password = m_session . askPassword ( passwordName , descr , key ) ;
2009-12-08 02:49:51 +01:00
}
2010-02-01 10:23:43 +01:00
return password ;
2009-12-08 02:49:51 +01:00
}
2009-09-08 19:11:49 +02:00
/***************** Session implementation ***********************/
2010-09-30 10:32:13 +02:00
void Session : : attach ( const Caller_t & caller )
{
boost : : shared_ptr < Client > client ( m_server . findClient ( caller ) ) ;
if ( ! client ) {
throw runtime_error ( " unknown client " ) ;
}
boost : : shared_ptr < Session > me = m_me . lock ( ) ;
if ( ! me ) {
throw runtime_error ( " session already deleted?! " ) ;
}
client - > attach ( me ) ;
}
2009-09-14 12:58:22 +02:00
void Session : : detach ( const Caller_t & caller )
2009-09-08 19:11:49 +02:00
{
boost : : shared_ptr < Client > client ( m_server . findClient ( caller ) ) ;
if ( ! client ) {
throw runtime_error ( " unknown client " ) ;
}
client - > detach ( this ) ;
}
2009-09-22 08:46:35 +02:00
static void setSyncFilters ( const ReadOperations : : Config_t & config , FilterConfigNode : : ConfigFilter & syncFilter , std : : map < std : : string , FilterConfigNode : : ConfigFilter > & sourceFilters )
{
ReadOperations : : Config_t : : const_iterator it ;
for ( it = config . begin ( ) ; it ! = config . end ( ) ; it + + ) {
map < string , string > : : const_iterator sit ;
if ( it - > first . empty ( ) ) {
for ( sit = it - > second . begin ( ) ; sit ! = it - > second . end ( ) ; sit + + ) {
syncFilter . insert ( * sit ) ;
}
} else {
string name = it - > first ;
if ( name . find ( " source/ " ) = = 0 ) {
name = name . substr ( 7 ) ; ///> 7 is the length of "source/"
FilterConfigNode : : ConfigFilter & sourceFilter = sourceFilters [ name ] ;
for ( sit = it - > second . begin ( ) ; sit ! = it - > second . end ( ) ; sit + + ) {
sourceFilter . insert ( * sit ) ;
}
}
}
}
}
void Session : : setConfig ( bool update , bool temporary ,
2009-09-09 12:52:54 +02:00
const ReadOperations : : Config_t & config )
{
if ( ! m_active ) {
2009-11-02 06:42:07 +01:00
SE_THROW_EXCEPTION ( InvalidCall , " session is not active, call not allowed at this time " ) ;
2009-09-09 12:52:54 +02:00
}
2010-03-15 02:33:43 +01:00
if ( m_runOperation ! = OP_NULL ) {
string msg = StringPrintf ( " %s started, cannot change configuration at this time " , runOpToString ( m_runOperation ) . c_str ( ) ) ;
SE_THROW_EXCEPTION ( InvalidCall , msg ) ;
2009-09-09 12:52:54 +02:00
}
2009-09-22 08:46:35 +02:00
if ( ! update & & temporary ) {
throw std : : runtime_error ( " Clearing existing configuration and temporary configuration changes which only affects the duration of the session are mutually exclusive " ) ;
}
2010-02-03 02:50:56 +01:00
m_server . getPresenceStatus ( ) . updateConfigPeers ( m_configName , config ) ;
2009-09-22 08:46:35 +02:00
/** check whether we need remove the entire configuration */
2009-10-09 05:16:44 +02:00
if ( ! update & & config . empty ( ) ) {
2009-10-14 17:53:39 +02:00
boost : : shared_ptr < SyncConfig > syncConfig ( new SyncConfig ( getConfigName ( ) ) ) ;
2009-09-22 08:46:35 +02:00
if ( syncConfig . get ( ) ) {
syncConfig - > remove ( ) ;
2010-08-25 21:03:31 +02:00
m_setConfig = true ;
2009-09-22 08:46:35 +02:00
}
2009-10-09 05:16:44 +02:00
return ;
2009-09-22 08:46:35 +02:00
}
if ( temporary ) {
2009-11-26 09:37:19 +01:00
/* save temporary configs in session filters */
2009-09-22 08:46:35 +02:00
setSyncFilters ( config , m_syncFilter , m_sourceFilters ) ;
2009-11-26 09:37:19 +01:00
m_tempConfig = true ;
2009-09-22 08:46:35 +02:00
} else {
FilterConfigNode : : ConfigFilter syncFilter ;
std : : map < std : : string , FilterConfigNode : : ConfigFilter > sourceFilters ;
setSyncFilters ( config , syncFilter , sourceFilters ) ;
/* need to save configurations */
2009-10-14 17:53:39 +02:00
boost : : shared_ptr < SyncConfig > from ( new SyncConfig ( getConfigName ( ) ) ) ;
2009-10-09 05:16:44 +02:00
/* if it is not clear mode and config does not exist, an error throws */
2009-09-22 08:46:35 +02:00
if ( update & & ! from - > exists ( ) ) {
config: share properties between peers, configuration view without peer
This patch makes the configuration layout with per-source and per-peer
properties the default for new configurations. Migrating old
configurations is not implemented. The command line has not
been updated at all (MB #8048). The D-Bus API is fairly complete,
only listing sessions independently of a peer is missing (MB #8049).
The key concept of this patch is that a pseudo-node implemented by
MultiplexConfigNode provides a view on all user-visible or hidden
properties. Based on the property name, it looks up the property
definition, picks one of the underlying nodes based on the property
visibility and sharing attributes, then reads and writes the property
via that node. Clearing properties is not needed and not implemented,
because of the uncertain semantic (really remove shared properties?!).
The "sync" property must be available both in the per-source config
(to pick a backend independently of a specific peer) and in the
per-peer configuration (to select a specific data format). This is
solved by making the property special (SHARED_AND_UNSHARED flag) and
then writing it into two nodes. Reading is done from the more specific
per-peer node, with the other node acting as fallback.
The MultiplexConfigNode has to implement the FilterConfigNode API
because it is used as one by the code which sets passwords in the
filter. For this to work, the base FilterConfigNode implementation must
use virtual method calls.
The TestDBusSessionConfig.testUpdateConfigError checks that the error
generated for an incorrect "sync" property contains the path of the
config.ini file. The meaning of the error message in this case is that
the wrong value is *for* that file, not that the property is already
wrong *in* the file, but that's okay.
The MultiplexConfigNode::getName() can only return a fixed name. To
satisfy the test and because it is the right choice at the moment for
all properties which might trigger such an error, it now is configured
so that it returns the most specific path of the non-shared
properties.
"syncevolution --print-config" shows errors that are in files. Wrong
command line parameters are rejected with a message that refers to the
command line parameter ("--source-property sync=foo").
A future enhancement would be to make the name depend on the
property (MB#8037).
Because an empty string is now a valid configuration name (referencing
the source properties without the per-peer properties) several checks
for such empty strings were removed. The corresponding tests were
updated resp. removed. Instead of talking about "server not found",
the more neutral name "configuration" is used. The new
TestMultipleConfigs.testSharing() covers the semantic of sharing
properties between multiple configs.
Access to non-existant nodes is routed into the new
DevNullConfigNode. It always returns an empty string when reading and
throws an error when trying to write into it. Unintentionally writing
into a config.ini file therefore became harder, compared with the
previous instantiation of SyncContext() with empty config name.
The parsing of incoming messages uses a SyncContext which is bound to
a VolatileConfigNode. This allows reading and writing of properties
without any risk of touching files on disk.
The patch which introduced the new config nodes was not complete yet
with regards to the new layout. Removing nodes and trees used the
wrong root path: getRootPath() refers to the most specific peer
config, m_root to the part without the peer path. SyncConfig must
distinguish between a view with peer-specific properties and one
without, which is done by setting the m_peerPath only if a peer was
selected. Copying properties must know whether writing per-specific
properties ("unshared") is wanted, because trying to do it for a view
without those properties would trigger the DevNullConfigNode
exception.
SyncConfig::removeSyncSource() removes source properties both in the
shared part of the config and in *all* peers. This is used by
Session.SetConfig() for the case that the caller is a) setting instead
of updating the config and b) not providing any properties for the
source. This is clearly a risky operation which should not be done
when there are other peers which still use the source. We might have a
problem in our D-Bus API definition for "removing a peer
configuration" (MB #8059) because it always has an effect on other
peers.
The property registries were initialized implicitly before. With the
recent changes it happened that SyncContext was initialized to analyze
a SyncML message without initializing the registry, which caused
getRemoteDevID() to use a property where the hidden flag had not been
set yet.
Moving all of these additional flags into the property constructors is
awkward (which is why they are in the getRegistry() methods), so this
was fixed by initializing the properties in the SyncConfig
constructors by asking for the registries. Because there is no way to
access them except via the registry and SyncConfig instances (*), this
should ensure that the properties are valid when used.
(*) Exception are some properties which are declared publicly to have access
to their name. Nobody's perfect...
2009-11-13 20:02:44 +01:00
SE_THROW_EXCEPTION ( NoSuchConfig , " The configuration ' " + getConfigName ( ) + " ' doesn't exist " ) ;
2009-10-09 05:16:44 +02:00
}
if ( ! update ) {
list < string > sources = from - > getSyncSources ( ) ;
list < string > : : iterator it ;
for ( it = sources . begin ( ) ; it ! = sources . end ( ) ; + + it ) {
string source = " source/ " ;
source + = * it ;
ReadOperations : : Config_t : : const_iterator configIt = config . find ( source ) ;
if ( configIt = = config . end ( ) ) {
/** if no config for this source, we remove it */
from - > removeSyncSource ( * it ) ;
} else {
/** just clear visiable properties, remove them and their values */
from - > clearSyncSourceProperties ( * it ) ;
}
2009-09-22 08:46:35 +02:00
}
2009-10-09 05:16:44 +02:00
from - > clearSyncProperties ( ) ;
2009-09-22 08:46:35 +02:00
}
/** generate new sources in the config map */
for ( ReadOperations : : Config_t : : const_iterator it = config . begin ( ) ; it ! = config . end ( ) ; + + it ) {
string sourceName = it - > first ;
if ( sourceName . find ( " source/ " ) = = 0 ) {
sourceName = sourceName . substr ( 7 ) ; ///> 7 is the length of "source/"
from - > getSyncSourceNodes ( sourceName ) ;
}
}
/* apply user settings */
from - > setConfigFilter ( true , " " , syncFilter ) ;
map < string , FilterConfigNode : : ConfigFilter > : : iterator it ;
for ( it = sourceFilters . begin ( ) ; it ! = sourceFilters . end ( ) ; it + + ) {
from - > setConfigFilter ( false , it - > first , it - > second ) ;
}
2009-10-14 17:53:39 +02:00
boost : : shared_ptr < DBusSync > syncConfig ( new DBusSync ( getConfigName ( ) , * this ) ) ;
2009-09-22 08:46:35 +02:00
syncConfig - > copy ( * from , NULL ) ;
syncConfig - > preFlush ( * syncConfig ) ;
syncConfig - > flush ( ) ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
m_setConfig = true ;
2009-09-22 08:46:35 +02:00
}
2009-09-09 12:52:54 +02:00
}
2009-09-27 22:48:04 +02:00
void Session : : initServer ( SharedBuffer data , const std : : string & messageType )
{
m_serverMode = true ;
m_initialMessage = data ;
m_initialMessageType = messageType ;
}
2009-11-11 10:55:15 +01:00
void Session : : sync ( const std : : string & mode , const SourceModes_t & source_modes )
2009-09-14 12:58:22 +02:00
{
if ( ! m_active ) {
2009-11-02 06:42:07 +01:00
SE_THROW_EXCEPTION ( InvalidCall , " session is not active, call not allowed at this time " ) ;
2009-09-14 12:58:22 +02:00
}
2010-03-15 02:33:43 +01:00
if ( m_runOperation = = OP_SYNC ) {
string msg = StringPrintf ( " %s started, cannot start again " , runOpToString ( m_runOperation ) . c_str ( ) ) ;
SE_THROW_EXCEPTION ( InvalidCall , msg ) ;
} else if ( m_runOperation ! = OP_NULL ) {
string msg = StringPrintf ( " %s started, cannot start sync " , runOpToString ( m_runOperation ) . c_str ( ) ) ;
2009-12-14 06:59:02 +01:00
SE_THROW_EXCEPTION ( InvalidCall , msg ) ;
2009-09-14 12:58:22 +02:00
}
2009-10-14 17:53:39 +02:00
m_sync . reset ( new DBusSync ( getConfigName ( ) , * this ) ) ;
2009-09-27 22:48:04 +02:00
if ( m_serverMode ) {
m_sync - > initServer ( m_sessionID ,
m_initialMessage ,
m_initialMessageType ) ;
2009-11-11 10:55:15 +01:00
boost : : shared_ptr < Connection > c = m_connection . lock ( ) ;
if ( c & & ! c - > mustAuthenticate ( ) ) {
// unsetting username/password disables checking them
m_syncFilter [ " password " ] = " " ;
m_syncFilter [ " username " ] = " " ;
}
2009-09-27 22:48:04 +02:00
}
2009-09-14 12:58:22 +02:00
2010-03-12 09:34:28 +01:00
if ( m_remoteInitiated ) {
m_sync - > setRemoteInitiated ( true ) ;
}
2009-09-14 22:33:30 +02:00
// Apply temporary config filters. The parameters of this function
// override the source filters, if set.
m_sync - > setConfigFilter ( true , " " , m_syncFilter ) ;
FilterConfigNode : : ConfigFilter filter ;
filter = m_sourceFilter ;
if ( ! mode . empty ( ) ) {
filter [ SyncSourceConfig : : m_sourcePropSync . getName ( ) ] = mode ;
}
m_sync - > setConfigFilter ( false , " " , filter ) ;
BOOST_FOREACH ( const std : : string & source ,
m_sync - > getSyncSources ( ) ) {
filter = m_sourceFilters [ source ] ;
SourceModes_t : : const_iterator it = source_modes . find ( source ) ;
if ( it ! = source_modes . end ( ) ) {
filter [ SyncSourceConfig : : m_sourcePropSync . getName ( ) ] = it - > second ;
}
m_sync - > setConfigFilter ( false , source , filter ) ;
}
2009-09-14 12:58:22 +02:00
// Update status and progress. From now on, all configured sources
// have their default entry (referencing them by name creates the
// entry).
BOOST_FOREACH ( const std : : string source ,
m_sync - > getSyncSources ( ) ) {
m_sourceStatus [ source ] ;
m_sourceProgress [ source ] ;
}
fireProgress ( true ) ;
fireStatus ( true ) ;
2009-12-14 06:59:02 +01:00
m_runOperation = OP_SYNC ;
2009-09-14 12:58:22 +02:00
// now that we have a DBusSync object, return from the main loop
// and once that is done, transfer control to that object
g_main_loop_quit ( loop ) ;
}
void Session : : abort ( )
{
2010-03-15 02:33:43 +01:00
if ( m_runOperation ! = OP_SYNC & & m_runOperation ! = OP_CMDLINE ) {
2009-11-02 06:42:07 +01:00
SE_THROW_EXCEPTION ( InvalidCall , " sync not started, cannot abort at this time " ) ;
2009-09-14 12:58:22 +02:00
}
2009-10-13 09:56:32 +02:00
m_syncStatus = SYNC_ABORT ;
2009-10-15 09:41:14 +02:00
fireStatus ( true ) ;
2009-10-30 07:25:53 +01:00
// state change, return to caller so that it can react
g_main_loop_quit ( m_server . getLoop ( ) ) ;
2009-09-14 12:58:22 +02:00
}
void Session : : suspend ( )
{
2010-03-15 02:33:43 +01:00
if ( m_runOperation ! = OP_SYNC & & m_runOperation ! = OP_CMDLINE ) {
2009-11-02 06:42:07 +01:00
SE_THROW_EXCEPTION ( InvalidCall , " sync not started, cannot suspend at this time " ) ;
2009-09-14 12:58:22 +02:00
}
2009-10-13 09:56:32 +02:00
m_syncStatus = SYNC_SUSPEND ;
2009-10-15 09:41:14 +02:00
fireStatus ( true ) ;
2009-10-30 07:25:53 +01:00
g_main_loop_quit ( m_server . getLoop ( ) ) ;
2009-09-14 12:58:22 +02:00
}
void Session : : getStatus ( std : : string & status ,
uint32_t & error ,
SourceStatuses_t & sources )
{
2009-11-19 13:44:22 +01:00
status = syncStatusToString ( m_syncStatus ) ;
2009-12-07 07:26:01 +01:00
if ( m_stepIsWaiting ) {
status + = " ;waiting " ;
}
2009-09-14 12:58:22 +02:00
error = m_error ;
sources = m_sourceStatus ;
}
void Session : : getProgress ( int32_t & progress ,
SourceProgresses_t & sources )
{
progress = m_progress ;
sources = m_sourceProgress ;
}
void Session : : fireStatus ( bool flush )
{
std : : string status ;
uint32_t error ;
SourceStatuses_t sources ;
2009-10-12 08:16:22 +02:00
/** not force flushing and not timeout, return */
if ( ! flush & & ! m_statusTimer . timeout ( ) ) {
return ;
}
m_statusTimer . reset ( ) ;
2009-09-14 12:58:22 +02:00
getStatus ( status , error , sources ) ;
emitStatus ( status , error , sources ) ;
}
void Session : : fireProgress ( bool flush )
{
int32_t progress ;
SourceProgresses_t sources ;
2009-10-12 08:16:22 +02:00
/** not force flushing and not timeout, return */
if ( ! flush & & ! m_progressTimer . timeout ( ) ) {
return ;
}
m_progressTimer . reset ( ) ;
2009-09-14 12:58:22 +02:00
getProgress ( progress , sources ) ;
emitProgress ( progress , sources ) ;
}
2009-10-13 09:56:32 +02:00
string Session : : syncStatusToString ( SyncStatus state )
{
switch ( state ) {
2009-11-19 13:44:22 +01:00
case SYNC_QUEUEING :
return " queueing " ;
2009-10-13 09:56:32 +02:00
case SYNC_IDLE :
return " idle " ;
case SYNC_RUNNING :
return " running " ;
case SYNC_ABORT :
return " aborting " ;
case SYNC_SUSPEND :
return " suspending " ;
case SYNC_DONE :
return " done " ;
default :
return " " ;
} ;
}
2009-09-09 12:52:54 +02:00
2010-09-30 10:32:13 +02:00
boost : : shared_ptr < Session > Session : : createSession ( DBusServer & server ,
const std : : string & peerDeviceID ,
const std : : string & config_name ,
const std : : string & session ,
const std : : vector < std : : string > & flags )
{
boost : : shared_ptr < Session > me ( new Session ( server , peerDeviceID , config_name , session , flags ) ) ;
me - > m_me = me ;
return me ;
}
2009-09-08 19:11:49 +02:00
Session : : Session ( DBusServer & server ,
2009-11-09 21:10:10 +01:00
const std : : string & peerDeviceID ,
2009-09-09 12:52:54 +02:00
const std : : string & config_name ,
2010-08-25 10:37:58 +02:00
const std : : string & session ,
const std : : vector < std : : string > & flags ) :
2009-09-08 19:11:49 +02:00
DBusObjectHelper ( server . getConnection ( ) ,
2009-09-23 09:44:01 +02:00
std : : string ( " /org/syncevolution/Session/ " ) + session ,
2010-01-13 02:47:42 +01:00
" org.syncevolution.Session " ,
boost : : bind ( & DBusServer : : autoTermCallback , & server ) ) ,
2010-02-03 14:17:37 +01:00
ReadOperations ( config_name , server ) ,
2009-09-08 19:11:49 +02:00
m_server ( server ) ,
2010-08-25 10:37:58 +02:00
m_flags ( flags ) ,
2009-09-27 22:48:04 +02:00
m_sessionID ( session ) ,
2009-11-09 21:10:10 +01:00
m_peerDeviceID ( peerDeviceID ) ,
2009-09-27 22:48:04 +02:00
m_serverMode ( false ) ,
2009-09-17 21:07:55 +02:00
m_useConnection ( false ) ,
2009-11-26 09:37:19 +01:00
m_tempConfig ( false ) ,
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
m_setConfig ( false ) ,
2009-09-08 19:11:49 +02:00
m_active ( false ) ,
2010-09-30 14:27:02 +02:00
m_done ( false ) ,
2010-03-12 09:34:28 +01:00
m_remoteInitiated ( false ) ,
2009-11-19 13:44:22 +01:00
m_syncStatus ( SYNC_QUEUEING ) ,
2009-12-07 07:26:01 +01:00
m_stepIsWaiting ( false ) ,
2009-09-14 12:58:22 +02:00
m_priority ( PRI_DEFAULT ) ,
2009-10-09 09:22:13 +02:00
m_progress ( 0 ) ,
m_progData ( m_progress ) ,
2009-09-14 12:58:22 +02:00
m_error ( 0 ) ,
2009-10-12 08:16:22 +02:00
m_statusTimer ( 100 ) ,
2009-10-29 11:00:33 +01:00
m_progressTimer ( 50 ) ,
2009-12-14 06:59:02 +01:00
m_restoreBefore ( true ) ,
2009-12-22 09:47:31 +01:00
m_restoreSrcTotal ( 0 ) ,
m_restoreSrcEnd ( 0 ) ,
2010-03-15 02:33:43 +01:00
m_runOperation ( OP_NULL ) ,
2010-03-04 02:53:04 +01:00
m_listener ( NULL ) ,
2009-10-14 17:24:22 +02:00
emitStatus ( * this , " StatusChanged " ) ,
emitProgress ( * this , " ProgressChanged " )
2009-10-12 21:01:02 +02:00
{
2010-09-30 10:32:13 +02:00
add ( this , & Session : : attach , " Attach " ) ;
2009-10-12 21:01:02 +02:00
add ( this , & Session : : detach , " Detach " ) ;
2010-08-25 10:37:58 +02:00
add ( this , & Session : : getFlags , " GetFlags " ) ;
2010-08-25 15:21:08 +02:00
add ( this , & Session : : getNormalConfigName , " GetConfigName " ) ;
2010-02-03 14:17:37 +01:00
add ( static_cast < ReadOperations * > ( this ) , & ReadOperations : : getConfigs , " GetConfigs " ) ;
2009-11-26 09:37:19 +01:00
add ( static_cast < ReadOperations * > ( this ) , & ReadOperations : : getConfig , " GetConfig " ) ;
2009-10-12 21:01:02 +02:00
add ( this , & Session : : setConfig , " SetConfig " ) ;
2009-11-26 09:37:19 +01:00
add ( static_cast < ReadOperations * > ( this ) , & ReadOperations : : getReports , " GetReports " ) ;
add ( static_cast < ReadOperations * > ( this ) , & ReadOperations : : checkSource , " CheckSource " ) ;
add ( static_cast < ReadOperations * > ( this ) , & ReadOperations : : getDatabases , " GetDatabases " ) ;
2009-11-11 10:55:15 +01:00
add ( this , & Session : : sync , " Sync " ) ;
2009-10-12 21:01:02 +02:00
add ( this , & Session : : abort , " Abort " ) ;
add ( this , & Session : : suspend , " Suspend " ) ;
add ( this , & Session : : getStatus , " GetStatus " ) ;
add ( this , & Session : : getProgress , " GetProgress " ) ;
2009-12-14 06:59:02 +01:00
add ( this , & Session : : restore , " Restore " ) ;
2010-02-03 02:50:56 +01:00
add ( this , & Session : : checkPresence , " checkPresence " ) ;
2010-03-15 02:33:43 +01:00
add ( this , & Session : : execute , " Execute " ) ;
2009-10-12 21:01:02 +02:00
add ( emitStatus ) ;
add ( emitProgress ) ;
}
2009-09-08 19:11:49 +02:00
2010-09-30 14:27:02 +02:00
void Session : : done ( )
2009-09-08 19:11:49 +02:00
{
2010-09-30 14:27:02 +02:00
if ( m_done ) {
return ;
}
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
/* update auto sync manager when a config is changed */
2010-09-30 14:27:02 +02:00
if ( m_setConfig ) {
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
m_server . getAutoSyncManager ( ) . update ( m_configName ) ;
}
2009-09-08 19:11:49 +02:00
m_server . dequeue ( this ) ;
2010-08-25 21:03:31 +02:00
// now tell other clients about config change?
if ( m_setConfig ) {
m_server . configChanged ( ) ;
}
2010-09-30 14:27:02 +02:00
// typically set by m_server.dequeue(), but let's really make sure...
m_active = false ;
m_done = true ;
}
Session : : ~ Session ( )
{
done ( ) ;
2009-09-08 19:11:49 +02:00
}
void Session : : setActive ( bool active )
{
m_active = active ;
if ( active ) {
2009-11-19 13:44:22 +01:00
if ( m_syncStatus = = SYNC_QUEUEING ) {
m_syncStatus = SYNC_IDLE ;
fireStatus ( true ) ;
}
2009-09-08 19:11:49 +02:00
boost : : shared_ptr < Connection > c = m_connection . lock ( ) ;
if ( c ) {
c - > ready ( ) ;
}
}
}
2009-09-14 12:58:22 +02:00
void Session : : syncProgress ( sysync : : TProgressEventEnum type ,
int32_t extra1 , int32_t extra2 , int32_t extra3 )
{
2009-10-09 09:22:13 +02:00
switch ( type ) {
case sysync : : PEV_SESSIONSTART :
m_progData . setStep ( ProgressData : : PRO_SYNC_INIT ) ;
fireProgress ( true ) ;
break ;
case sysync : : PEV_SESSIONEND :
2009-11-30 10:16:09 +01:00
if ( ( uint32_t ) extra1 ! = m_error ) {
m_error = extra1 ;
fireStatus ( true ) ;
}
2009-10-09 09:22:13 +02:00
m_progData . setStep ( ProgressData : : PRO_SYNC_INVALID ) ;
fireProgress ( true ) ;
break ;
case sysync : : PEV_SENDSTART :
m_progData . sendStart ( ) ;
break ;
case sysync : : PEV_SENDEND :
case sysync : : PEV_RECVSTART :
case sysync : : PEV_RECVEND :
m_progData . receiveEnd ( ) ;
fireProgress ( ) ;
break ;
case sysync : : PEV_DISPLAY100 :
case sysync : : PEV_SUSPENDCHECK :
case sysync : : PEV_DELETING :
break ;
case sysync : : PEV_SUSPENDING :
m_syncStatus = SYNC_SUSPEND ;
fireStatus ( true ) ;
break ;
default :
;
}
2009-09-14 12:58:22 +02:00
}
void Session : : sourceProgress ( sysync : : TProgressEventEnum type ,
SyncSource & source ,
int32_t extra1 , int32_t extra2 , int32_t extra3 )
{
2009-12-22 09:47:31 +01:00
switch ( m_runOperation ) {
case OP_SYNC : {
SourceProgress & progress = m_sourceProgress [ source . getName ( ) ] ;
SourceStatus & status = m_sourceStatus [ source . getName ( ) ] ;
switch ( type ) {
case sysync : : PEV_SYNCSTART :
if ( source . getFinalSyncMode ( ) ! = SYNC_NONE ) {
m_progData . setStep ( ProgressData : : PRO_SYNC_UNINIT ) ;
fireProgress ( ) ;
}
break ;
case sysync : : PEV_SYNCEND :
if ( source . getFinalSyncMode ( ) ! = SYNC_NONE ) {
status . set ( PrettyPrintSyncMode ( source . getFinalSyncMode ( ) ) , " done " , extra1 ) ;
fireStatus ( true ) ;
}
break ;
case sysync : : PEV_PREPARING :
if ( source . getFinalSyncMode ( ) ! = SYNC_NONE ) {
progress . m_phase = " preparing " ;
progress . m_prepareCount = extra1 ;
progress . m_prepareTotal = extra2 ;
m_progData . itemPrepare ( ) ;
fireProgress ( true ) ;
}
break ;
case sysync : : PEV_ITEMSENT :
if ( source . getFinalSyncMode ( ) ! = SYNC_NONE ) {
progress . m_phase = " sending " ;
progress . m_sendCount = extra1 ;
progress . m_sendTotal = extra2 ;
fireProgress ( true ) ;
}
break ;
case sysync : : PEV_ITEMRECEIVED :
if ( source . getFinalSyncMode ( ) ! = SYNC_NONE ) {
progress . m_phase = " receiving " ;
progress . m_receiveCount = extra1 ;
progress . m_receiveTotal = extra2 ;
m_progData . itemReceive ( source . getName ( ) , extra1 , extra2 ) ;
fireProgress ( true ) ;
}
break ;
case sysync : : PEV_ALERTED :
if ( source . getFinalSyncMode ( ) ! = SYNC_NONE ) {
status . set ( PrettyPrintSyncMode ( source . getFinalSyncMode ( ) ) , " running " , 0 ) ;
fireStatus ( true ) ;
m_progData . setStep ( ProgressData : : PRO_SYNC_DATA ) ;
m_progData . addSyncMode ( source . getFinalSyncMode ( ) ) ;
fireProgress ( ) ;
}
break ;
default :
;
2009-10-09 09:22:13 +02:00
}
break ;
2009-12-22 09:47:31 +01:00
}
case OP_RESTORE : {
switch ( type ) {
case sysync : : PEV_ALERTED :
// count the total number of sources to be restored
m_restoreSrcTotal + + ;
break ;
case sysync : : PEV_SYNCSTART : {
if ( source . getFinalSyncMode ( ) ! = SYNC_NONE ) {
SourceStatus & status = m_sourceStatus [ source . getName ( ) ] ;
// set statuses as 'restore-from-backup'
status . set ( PrettyPrintSyncMode ( source . getFinalSyncMode ( ) ) , " running " , 0 ) ;
fireStatus ( true ) ;
}
break ;
2009-10-09 09:22:13 +02:00
}
2009-12-22 09:47:31 +01:00
case sysync : : PEV_SYNCEND : {
if ( source . getFinalSyncMode ( ) ! = SYNC_NONE ) {
m_restoreSrcEnd + + ;
SourceStatus & status = m_sourceStatus [ source . getName ( ) ] ;
status . set ( PrettyPrintSyncMode ( source . getFinalSyncMode ( ) ) , " done " , 0 ) ;
m_progress = 100 * m_restoreSrcEnd / m_restoreSrcTotal ;
fireStatus ( true ) ;
fireProgress ( true ) ;
}
break ;
2009-10-09 09:22:13 +02:00
}
2009-12-22 09:47:31 +01:00
default :
break ;
2009-10-09 09:22:13 +02:00
}
break ;
2009-12-22 09:47:31 +01:00
}
2009-10-09 09:22:13 +02:00
default :
2009-12-22 09:47:31 +01:00
break ;
2009-10-09 09:22:13 +02:00
}
2009-09-14 12:58:22 +02:00
}
void Session : : run ( )
{
2010-03-15 02:33:43 +01:00
if ( m_runOperation ! = OP_NULL ) {
2009-09-14 12:58:22 +02:00
try {
2009-11-27 06:57:52 +01:00
m_syncStatus = SYNC_RUNNING ;
fireStatus ( true ) ;
2009-12-14 06:59:02 +01:00
switch ( m_runOperation ) {
case OP_SYNC : {
SyncMLStatus status ;
m_progData . setStep ( ProgressData : : PRO_SYNC_PREPARE ) ;
try {
status = m_sync - > sync ( ) ;
} catch ( . . . ) {
status = m_sync - > handleException ( ) ;
}
if ( ! m_error ) {
m_error = status ;
}
// if there is a connection, then it is no longer needed
boost : : shared_ptr < Connection > c = m_connection . lock ( ) ;
if ( c ) {
c - > shutdown ( ) ;
}
2010-03-04 02:53:04 +01:00
// report 'sync done' event to listener
if ( m_listener ) {
m_listener - > syncDone ( status ) ;
}
2009-12-14 06:59:02 +01:00
break ;
2009-11-19 09:37:58 +01:00
}
2009-12-14 06:59:02 +01:00
case OP_RESTORE :
m_sync - > restore ( m_restoreDir ,
m_restoreBefore ? SyncContext : : DATABASE_BEFORE_SYNC : SyncContext : : DATABASE_AFTER_SYNC ) ;
break ;
2010-03-15 02:33:43 +01:00
case OP_CMDLINE :
2010-04-14 10:38:05 +02:00
try {
m_cmdline - > run ( ) ;
} catch ( . . . ) {
SyncMLStatus status = Exception : : handle ( ) ;
if ( ! m_error ) {
m_error = status ;
}
}
2010-08-25 21:03:31 +02:00
m_setConfig = m_cmdline - > configWasModified ( ) ;
break ;
2009-12-14 06:59:02 +01:00
default :
break ;
} ;
2009-09-14 12:58:22 +02:00
} catch ( . . . ) {
2009-11-19 09:37:58 +01:00
// we must enter SYNC_DONE under all circumstances,
// even when failing during connection shutdown
m_syncStatus = SYNC_DONE ;
2010-01-14 08:26:54 +01:00
m_stepIsWaiting = false ;
2009-11-19 09:37:58 +01:00
fireStatus ( true ) ;
throw ;
2009-09-14 12:58:22 +02:00
}
2009-10-13 09:56:32 +02:00
m_syncStatus = SYNC_DONE ;
2010-01-14 08:26:54 +01:00
m_stepIsWaiting = false ;
2009-09-14 12:58:22 +02:00
fireStatus ( true ) ;
2010-03-15 02:33:43 +01:00
}
2009-09-14 12:58:22 +02:00
}
2009-12-25 09:17:58 +01:00
bool Session : : setFilters ( SyncConfig & config )
2009-11-26 09:37:19 +01:00
{
/** apply temporary configs to config */
config . setConfigFilter ( true , " " , m_syncFilter ) ;
// set all sources in the filter to config
BOOST_FOREACH ( const SourceFilters_t : : value_type & value , m_sourceFilters ) {
config . setConfigFilter ( false , value . first , value . second ) ;
}
2009-12-25 09:17:58 +01:00
return m_tempConfig ;
2009-11-26 09:37:19 +01:00
}
2009-12-07 07:26:01 +01:00
void Session : : setStepInfo ( bool isWaiting )
{
// if stepInfo doesn't change, then ignore it to avoid duplicate status info
if ( m_stepIsWaiting ! = isWaiting ) {
m_stepIsWaiting = isWaiting ;
fireStatus ( true ) ;
}
}
2009-12-14 06:59:02 +01:00
void Session : : restore ( const string & dir , bool before , const std : : vector < std : : string > & sources )
{
if ( ! m_active ) {
SE_THROW_EXCEPTION ( InvalidCall , " session is not active, call not allowed at this time " ) ;
}
2010-03-15 02:33:43 +01:00
if ( m_runOperation = = OP_RESTORE ) {
string msg = StringPrintf ( " restore started, cannot restore again " ) ;
SE_THROW_EXCEPTION ( InvalidCall , msg ) ;
} else if ( m_runOperation ! = OP_NULL ) {
2009-12-14 06:59:02 +01:00
// actually this never happen currently, for during the real restore process,
// it never poll the sources in default main context
2010-03-15 02:33:43 +01:00
string msg = StringPrintf ( " %s started, cannot restore " , runOpToString ( m_runOperation ) . c_str ( ) ) ;
2009-12-14 06:59:02 +01:00
SE_THROW_EXCEPTION ( InvalidCall , msg ) ;
}
m_sync . reset ( new DBusSync ( getConfigName ( ) , * this ) ) ;
if ( ! sources . empty ( ) ) {
BOOST_FOREACH ( const std : : string & source , sources ) {
FilterConfigNode : : ConfigFilter filter ;
filter [ SyncSourceConfig : : m_sourcePropSync . getName ( ) ] = " two-way " ;
m_sync - > setConfigFilter ( false , source , filter ) ;
}
// disable other sources
FilterConfigNode : : ConfigFilter disabled ;
disabled [ SyncSourceConfig : : m_sourcePropSync . getName ( ) ] = " disabled " ;
m_sync - > setConfigFilter ( false , " " , disabled ) ;
}
m_restoreBefore = before ;
m_restoreDir = dir ;
m_runOperation = OP_RESTORE ;
2009-12-22 09:47:31 +01:00
// initiate status and progress and sourceProgress is not calculated currently
BOOST_FOREACH ( const std : : string source ,
m_sync - > getSyncSources ( ) ) {
m_sourceStatus [ source ] ;
}
fireProgress ( true ) ;
fireStatus ( true ) ;
2009-12-14 06:59:02 +01:00
g_main_loop_quit ( loop ) ;
}
string Session : : runOpToString ( RunOperation op )
{
switch ( op ) {
case OP_SYNC :
return " sync " ;
case OP_RESTORE :
return " restore " ;
2010-03-15 02:33:43 +01:00
case OP_CMDLINE :
return " cmdline " ;
2009-12-14 06:59:02 +01:00
default :
return " " ;
} ;
}
2010-04-15 10:26:41 +02:00
void Session : : execute ( const vector < string > & args , const map < string , string > & vars )
2010-03-15 02:33:43 +01:00
{
if ( ! m_active ) {
SE_THROW_EXCEPTION ( InvalidCall , " session is not active, call not allowed at this time " ) ;
}
if ( m_runOperation = = OP_CMDLINE ) {
SE_THROW_EXCEPTION ( InvalidCall , " cmdline started, cannot start again " ) ;
} else if ( m_runOperation ! = OP_NULL ) {
string msg = StringPrintf ( " %s started, cannot start cmdline " , runOpToString ( m_runOperation ) . c_str ( ) ) ;
SE_THROW_EXCEPTION ( InvalidCall , msg ) ;
}
//create ostream with a specified streambuf
2010-04-15 10:26:41 +02:00
m_cmdline . reset ( new CmdlineWrapper ( * this , args , vars ) ) ;
2010-04-06 04:59:01 +02:00
if ( ! m_cmdline - > parse ( ) ) {
m_cmdline . reset ( ) ;
SE_THROW_EXCEPTION ( DBusSyncException , " arguments parsing error " ) ;
}
2010-03-15 02:33:43 +01:00
m_runOperation = OP_CMDLINE ;
g_main_loop_quit ( loop ) ;
}
2009-12-24 08:20:25 +01:00
inline void insertPair ( std : : map < string , string > & params ,
const string & key ,
const string & value )
{
if ( ! value . empty ( ) ) {
params . insert ( pair < string , string > ( key , value ) ) ;
}
}
string Session : : askPassword ( const string & passwordName ,
const string & descr ,
const ConfigPasswordKey & key )
{
std : : map < string , string > params ;
insertPair ( params , " description " , descr ) ;
insertPair ( params , " user " , key . user ) ;
insertPair ( params , " SyncML server " , key . server ) ;
insertPair ( params , " domain " , key . domain ) ;
insertPair ( params , " object " , key . object ) ;
insertPair ( params , " protocol " , key . protocol ) ;
insertPair ( params , " authtype " , key . authtype ) ;
insertPair ( params , " port " , key . port ? StringPrintf ( " %u " , key . port ) : " " ) ;
boost : : shared_ptr < InfoReq > req = m_server . createInfoReq ( " password " , params , this ) ;
std : : map < string , string > response ;
if ( req - > wait ( response ) = = InfoReq : : ST_OK ) {
2010-04-13 11:07:03 +02:00
std : : map < string , string > : : iterator it = response . find ( " password " ) ;
if ( it = = response . end ( ) ) {
SE_THROW_EXCEPTION_STATUS ( StatusException , " user didn't provide password, abort " , SyncMLStatus ( sysync : : LOCERR_USERABORT ) ) ;
} else {
return it - > second ;
}
2009-12-24 08:20:25 +01:00
}
2010-03-17 08:54:38 +01:00
SE_THROW_EXCEPTION_STATUS ( StatusException , " can't get the password from clients. The password request is ' " + req - > getStatusStr ( ) + " ' " , STATUS_PASSWORD_TIMEOUT ) ;
2009-12-24 08:20:25 +01:00
return " " ;
}
2010-02-03 02:50:56 +01:00
/*Implementation of Session.CheckPresence */
void Session : : checkPresence ( string & status )
{
vector < string > transport ;
m_server . m_presence . checkPresence ( m_configName , status , transport ) ;
}
2010-03-04 02:53:04 +01:00
void Session : : syncSuccessStart ( )
{
// if listener, report 'sync started' to it
if ( m_listener ) {
m_listener - > syncSuccessStart ( ) ;
}
}
SessionListener * Session : : addListener ( SessionListener * listener )
{
SessionListener * old = m_listener ;
m_listener = listener ;
return old ;
}
2009-10-09 09:22:13 +02:00
/************************ ProgressData implementation *****************/
ProgressData : : ProgressData ( int32_t & progress )
: m_progress ( progress ) ,
m_step ( PRO_SYNC_INVALID ) ,
m_sendCounts ( 0 ) ,
m_internalMode ( INTERNAL_NONE )
{
/**
* init default units of each step
*/
float totalUnits = 0.0 ;
for ( int i = 0 ; i < PRO_SYNC_TOTAL ; i + + ) {
float units = getDefaultUnits ( ( ProgressStep ) i ) ;
m_syncUnits [ i ] = units ;
totalUnits + = units ;
}
m_propOfUnit = 1.0 / totalUnits ;
/**
* init default sync step proportions . each step stores proportions of
* its previous steps and itself .
*/
m_syncProp [ 0 ] = 0 ;
for ( int i = 1 ; i < PRO_SYNC_TOTAL - 1 ; i + + ) {
m_syncProp [ i ] = m_syncProp [ i - 1 ] + m_syncUnits [ i ] / totalUnits ;
}
m_syncProp [ PRO_SYNC_TOTAL - 1 ] = 1.0 ;
}
void ProgressData : : setStep ( ProgressStep step )
{
if ( m_step ! = step ) {
/** if state is changed, progress is set as the end of current step*/
m_progress = 100.0 * m_syncProp [ ( int ) m_step ] ;
m_step = step ; ///< change to new state
m_sendCounts = 0 ; ///< clear send/receive counts
m_source = " " ; ///< clear source
}
}
void ProgressData : : sendStart ( )
{
checkInternalMode ( ) ;
m_sendCounts + + ;
/* self adapts. If a new send and not default, we need re-calculate proportions */
if ( m_sendCounts > MSG_SEND_RECEIVE_TIMES ) {
m_syncUnits [ ( int ) m_step ] + = 1 ;
recalc ( ) ;
}
/**
* If in the send operation of PRO_SYNC_UNINIT , it often takes extra time
* to send message due to items handling
*/
if ( m_step = = PRO_SYNC_UNINIT & & m_syncUnits [ ( int ) m_step ] ! = MSG_SEND_RECEIVE_TIMES ) {
updateProg ( DATA_PREPARE_RATIO ) ;
}
}
void ProgressData : : receiveEnd ( )
{
/**
* often receiveEnd is the last operation of each step by default .
* If more send / receive , then we need expand proportion of current
* step and re - calc them
*/
updateProg ( m_syncUnits [ ( int ) m_step ] ) ;
}
void ProgressData : : addSyncMode ( SyncMode mode )
{
switch ( mode ) {
case SYNC_TWO_WAY :
case SYNC_SLOW :
m_internalMode | = INTERNAL_TWO_WAY ;
break ;
case SYNC_ONE_WAY_FROM_CLIENT :
case SYNC_REFRESH_FROM_CLIENT :
m_internalMode | = INTERNAL_ONLY_TO_CLIENT ;
break ;
case SYNC_ONE_WAY_FROM_SERVER :
case SYNC_REFRESH_FROM_SERVER :
m_internalMode | = INTERNAL_ONLY_TO_SERVER ;
break ;
default :
;
} ;
}
void ProgressData : : itemPrepare ( )
{
checkInternalMode ( ) ;
/**
* only the first PEV_ITEMPREPARE event takes some time
* due to data access , other events don ' t according to
* profiling data
*/
if ( m_source . empty ( ) ) {
m_source = " source " ; ///< use this to check whether itemPrepare occurs
updateProg ( DATA_PREPARE_RATIO ) ;
}
}
void ProgressData : : itemReceive ( const string & source , int count , int total )
{
/**
* source is used to check whether a new source is received
* If the first source , we compare its total number and default number
* then re - calc sync units
*/
if ( m_source . empty ( ) ) {
m_source = source ;
if ( total ! = 0 ) {
m_syncUnits [ PRO_SYNC_UNINIT ] + = ONEITEM_RECEIVE_RATIO * ( total - DEFAULT_ITEMS ) ;
recalc ( ) ;
}
/** if another new source, add them into sync units */
} else if ( m_source ! = source ) {
m_source = source ;
if ( total ! = 0 ) {
m_syncUnits [ PRO_SYNC_UNINIT ] + = ONEITEM_RECEIVE_RATIO * total ;
recalc ( ) ;
}
}
updateProg ( ONEITEM_RECEIVE_RATIO ) ;
}
void ProgressData : : updateProg ( float ratio )
{
m_progress + = m_propOfUnit * 100 * ratio ;
m_syncUnits [ ( int ) m_step ] - = ratio ;
}
/** dynamically adapt the proportion of each step by their current units */
void ProgressData : : recalc ( )
{
float units = getRemainTotalUnits ( ) ;
if ( std : : abs ( units ) < std : : numeric_limits < float > : : epsilon ( ) ) {
m_propOfUnit = 0.0 ;
} else {
m_propOfUnit = ( 100.0 - m_progress ) / ( 100.0 * units ) ;
}
if ( m_step ! = PRO_SYNC_TOTAL - 1 ) {
m_syncProp [ ( int ) m_step ] = m_progress / 100.0 + m_syncUnits [ ( int ) m_step ] * m_propOfUnit ;
for ( int i = ( ( int ) m_step ) + 1 ; i < PRO_SYNC_TOTAL - 1 ; i + + ) {
m_syncProp [ i ] = m_syncProp [ i - 1 ] + m_syncUnits [ i ] * m_propOfUnit ;
}
}
}
void ProgressData : : checkInternalMode ( )
{
if ( ! m_internalMode ) {
return ;
} else if ( m_internalMode & INTERNAL_TWO_WAY ) {
// don't adjust
} else if ( m_internalMode & INTERNAL_ONLY_TO_CLIENT ) {
// only to client, remove units of prepare and send
m_syncUnits [ PRO_SYNC_DATA ] - = ( ONEITEM_RECEIVE_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO ) ;
recalc ( ) ;
} else if ( m_internalMode & INTERNAL_ONLY_TO_SERVER ) {
// only to server, remove units of receive
m_syncUnits [ PRO_SYNC_UNINIT ] - = ( ONEITEM_RECEIVE_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO ) ;
recalc ( ) ;
}
m_internalMode = INTERNAL_NONE ;
}
float ProgressData : : getRemainTotalUnits ( )
{
float total = 0.0 ;
for ( int i = ( int ) m_step ; i < PRO_SYNC_TOTAL ; i + + ) {
total + = m_syncUnits [ i ] ;
}
return total ;
}
float ProgressData : : getDefaultUnits ( ProgressStep step )
{
switch ( step ) {
case PRO_SYNC_PREPARE :
return PRO_SYNC_PREPARE_RATIO ;
case PRO_SYNC_INIT :
return CONN_SETUP_RATIO + MSG_SEND_RECEIVE_TIMES ;
case PRO_SYNC_DATA :
return ONEITEM_SEND_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO + MSG_SEND_RECEIVE_TIMES ;
case PRO_SYNC_UNINIT :
return ONEITEM_RECEIVE_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO + MSG_SEND_RECEIVE_TIMES ;
default :
return 0 ;
} ;
}
2009-09-08 19:11:49 +02:00
/************************ Connection implementation *****************/
void Connection : : failed ( const std : : string & reason )
{
if ( m_failure . empty ( ) ) {
m_failure = reason ;
2009-09-30 17:24:55 +02:00
if ( m_session ) {
m_session - > setConnectionError ( reason ) ;
}
2009-09-08 19:11:49 +02:00
}
if ( m_state ! = FAILED ) {
abort ( ) ;
}
m_state = FAILED ;
}
2009-09-27 22:48:04 +02:00
std : : string Connection : : buildDescription ( const StringMap & peer )
2009-09-08 19:11:49 +02:00
{
2009-09-27 22:48:04 +02:00
StringMap : : const_iterator
2009-09-08 19:11:49 +02:00
desc = peer . find ( " description " ) ,
id = peer . find ( " id " ) ,
trans = peer . find ( " transport " ) ,
trans_desc = peer . find ( " transport_description " ) ;
std : : string buffer ;
buffer . reserve ( 256 ) ;
if ( desc ! = peer . end ( ) ) {
buffer + = desc - > second ;
}
if ( id ! = peer . end ( ) | | trans ! = peer . end ( ) ) {
if ( ! buffer . empty ( ) ) {
buffer + = " " ;
}
buffer + = " ( " ;
if ( id ! = peer . end ( ) ) {
buffer + = id - > second ;
if ( trans ! = peer . end ( ) ) {
buffer + = " via " ;
}
}
if ( trans ! = peer . end ( ) ) {
buffer + = trans - > second ;
if ( trans_desc ! = peer . end ( ) ) {
buffer + = " " ;
buffer + = trans_desc - > second ;
}
}
buffer + = " ) " ;
}
return buffer ;
}
2009-10-30 07:25:53 +01:00
void Connection : : wakeupSession ( )
{
if ( m_loop ) {
g_main_loop_quit ( m_loop ) ;
m_loop = NULL ;
}
}
2009-09-08 19:11:49 +02:00
void Connection : : process ( const Caller_t & caller ,
const std : : pair < size_t , const uint8_t * > & message ,
const std : : string & message_type )
{
2009-11-09 21:10:10 +01:00
SE_LOG_DEBUG ( NULL , NULL , " D-Bus client %s sends %lu bytes via connection %s, %s " ,
2009-09-08 19:11:49 +02:00
caller . c_str ( ) ,
2009-11-30 21:23:36 +01:00
( unsigned long ) message . first ,
2009-11-09 21:10:10 +01:00
getPath ( ) ,
2009-09-08 19:11:49 +02:00
message_type . c_str ( ) ) ;
boost : : shared_ptr < Client > client ( m_server . findClient ( caller ) ) ;
if ( ! client ) {
throw runtime_error ( " unknown client " ) ;
}
boost : : shared_ptr < Connection > myself =
boost : : static_pointer_cast < Connection , Resource > ( client - > findResource ( this ) ) ;
if ( ! myself ) {
throw runtime_error ( " client does not own connection " ) ;
}
2009-09-30 17:24:55 +02:00
// any kind of error from now on terminates the connection
try {
switch ( m_state ) {
case SETUP : {
std : : string config ;
2009-11-09 21:10:10 +01:00
std : : string peerDeviceID ;
2009-09-30 17:24:55 +02:00
bool serverMode = false ;
// check message type, determine whether we act
// as client or server, choose config
if ( message_type = = " HTTP Config " ) {
// type used for testing, payload is config name
config . assign ( reinterpret_cast < const char * > ( message . second ) ,
message . first ) ;
2010-03-15 14:14:12 +01:00
} else if ( message_type = = TransportAgent : : m_contentTypeServerAlertedNotificationDS ) {
2009-09-24 12:54:36 +02:00
sysync : : SanPackage san ;
2010-03-15 14:14:12 +01:00
if ( san . PassSan ( const_cast < uint8_t * > ( message . second ) , message . first , 2 ) | | san . GetHeader ( ) ) {
2009-09-24 12:54:36 +02:00
// We are very tolerant regarding the content of the message.
// If it doesn't parse, try to do something useful anyway.
2010-02-23 10:01:37 +01:00
// only for SAN 1.2, for SAN 1.0/1.1 we can not be sure
// whether it is a SAN package or a normal sync pacakge
if ( message_type = = TransportAgent : : m_contentTypeServerAlertedNotificationDS ) {
config = " default " ;
SE_LOG_DEBUG ( NULL , NULL , " SAN parsing failed, falling back to 'default' config " ) ;
}
2010-03-12 02:16:45 +01:00
} else { //Server alerted notification case
2009-09-24 12:54:36 +02:00
// Extract server ID and match it against a server
// configuration. Multiple different peers might use the
// same serverID ("PC Suite"), so check properties of the
// of our configs first before going back to the name itself.
std : : string serverID = san . fServerID ;
Configuration templates matching: match templates based on metadata
Introduced TemplateConfig to abstracting the template configuration structure,
the template metadata used for matching is also parsed here.
The fields introduced in the metadata are:
PeerIsClient: identify whether this is a server side configuration or a client
side configuration.
Fingerprint: the matching string for this template, it is a comma separated string
with each string modeled as: "Manufacture_Model". The first substring is also
used as the name to identify this template so that user can select the template
by this name.
eg:
Nokia 7210c: Nokia_7210c
SyncEvolution server: SyncEvolutionServer, SyncEvolution
ScheduleWorld: ScheduleWorld,default
SyncEvolution client: SyncEvolutionClient, SyncEvolution
Description: this is a just a descriptive string not used for matching.
GetServerTemplates is changed to add another "devices" parameter to identify
it is asking for templates for a list of "devices". Each device is a tuple
<matchstring (devicename), matchMode (server/client/all)>.
TemplateList as the return type, which is a list of class TemplateDescription
so that we can also return enough information for corresponding templates. This
list is sorted by the 3-tuple <finger, rank, name>.
Add MatchServerTemplates method which will iterating all templates inside the
folder and match against the input parameter and finally return a sorted
list of matched templates.
The atcually fuzzy match algorithm is based on a LCS (added in the following
commit).
Cmdline interface is changed accordingly:
--template ? is changed to --template ?[string], so that user use the former
case to match all templates for a tradiontial SyncML client and the latter case
to match templates related to an input string.
SyncConfig API is also renamed (Server -> Peer) because both server/client
configuration/template are handled.
The original configuration template (Funambol and ScheduleWorld) has been moved
to the new template structure (under servers), they also have a .template.ini
file added so that they can be matched and picked up. All templates for
supported servers still have built-in template support in the code as before.
Templates for SyncEvolution based server is also added.
Server side templates are added (Nokia default, Nokia_7210c and SyncEvolutionServer).
Add unit test for the new template match use case.
2010-01-19 08:01:05 +01:00
SyncConfig : : ConfigList servers = SyncConfig : : getConfigs ( ) ;
BOOST_FOREACH ( const SyncConfig : : ConfigList : : value_type & server ,
2009-12-21 07:56:23 +01:00
servers ) {
SyncConfig conf ( server . first ) ;
2010-01-29 06:27:06 +01:00
vector < string > urls = conf . getSyncURL ( ) ;
BOOST_FOREACH ( const string & url , urls ) {
if ( url = = serverID ) {
config = server . first ;
break ;
}
}
if ( ! config . empty ( ) ) {
2009-12-21 07:56:23 +01:00
break ;
}
}
2009-09-24 12:54:36 +02:00
2009-12-21 07:56:23 +01:00
// for Bluetooth transports match against mac address.
StringMap : : const_iterator id = m_peer . find ( " id " ) ,
trans = m_peer . find ( " transport " ) ;
if ( trans ! = m_peer . end ( ) & & id ! = m_peer . end ( ) ) {
if ( trans - > second = = " org.openobex.obexd " ) {
2010-03-12 02:16:45 +01:00
m_peerBtAddr = id - > second . substr ( 0 , id - > second . find ( " + " ) ) ;
Configuration templates matching: match templates based on metadata
Introduced TemplateConfig to abstracting the template configuration structure,
the template metadata used for matching is also parsed here.
The fields introduced in the metadata are:
PeerIsClient: identify whether this is a server side configuration or a client
side configuration.
Fingerprint: the matching string for this template, it is a comma separated string
with each string modeled as: "Manufacture_Model". The first substring is also
used as the name to identify this template so that user can select the template
by this name.
eg:
Nokia 7210c: Nokia_7210c
SyncEvolution server: SyncEvolutionServer, SyncEvolution
ScheduleWorld: ScheduleWorld,default
SyncEvolution client: SyncEvolutionClient, SyncEvolution
Description: this is a just a descriptive string not used for matching.
GetServerTemplates is changed to add another "devices" parameter to identify
it is asking for templates for a list of "devices". Each device is a tuple
<matchstring (devicename), matchMode (server/client/all)>.
TemplateList as the return type, which is a list of class TemplateDescription
so that we can also return enough information for corresponding templates. This
list is sorted by the 3-tuple <finger, rank, name>.
Add MatchServerTemplates method which will iterating all templates inside the
folder and match against the input parameter and finally return a sorted
list of matched templates.
The atcually fuzzy match algorithm is based on a LCS (added in the following
commit).
Cmdline interface is changed accordingly:
--template ? is changed to --template ?[string], so that user use the former
case to match all templates for a tradiontial SyncML client and the latter case
to match templates related to an input string.
SyncConfig API is also renamed (Server -> Peer) because both server/client
configuration/template are handled.
The original configuration template (Funambol and ScheduleWorld) has been moved
to the new template structure (under servers), they also have a .template.ini
file added so that they can be matched and picked up. All templates for
supported servers still have built-in template support in the code as before.
Templates for SyncEvolution based server is also added.
Server side templates are added (Nokia default, Nokia_7210c and SyncEvolutionServer).
Add unit test for the new template match use case.
2010-01-19 08:01:05 +01:00
BOOST_FOREACH ( const SyncConfig : : ConfigList : : value_type & server ,
2009-12-21 07:56:23 +01:00
servers ) {
SyncConfig conf ( server . first ) ;
2010-01-29 06:27:06 +01:00
vector < string > urls = conf . getSyncURL ( ) ;
BOOST_FOREACH ( string & url , urls ) {
url = url . substr ( 0 , url . find ( " + " ) ) ;
SE_LOG_DEBUG ( NULL , NULL , " matching against %s " , url . c_str ( ) ) ;
2010-03-12 02:16:45 +01:00
if ( url . find ( " obex-bt:// " ) = = 0 & & url . substr ( strlen ( " obex-bt:// " ) , url . npos ) = = m_peerBtAddr ) {
2010-01-29 06:27:06 +01:00
config = server . first ;
break ;
}
}
if ( ! config . empty ( ) ) {
2009-12-21 07:56:23 +01:00
break ;
2010-01-29 06:27:06 +01:00
}
2009-12-21 07:56:23 +01:00
}
}
2009-09-24 12:54:36 +02:00
}
2009-12-21 07:56:23 +01:00
2009-09-24 12:54:36 +02:00
if ( config . empty ( ) ) {
Configuration templates matching: match templates based on metadata
Introduced TemplateConfig to abstracting the template configuration structure,
the template metadata used for matching is also parsed here.
The fields introduced in the metadata are:
PeerIsClient: identify whether this is a server side configuration or a client
side configuration.
Fingerprint: the matching string for this template, it is a comma separated string
with each string modeled as: "Manufacture_Model". The first substring is also
used as the name to identify this template so that user can select the template
by this name.
eg:
Nokia 7210c: Nokia_7210c
SyncEvolution server: SyncEvolutionServer, SyncEvolution
ScheduleWorld: ScheduleWorld,default
SyncEvolution client: SyncEvolutionClient, SyncEvolution
Description: this is a just a descriptive string not used for matching.
GetServerTemplates is changed to add another "devices" parameter to identify
it is asking for templates for a list of "devices". Each device is a tuple
<matchstring (devicename), matchMode (server/client/all)>.
TemplateList as the return type, which is a list of class TemplateDescription
so that we can also return enough information for corresponding templates. This
list is sorted by the 3-tuple <finger, rank, name>.
Add MatchServerTemplates method which will iterating all templates inside the
folder and match against the input parameter and finally return a sorted
list of matched templates.
The atcually fuzzy match algorithm is based on a LCS (added in the following
commit).
Cmdline interface is changed accordingly:
--template ? is changed to --template ?[string], so that user use the former
case to match all templates for a tradiontial SyncML client and the latter case
to match templates related to an input string.
SyncConfig API is also renamed (Server -> Peer) because both server/client
configuration/template are handled.
The original configuration template (Funambol and ScheduleWorld) has been moved
to the new template structure (under servers), they also have a .template.ini
file added so that they can be matched and picked up. All templates for
supported servers still have built-in template support in the code as before.
Templates for SyncEvolution based server is also added.
Server side templates are added (Nokia default, Nokia_7210c and SyncEvolutionServer).
Add unit test for the new template match use case.
2010-01-19 08:01:05 +01:00
BOOST_FOREACH ( const SyncConfig : : ConfigList : : value_type & server ,
2009-09-24 12:54:36 +02:00
servers ) {
if ( server . first = = serverID ) {
config = serverID ;
break ;
}
}
}
2010-03-12 02:16:45 +01:00
// create a default configuration name if none matched
2009-09-24 12:54:36 +02:00
if ( config . empty ( ) ) {
2010-03-12 02:16:45 +01:00
config = serverID + " _ " + getCurrentTime ( ) ;
SE_LOG_DEBUG ( NULL ,
NULL ,
" SAN Server ID '%s' unknown, falling back to automatically created '%s' config " ,
serverID . c_str ( ) , config . c_str ( ) ) ;
2009-09-24 12:54:36 +02:00
}
SE_LOG_DEBUG ( NULL , NULL , " SAN sync with config %s " , config . c_str ( ) ) ;
2010-03-12 02:16:45 +01:00
m_SANContent . reset ( new SANContent ( ) ) ;
2009-09-24 12:54:36 +02:00
// extract number of sources
int numSources = san . fNSync ;
int syncType ;
uint32_t contentType ;
std : : string serverURI ;
if ( ! numSources ) {
2010-03-12 02:16:45 +01:00
SE_LOG_DEBUG ( NULL , NULL , " SAN message with no sources, using selected modes " ) ;
// Synchronize all known sources with the default mode.
2009-09-24 12:54:36 +02:00
if ( san . GetNthSync ( 0 , syncType , contentType , serverURI ) ) {
SE_LOG_DEBUG ( NULL , NULL , " SAN invalid header, using default modes " ) ;
} else if ( syncType < SYNC_FIRST | | syncType > SYNC_LAST ) {
SE_LOG_DEBUG ( NULL , NULL , " SAN invalid sync type %d, using default modes " , syncType ) ;
} else {
m_syncMode = PrettyPrintSyncMode ( SyncMode ( syncType ) , true ) ;
SE_LOG_DEBUG ( NULL , NULL , " SAN sync mode for all configured sources: %s " , m_syncMode . c_str ( ) ) ;
}
} else {
for ( int sync = 1 ; sync < = numSources ; sync + + ) {
if ( san . GetNthSync ( sync , syncType , contentType , serverURI ) ) {
SE_LOG_DEBUG ( NULL , NULL , " SAN invalid sync entry #%d " , sync ) ;
} else if ( syncType < SYNC_FIRST | | syncType > SYNC_LAST ) {
SE_LOG_DEBUG ( NULL , NULL , " SAN invalid sync type %d for entry #%d, ignoring entry " , syncType , sync ) ;
} else {
std : : string syncMode = PrettyPrintSyncMode ( SyncMode ( syncType ) , true ) ;
2010-03-12 02:16:45 +01:00
m_SANContent - > m_syncType . push_back ( syncMode ) ;
m_SANContent - > m_serverURI . push_back ( serverURI ) ;
m_SANContent - > m_contentType . push_back ( contentType ) ;
2009-09-24 12:54:36 +02:00
}
}
}
2009-09-30 17:24:55 +02:00
}
// TODO: use the session ID set by the server if non-null
2010-06-30 11:35:11 +02:00
} else if ( // relaxed checking for XML: ignore stuff like "; CHARSET=UTF-8"
message_type . substr ( 0 , message_type . find ( ' ; ' ) ) = = TransportAgent : : m_contentTypeSyncML | |
2010-03-15 14:14:12 +01:00
message_type = = TransportAgent : : m_contentTypeSyncWBXML ) {
// run a new SyncML session as server
serverMode = true ;
if ( m_peer . find ( " config " ) = = m_peer . end ( ) & &
! m_peer [ " config " ] . empty ( ) ) {
SE_LOG_DEBUG ( NULL , NULL , " ignoring pre-chosen config '%s' " ,
m_peer [ " config " ] . c_str ( ) ) ;
}
// peek into the data to extract the locURI = device ID,
// then use it to find the configuration
SyncContext : : SyncMLMessageInfo info ;
info = SyncContext : : analyzeSyncMLMessage ( reinterpret_cast < const char * > ( message . second ) ,
message . first ,
message_type ) ;
if ( info . m_deviceID . empty ( ) ) {
// TODO: proper exception
throw runtime_error ( " could not extract LocURI=deviceID from initial message " ) ;
}
BOOST_FOREACH ( const SyncConfig : : ConfigList : : value_type & entry ,
SyncConfig : : getConfigs ( ) ) {
SyncConfig peer ( entry . first ) ;
if ( info . m_deviceID = = peer . getRemoteDevID ( ) ) {
config = entry . first ;
command line + daemon: usability improvements (MB #5043)
This patch changes what is shown to the user and how the user
interacts with the command line. Details below.
"--use-daemon [yes/no]" implies that yes/no is optional (square
brackets!), with "yes" being the option that could be expected for a
plain "--use-daemon" parameter. But the implementation always expects
a "yes" or "no" parameter. The original format suggested was
"--use-daemon[=yes/no]"
This patch switches to that format, changes --use-daemon into --daemon
(to be consistent with --keyring) and enables the same format for
--keyring. Although not documented, 0/f/false and 1/t/true are also
accepted. Because the value becomes part of the parameter, m_argc checks
had to be adapted.
The documentation for "--use-daemon" was inserted in the middle of the
"--keyring documentation".
"Parameter not set" has to be available to the Cmdline caller in the
command line too, in addition to true/false. This was done originally
with a string which is empty, "yes" or "no". Using a tri-state
Cmdline::Bool class makes this a bit more explicit and removes the
ambiguity around what useDaemon() returns (values weren't documented).
When running without --daemon and no daemon available, the
first lines of output were:
ERROR: org.<cryptic error>
[INFO] itodo20: inactive
....
=> The command line should fall back *silently* to running in-process.
=> Error messages should be formatted and logged as such, using SE_LOG_ERROR().
Old code might not have done that and we need to preserve that for compatibility
with frontends, but new code should use [ERROR] as output.
=> D-Bus error messages (as in the case above) must have some user (and developer!)
comprehensible explanation what went wrong. The D-Bus error class itself
is no enough.
Although I haven't tested it, I suspect that the code also would have
re-run the operation in-process after the D-Bus server already
executed it and failed.
I rewrote this so that a check for "daemon available" without error messages
is done first before committing to using the daemon. Once that decision is made,
the command line will not fall back to in-process execution.
Rewrote several error messages. Telling a user of a distro's binary to
"re-configure" is misleading (he didn't configure himself).
"can't" => "cannot", punctuation changes. Not sure whether is always an
improvement, comments welcome.
Comment on coding style: I've used "if ()" instead of "if()" because that is
the GNU recommendation.
2010-03-26 18:47:41 +01:00
SE_LOG_INFO ( NULL , NULL , " matched %s against config %s (%s) " ,
info . toString ( ) . c_str ( ) ,
entry . first . c_str ( ) ,
entry . second . c_str ( ) ) ;
2010-03-15 14:14:12 +01:00
break ;
}
}
if ( config . empty ( ) ) {
// TODO: proper exception
throw runtime_error ( string ( " no configuration found for " ) +
info . toString ( ) ) ;
}
// abort previous session of this client
m_server . killSessions ( info . m_deviceID ) ;
peerDeviceID = info . m_deviceID ;
2009-09-21 15:25:14 +02:00
} else {
2010-06-30 11:35:11 +02:00
throw runtime_error ( StringPrintf ( " message type '%s' not supported for starting a sync " , message_type . c_str ( ) ) ) ;
2009-09-21 15:25:14 +02:00
}
2009-11-09 21:10:10 +01:00
// run session as client or server
2009-09-30 17:24:55 +02:00
m_state = PROCESSING ;
2010-09-30 10:32:13 +02:00
m_session = Session : : createSession ( m_server ,
peerDeviceID ,
config ,
m_sessionID ) ;
2009-09-30 17:24:55 +02:00
if ( serverMode ) {
m_session - > initServer ( SharedBuffer ( reinterpret_cast < const char * > ( message . second ) ,
message . first ) ,
message_type ) ;
2009-09-27 22:48:04 +02:00
}
2009-09-30 17:24:55 +02:00
m_session - > setPriority ( Session : : PRI_CONNECTION ) ;
m_session - > setConnection ( myself ) ;
// this will be reset only when the connection shuts down okay
// or overwritten with the error given to us in
// Connection::close()
m_session - > setConnectionError ( " closed prematurely " ) ;
m_server . enqueue ( m_session ) ;
break ;
2009-09-17 21:07:55 +02:00
}
2009-09-30 17:24:55 +02:00
case PROCESSING :
throw std : : runtime_error ( " protocol error: already processing a message " ) ;
break ;
case WAITING :
m_incomingMsg = SharedBuffer ( reinterpret_cast < const char * > ( message . second ) ,
message . first ) ;
m_incomingMsgType = message_type ;
m_state = PROCESSING ;
// get out of DBusTransportAgent::wait()
2009-10-30 07:25:53 +01:00
wakeupSession ( ) ;
2009-09-30 17:24:55 +02:00
break ;
case FINAL :
2009-10-30 07:25:53 +01:00
wakeupSession ( ) ;
2009-09-30 17:24:55 +02:00
throw std : : runtime_error ( " protocol error: final reply sent, no further message processing possible " ) ;
case DONE :
throw std : : runtime_error ( " protocol error: connection closed, no further message processing possible " ) ;
break ;
case FAILED :
throw std : : runtime_error ( m_failure ) ;
break ;
default :
throw std : : runtime_error ( " protocol error: unknown internal state " ) ;
break ;
2009-09-27 22:48:04 +02:00
}
2009-09-30 17:24:55 +02:00
} catch ( const std : : exception & error ) {
failed ( error . what ( ) ) ;
throw ;
} catch ( . . . ) {
failed ( " unknown exception in Connection::process " ) ;
throw ;
2009-09-08 19:11:49 +02:00
}
}
void Connection : : close ( const Caller_t & caller ,
bool normal ,
const std : : string & error )
{
2009-11-09 21:10:10 +01:00
SE_LOG_DEBUG ( NULL , NULL , " D-Bus client %s closes connection %s %s%s%s " ,
2009-09-08 19:11:49 +02:00
caller . c_str ( ) ,
2009-11-09 21:10:10 +01:00
getPath ( ) ,
2009-09-08 19:11:49 +02:00
normal ? " normally " : " with error " ,
error . empty ( ) ? " " : " : " ,
error . c_str ( ) ) ;
boost : : shared_ptr < Client > client ( m_server . findClient ( caller ) ) ;
if ( ! client ) {
throw runtime_error ( " unknown client " ) ;
}
if ( ! normal | |
m_state ! = FINAL ) {
2009-09-17 21:07:55 +02:00
std : : string err = error . empty ( ) ?
" connection closed unexpectedly " :
error ;
2009-09-30 17:24:55 +02:00
if ( m_session ) {
m_session - > setConnectionError ( err ) ;
}
2009-09-17 21:07:55 +02:00
failed ( err ) ;
2009-09-08 19:11:49 +02:00
} else {
m_state = DONE ;
2009-09-30 17:24:55 +02:00
if ( m_session ) {
m_session - > setConnectionError ( " " ) ;
}
2009-09-08 19:11:49 +02:00
}
// remove reference to us from client, will destruct *this*
// instance!
client - > detach ( this ) ;
}
2009-11-09 21:07:32 +01:00
void Connection : : abort ( )
{
if ( ! m_abortSent ) {
sendAbort ( ) ;
m_abortSent = true ;
m_state = FAILED ;
}
}
2009-09-17 21:07:55 +02:00
void Connection : : shutdown ( )
{
// trigger removal of this connection by removing all
// references to it
2009-09-30 17:24:55 +02:00
m_server . detach ( this ) ;
2009-09-17 21:07:55 +02:00
}
2009-09-08 19:11:49 +02:00
Connection : : Connection ( DBusServer & server ,
2009-09-23 09:44:01 +02:00
const DBusConnectionPtr & conn ,
const std : : string & sessionID ,
2009-09-27 22:48:04 +02:00
const StringMap & peer ,
2009-09-23 09:44:01 +02:00
bool must_authenticate ) :
2009-09-08 19:11:49 +02:00
DBusObjectHelper ( conn . get ( ) ,
2009-09-23 09:44:01 +02:00
std : : string ( " /org/syncevolution/Connection/ " ) + sessionID ,
2010-01-13 02:47:42 +01:00
" org.syncevolution.Connection " ,
boost : : bind ( & DBusServer : : autoTermCallback , & server ) ) ,
2009-09-08 19:11:49 +02:00
m_server ( server ) ,
m_peer ( peer ) ,
m_mustAuthenticate ( must_authenticate ) ,
m_state ( SETUP ) ,
2009-09-23 09:44:01 +02:00
m_sessionID ( sessionID ) ,
2009-09-17 21:07:55 +02:00
m_loop ( NULL ) ,
2009-11-05 17:55:02 +01:00
sendAbort ( * this , " Abort " ) ,
m_abortSent ( false ) ,
2009-09-08 19:11:49 +02:00
reply ( * this , " Reply " ) ,
m_description ( buildDescription ( peer ) )
2009-10-12 21:01:02 +02:00
{
add ( this , & Connection : : process , " Process " ) ;
add ( this , & Connection : : close , " Close " ) ;
2009-11-05 17:55:02 +01:00
add ( sendAbort ) ;
2009-10-12 21:01:02 +02:00
add ( reply ) ;
2010-01-13 02:47:42 +01:00
m_server . autoTermRef ( ) ;
2009-10-12 21:01:02 +02:00
}
2009-09-08 19:11:49 +02:00
Connection : : ~ Connection ( )
{
SE_LOG_DEBUG ( NULL , NULL , " done with connection to '%s'%s%s%s " ,
m_description . c_str ( ) ,
m_state = = DONE ? " , normal shutdown " : " unexpectedly " ,
m_failure . empty ( ) ? " " : " : " ,
m_failure . c_str ( ) ) ;
2009-09-17 21:07:55 +02:00
try {
if ( m_state ! = DONE ) {
abort ( ) ;
}
// DBusTransportAgent waiting? Wake it up.
2009-10-30 07:25:53 +01:00
wakeupSession ( ) ;
2009-09-17 21:07:55 +02:00
m_session . use_count ( ) ;
m_session . reset ( ) ;
} catch ( . . . ) {
// log errors, but do not propagate them because we are
// destructing
Exception : : handle ( ) ;
2009-09-08 19:11:49 +02:00
}
2010-01-13 02:47:42 +01:00
m_server . autoTermUnref ( ) ;
2009-09-08 19:11:49 +02:00
}
void Connection : : ready ( )
{
2010-03-12 02:16:45 +01:00
//if configuration not yet created
std : : string configName = m_session - > getConfigName ( ) ;
SyncConfig config ( configName ) ;
if ( ! config . exists ( ) & & m_SANContent ) {
SE_LOG_DEBUG ( NULL , NULL , " Configuration %s not exists for a runnable session in a SAN context, create it automatically " , configName . c_str ( ) ) ;
ReadOperations : : Config_t from ;
const std : : string templateName = " SyncEvolution " ;
// TODO: support SAN from other well known servers
ReadOperations ops ( templateName , m_server ) ;
ops . getConfig ( true , from ) ;
if ( ! m_peerBtAddr . empty ( ) ) {
from [ " " ] [ " SyncURL " ] = string ( " obex-bt:// " ) + m_peerBtAddr ;
}
m_session - > setConfig ( false , false , from ) ;
}
const SyncContext context ( configName ) ;
std : : list < std : : string > sources = context . getSyncSources ( ) ;
if ( m_SANContent & & ! m_SANContent - > m_syncType . empty ( ) ) {
// check what the server wants us to synchronize
// and only synchronize that
m_syncMode = " disabled " ;
for ( size_t sync = 0 ; sync < m_SANContent - > m_syncType . size ( ) ; sync + + ) {
std : : string syncMode = m_SANContent - > m_syncType [ sync ] ;
std : : string serverURI = m_SANContent - > m_serverURI [ sync ] ;
//uint32_t contentType = m_SANContent->m_contentType[sync];
bool found = false ;
BOOST_FOREACH ( const std : : string & source , sources ) {
boost : : shared_ptr < const PersistentSyncSourceConfig > sourceConfig ( context . getSyncSourceConfig ( source ) ) ;
// prefix match because the local
// configuration might contain
// additional parameters (like date
// range selection for events)
if ( boost : : starts_with ( sourceConfig - > getURI ( ) , serverURI ) ) {
SE_LOG_DEBUG ( NULL , NULL ,
2010-03-15 12:58:06 +01:00
" SAN entry #%d = source %s with mode %s " ,
( int ) sync , source . c_str ( ) , syncMode . c_str ( ) ) ;
2010-03-12 02:16:45 +01:00
m_sourceModes [ source ] = syncMode ;
found = true ;
break ;
}
}
if ( ! found ) {
SE_LOG_DEBUG ( NULL , NULL ,
2010-03-15 12:58:06 +01:00
" SAN entry #%d with mode %s ignored because Server URI %s is unknown " ,
( int ) sync , syncMode . c_str ( ) , serverURI . c_str ( ) ) ;
2010-03-12 02:16:45 +01:00
}
}
if ( m_sourceModes . empty ( ) ) {
SE_LOG_DEBUG ( NULL , NULL ,
" SAN message with no known entries, falling back to default " ) ;
m_syncMode = " " ;
}
}
if ( m_SANContent ) {
2010-03-15 12:58:06 +01:00
m_session - > setRemoteInitiated ( true ) ;
2010-03-12 02:16:45 +01:00
}
2009-09-17 21:07:55 +02:00
// proceed with sync now that our session is ready
2009-11-11 10:55:15 +01:00
m_session - > sync ( m_syncMode , m_sourceModes ) ;
2009-09-17 21:07:55 +02:00
}
2009-09-08 19:11:49 +02:00
2009-09-17 21:07:55 +02:00
/****************** DBusTransportAgent implementation **************/
DBusTransportAgent : : DBusTransportAgent ( GMainLoop * loop ,
Session & session ,
boost : : weak_ptr < Connection > connection ) :
m_loop ( loop ) ,
m_session ( session ) ,
2010-01-14 12:03:40 +01:00
m_connection ( connection ) ,
m_callback ( NULL ) ,
m_eventTriggered ( false ) ,
m_waiting ( false )
2009-09-17 21:07:55 +02:00
{
}
DBusTransportAgent : : ~ DBusTransportAgent ( )
{
boost : : shared_ptr < Connection > connection = m_connection . lock ( ) ;
if ( connection ) {
connection - > shutdown ( ) ;
2009-09-08 19:11:49 +02:00
}
}
2009-09-17 21:07:55 +02:00
void DBusTransportAgent : : send ( const char * data , size_t len )
{
boost : : shared_ptr < Connection > connection = m_connection . lock ( ) ;
if ( ! connection ) {
SE_THROW_EXCEPTION ( TransportException ,
" D-Bus peer has disconnected " ) ;
}
if ( connection - > m_state ! = Connection : : PROCESSING ) {
SE_THROW_EXCEPTION ( TransportException ,
" cannot send to our D-Bus peer " ) ;
}
// Change state in advance. If we fail while replying, then all
// further resends will fail with the error above.
connection - > m_state = Connection : : WAITING ;
connection - > m_incomingMsg = SharedBuffer ( ) ;
2010-01-14 12:03:40 +01:00
// setup regular callback
if ( m_callback ) {
m_eventSource = g_timeout_add_seconds ( m_callbackInterval , timeoutCallback , static_cast < gpointer > ( this ) ) ;
}
m_eventTriggered = false ;
2009-09-17 21:07:55 +02:00
// TODO: turn D-Bus exceptions into transport exceptions
2009-09-27 22:48:04 +02:00
StringMap meta ;
2009-09-17 21:07:55 +02:00
meta [ " URL " ] = m_url ;
connection - > reply ( std : : make_pair ( len , reinterpret_cast < const uint8_t * > ( data ) ) ,
2009-09-23 09:44:01 +02:00
m_type , meta , false , connection - > m_sessionID ) ;
2009-09-17 21:07:55 +02:00
}
void DBusTransportAgent : : shutdown ( )
{
boost : : shared_ptr < Connection > connection = m_connection . lock ( ) ;
if ( ! connection ) {
SE_THROW_EXCEPTION ( TransportException ,
" D-Bus peer has disconnected " ) ;
}
2009-11-09 21:07:32 +01:00
if ( connection - > m_state ! = Connection : : FAILED ) {
// send final, empty message and wait for close
connection - > m_state = Connection : : FINAL ;
connection - > reply ( std : : pair < size_t , const uint8_t * > ( 0 , 0 ) ,
" " , StringMap ( ) ,
true , connection - > m_sessionID ) ;
}
2009-09-17 21:07:55 +02:00
}
2010-01-14 12:03:40 +01:00
gboolean DBusTransportAgent : : timeoutCallback ( gpointer transport )
{
DBusTransportAgent * me = static_cast < DBusTransportAgent * > ( transport ) ;
me - > m_callback ( me - > m_callbackData ) ;
// TODO: check or remove return code from callback?!
me - > m_eventTriggered = true ;
if ( me - > m_waiting ) {
g_main_loop_quit ( me - > m_loop ) ;
}
return false ;
}
2009-09-17 21:07:55 +02:00
void DBusTransportAgent : : doWait ( boost : : shared_ptr < Connection > & connection )
{
// let Connection wake us up when it has a reply or
// when it closes down
connection - > m_loop = m_loop ;
// release our reference so that the Connection instance can
// be destructed when requested by the D-Bus peer
connection . reset ( ) ;
// now wait
2010-01-14 12:03:40 +01:00
m_waiting = true ;
2009-09-17 21:07:55 +02:00
g_main_loop_run ( m_loop ) ;
2010-01-14 12:03:40 +01:00
m_waiting = false ;
2009-09-17 21:07:55 +02:00
}
DBusTransportAgent : : Status DBusTransportAgent : : wait ( bool noReply )
{
boost : : shared_ptr < Connection > connection = m_connection . lock ( ) ;
if ( ! connection ) {
SE_THROW_EXCEPTION ( TransportException ,
" D-Bus peer has disconnected " ) ;
}
switch ( connection - > m_state ) {
case Connection : : PROCESSING :
m_incomingMsg = connection - > m_incomingMsg ;
m_incomingMsgType = connection - > m_incomingMsgType ;
return GOT_REPLY ;
break ;
case Connection : : FINAL :
2010-01-14 12:03:40 +01:00
if ( m_eventTriggered ) {
return TIME_OUT ;
}
2009-09-17 21:07:55 +02:00
doWait ( connection ) ;
// if the connection is still available, then keep waiting
connection = m_connection . lock ( ) ;
if ( connection ) {
return ACTIVE ;
} else if ( m_session . getConnectionError ( ) . empty ( ) ) {
return INACTIVE ;
} else {
SE_THROW_EXCEPTION ( TransportException , m_session . getConnectionError ( ) ) ;
return FAILED ;
}
break ;
case Connection : : WAITING :
if ( noReply ) {
// message is sent as far as we know, so return
return INACTIVE ;
}
2010-01-14 12:03:40 +01:00
if ( m_eventTriggered ) {
return TIME_OUT ;
}
2009-09-17 21:07:55 +02:00
doWait ( connection ) ;
// tell caller to check again
return ACTIVE ;
break ;
case Connection : : DONE :
if ( ! noReply ) {
SE_THROW_EXCEPTION ( TransportException ,
" internal error: transport has shut down, can no longer receive reply " ) ;
}
return CLOSED ;
default :
SE_THROW_EXCEPTION ( TransportException ,
" internal error: send() on connection which is not ready " ) ;
break ;
}
return FAILED ;
}
void DBusTransportAgent : : getReply ( const char * & data , size_t & len , std : : string & contentType )
{
data = m_incomingMsg . get ( ) ;
len = m_incomingMsg . size ( ) ;
contentType = m_incomingMsgType ;
}
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
/********************* PresenceStatus implementation ****************/
void PresenceStatus : : init ( ) {
//initialize the configured peer list
if ( ! m_initiated ) {
SyncConfig : : ConfigList list = SyncConfig : : getConfigs ( ) ;
BOOST_FOREACH ( const SyncConfig : : ConfigList : : value_type & server , list ) {
SyncConfig config ( server . first ) ;
vector < string > urls = config . getSyncURL ( ) ;
m_peers [ server . first ] . clear ( ) ;
BOOST_FOREACH ( const string & url , urls ) {
2010-10-20 14:15:19 +02:00
// take current status into account,
// PresenceStatus::checkPresence() calls init() and
// expects up-to-date information
PeerStatus status ;
if ( ( boost : : starts_with ( url , " obex-bt " ) & & m_btPresence ) | |
( boost : : starts_with ( url , " http " ) & & m_httpPresence ) ) {
status = MIGHTWORK ;
} else {
status = NOTRANSPORT ;
}
m_peers [ server . first ] . push_back ( make_pair ( url , status ) ) ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
}
}
m_initiated = true ;
}
}
/* Implement DBusServer::checkPresence*/
void PresenceStatus : : checkPresence ( const string & peer , string & status , std : : vector < std : : string > & transport ) {
if ( ! m_initiated ) {
//might triggered by updateConfigPeers
init ( ) ;
}
string peerName = SyncConfig : : normalizeConfigString ( peer ) ;
vector < pair < string , PeerStatus > > mytransports = m_peers [ peerName ] ;
if ( mytransports . empty ( ) ) {
//wrong config name?
status = status2string ( NOTRANSPORT ) ;
transport . clear ( ) ;
return ;
}
PeerStatus mystatus = MIGHTWORK ;
transport . clear ( ) ;
//only if all transports are unavailable can we declare the peer
//status as unavailable
BOOST_FOREACH ( PeerStatusPair & mytransport , mytransports ) {
if ( mytransport . second = = MIGHTWORK ) {
transport . push_back ( mytransport . first ) ;
}
}
if ( transport . empty ( ) ) {
mystatus = NOTRANSPORT ;
}
status = status2string ( mystatus ) ;
}
void PresenceStatus : : updateConfigPeers ( const std : : string & peer , const ReadOperations : : Config_t & config ) {
ReadOperations : : Config_t : : const_iterator iter = config . find ( " " ) ;
if ( iter ! = config . end ( ) ) {
//As a simple approach, just reinitialize the whole STATUSMAP
//it will cause later updatePresenceStatus resend all signals
//and a reload in checkPresence
m_initiated = false ;
}
}
2010-04-01 04:15:20 +02:00
void PresenceStatus : : updatePresenceStatus ( bool newStatus , PresenceStatus : : TransportType type ) {
if ( type = = PresenceStatus : : HTTP_TRANSPORT ) {
updatePresenceStatus ( newStatus , m_btPresence ) ;
} else if ( type = = PresenceStatus : : BT_TRANSPORT ) {
updatePresenceStatus ( m_httpPresence , newStatus ) ;
} else {
}
}
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
void PresenceStatus : : updatePresenceStatus ( bool httpPresence , bool btPresence ) {
bool httpChanged = ( m_httpPresence ! = httpPresence ) ;
bool btChanged = ( m_btPresence ! = btPresence ) ;
if ( httpChanged ) {
m_httpTimer . reset ( ) ;
}
if ( btChanged ) {
m_btTimer . reset ( ) ;
}
if ( m_initiated & & ! httpChanged & & ! btChanged ) {
//nothing changed
return ;
}
2010-10-20 14:15:19 +02:00
//initialize the configured peer list using old presence status
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
bool initiated = m_initiated ;
if ( ! m_initiated ) {
init ( ) ;
}
2010-10-20 14:15:19 +02:00
// switch to new status
m_httpPresence = httpPresence ;
m_btPresence = btPresence ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
//iterate all configured peers and fire singals
BOOST_FOREACH ( StatusPair & peer , m_peers ) {
//iterate all possible transports
//TODO One peer might got more than one signals, avoid this
std : : vector < pair < string , PeerStatus > > & transports = peer . second ;
BOOST_FOREACH ( PeerStatusPair & entry , transports ) {
string url = entry . first ;
if ( boost : : starts_with ( url , " http " ) & & ( httpChanged | | ! initiated ) ) {
entry . second = m_httpPresence ? MIGHTWORK : NOTRANSPORT ;
m_server . emitPresence ( peer . first , status2string ( entry . second ) , entry . first ) ;
SE_LOG_DEBUG ( NULL , NULL ,
" http presence signal %s,%s,%s " ,
peer . first . c_str ( ) ,
status2string ( entry . second ) . c_str ( ) , entry . first . c_str ( ) ) ;
} else if ( boost : : starts_with ( url , " obex-bt " ) & & ( btChanged | | ! initiated ) ) {
entry . second = m_btPresence ? MIGHTWORK : NOTRANSPORT ;
m_server . emitPresence ( peer . first , status2string ( entry . second ) , entry . first ) ;
SE_LOG_DEBUG ( NULL , NULL ,
" bluetooth presence signal %s,%s,%s " ,
peer . first . c_str ( ) ,
status2string ( entry . second ) . c_str ( ) , entry . first . c_str ( ) ) ;
}
}
}
}
2009-09-17 21:07:55 +02:00
2010-04-01 04:15:20 +02:00
/********************** Connman Client implementation **************/
ConnmanClient : : ConnmanClient ( DBusServer & server ) :
m_server ( server ) ,
m_propertyChanged ( * this , " PropertyChanged " )
{
const char * connmanTest = getenv ( " DBUS_TEST_CONNMAN " ) ;
2010-08-09 16:10:17 +02:00
m_connmanConn = b_dbus_setup_bus ( connmanTest ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM , NULL , true , NULL ) ;
2010-04-01 04:15:20 +02:00
if ( m_connmanConn ) {
typedef std : : map < std : : string , boost : : variant < std : : vector < std : : string > > > PropDict ;
DBusClientCall1 < PropDict > getProp ( * this , " GetProperties " ) ;
getProp ( boost : : bind ( & ConnmanClient : : getPropCb , this , _1 , _2 ) ) ;
m_propertyChanged . activate ( boost : : bind ( & ConnmanClient : : propertyChanged , this , _1 , _2 ) ) ;
} else {
SE_LOG_ERROR ( NULL , NULL , " DBus connection setup for connman failed " ) ;
}
}
void ConnmanClient : : getPropCb ( const std : : map < std : : string ,
boost : : variant < std : : vector < std : : string > > > & props , const string & error ) {
if ( ! error . empty ( ) ) {
if ( error = = " org.freedesktop.DBus.Error.ServiceUnknown " ) {
// ensure there is still first set of singal set in case of no
// connman available
m_server . getPresenceStatus ( ) . updatePresenceStatus ( true , true ) ;
SE_LOG_DEBUG ( NULL , NULL , " No connman service available %s " , error . c_str ( ) ) ;
return ;
}
SE_LOG_DEBUG ( NULL , NULL , " error in connmanCallback %s " , error . c_str ( ) ) ;
return ;
}
typedef std : : pair < std : : string , boost : : variant < std : : vector < std : : string > > > element ;
bool httpPresence = false , btPresence = false ;
BOOST_FOREACH ( element entry , props ) {
//match connected for HTTP based peers (wifi/wimax/ethernet)
if ( entry . first = = " ConnectedTechnologies " ) {
std : : vector < std : : string > connected = boost : : get < std : : vector < std : : string > > ( entry . second ) ;
BOOST_FOREACH ( std : : string tech , connected ) {
if ( boost : : iequals ( tech , " wifi " ) | | boost : : iequals ( tech , " ethernet " )
| | boost : : iequals ( tech , " wimax " ) ) {
httpPresence = true ;
break ;
}
}
} else if ( entry . first = = " AvailableTechnologies " ) {
std : : vector < std : : string > enabled = boost : : get < std : : vector < std : : string > > ( entry . second ) ;
BOOST_FOREACH ( std : : string tech , enabled ) {
if ( boost : : iequals ( tech , " bluetooth " ) ) {
btPresence = true ;
break ;
}
}
} else {
continue ;
}
}
//now delivering the signals
m_server . getPresenceStatus ( ) . updatePresenceStatus ( httpPresence , btPresence ) ;
}
void ConnmanClient : : propertyChanged ( const string & name ,
const boost : : variant < vector < string > , string > & prop )
{
bool httpPresence = false , btPresence = false ;
bool httpChanged = false , btChanged = false ;
if ( boost : : iequals ( name , " ConnectedTechnologies " ) ) {
httpChanged = true ;
vector < string > connected = boost : : get < vector < string > > ( prop ) ;
BOOST_FOREACH ( std : : string tech , connected ) {
if ( boost : : iequals ( tech , " wifi " ) | | boost : : iequals ( tech , " ethernet " )
| | boost : : iequals ( tech , " wimax " ) ) {
httpPresence = true ;
break ;
}
}
} else if ( boost : : iequals ( name , " AvailableTechnologies " ) ) {
btChanged = true ;
vector < string > enabled = boost : : get < vector < string > > ( prop ) ;
BOOST_FOREACH ( std : : string tech , enabled ) {
if ( boost : : iequals ( tech , " bluetooth " ) ) {
btPresence = true ;
break ;
}
}
}
if ( httpChanged ) {
m_server . getPresenceStatus ( ) . updatePresenceStatus ( httpPresence , PresenceStatus : : HTTP_TRANSPORT ) ;
} else if ( btChanged ) {
m_server . getPresenceStatus ( ) . updatePresenceStatus ( btPresence , PresenceStatus : : BT_TRANSPORT ) ;
} else {
}
}
2009-09-08 19:11:49 +02:00
/********************** DBusServer implementation ******************/
void DBusServer : : clientGone ( Client * c )
{
for ( Clients_t : : iterator it = m_clients . begin ( ) ;
it ! = m_clients . end ( ) ;
+ + it ) {
if ( it - > second . get ( ) = = c ) {
SE_LOG_DEBUG ( NULL , NULL , " D-Bus client %s has disconnected " ,
c - > m_ID . c_str ( ) ) ;
2010-08-25 14:54:11 +02:00
autoTermUnref ( it - > second - > getAttachCount ( ) ) ;
2009-09-08 19:11:49 +02:00
m_clients . erase ( it ) ;
return ;
}
}
SE_LOG_DEBUG ( NULL , NULL , " unknown client has disconnected?! " ) ;
}
2009-09-23 09:44:01 +02:00
std : : string DBusServer : : getNextSession ( )
2009-09-08 19:11:49 +02:00
{
2009-11-05 17:49:01 +01:00
// Make the session ID somewhat random. This protects to
2009-10-01 15:21:32 +02:00
// some extend against injecting unwanted messages into the
// communication.
2009-09-08 19:11:49 +02:00
m_lastSession + + ;
if ( ! m_lastSession ) {
m_lastSession + + ;
}
2009-11-05 17:49:01 +01:00
return StringPrintf ( " %u%u " , rand ( ) , m_lastSession ) ;
2009-09-08 19:11:49 +02:00
}
2010-08-24 21:39:00 +02:00
vector < string > DBusServer : : getCapabilities ( )
{
// Note that this is tested by test-dbus.py in
// TestDBusServer.testCapabilities, update the test when adding
// capabilities.
vector < string > capabilities ;
2010-08-25 21:03:31 +02:00
capabilities . push_back ( " ConfigChanged " ) ;
2010-08-25 15:21:08 +02:00
capabilities . push_back ( " GetConfigName " ) ;
2010-08-25 14:54:11 +02:00
capabilities . push_back ( " Notifications " ) ;
2010-08-24 21:39:00 +02:00
capabilities . push_back ( " Version " ) ;
2010-08-25 10:37:58 +02:00
capabilities . push_back ( " SessionFlags " ) ;
2010-09-30 10:32:13 +02:00
capabilities . push_back ( " SessionAttach " ) ;
2010-08-24 21:39:00 +02:00
return capabilities ;
}
StringMap DBusServer : : getVersions ( )
{
StringMap versions ;
versions [ " version " ] = VERSION ;
versions [ " system " ] = EDSAbiWrapperInfo ( ) ;
versions [ " backends " ] = SyncSource : : backendsInfo ( ) ;
return versions ;
}
2009-09-22 15:09:31 +02:00
void DBusServer : : attachClient ( const Caller_t & caller ,
const boost : : shared_ptr < Watch > & watch )
{
2010-01-13 02:47:42 +01:00
boost : : shared_ptr < Client > client = addClient ( getConnection ( ) ,
caller ,
watch ) ;
autoTermRef ( ) ;
2010-08-25 14:54:11 +02:00
client - > increaseAttachCount ( ) ;
2009-09-22 15:09:31 +02:00
}
void DBusServer : : detachClient ( const Caller_t & caller )
{
2010-08-25 14:54:11 +02:00
boost : : shared_ptr < Client > client = findClient ( caller ) ;
if ( client ) {
autoTermUnref ( ) ;
client - > decreaseAttachCount ( ) ;
}
}
void DBusServer : : setNotifications ( bool enabled ,
const Caller_t & caller ,
const string & /* notifications */ )
{
boost : : shared_ptr < Client > client = findClient ( caller ) ;
if ( client & & client - > getAttachCount ( ) ) {
client - > setNotificationsEnabled ( enabled ) ;
} else {
SE_THROW ( " client not attached, not allowed to change notifications " ) ;
}
2010-01-13 02:47:42 +01:00
}
2010-08-25 14:54:11 +02:00
bool DBusServer : : notificationsEnabled ( )
2010-01-13 02:47:42 +01:00
{
2010-08-25 14:54:11 +02:00
for ( Clients_t : : iterator it = m_clients . begin ( ) ;
it ! = m_clients . end ( ) ;
+ + it ) {
if ( ! it - > second - > getNotificationsEnabled ( ) ) {
return false ;
2010-01-13 02:47:42 +01:00
}
}
2010-08-25 14:54:11 +02:00
return true ;
2009-09-22 15:09:31 +02:00
}
2009-09-08 19:11:49 +02:00
void DBusServer : : connect ( const Caller_t & caller ,
const boost : : shared_ptr < Watch > & watch ,
2009-09-27 22:48:04 +02:00
const StringMap & peer ,
2009-09-08 19:11:49 +02:00
bool must_authenticate ,
2009-09-23 09:44:01 +02:00
const std : : string & session ,
2009-09-08 19:11:49 +02:00
DBusObject_t & object )
{
2009-09-23 09:44:01 +02:00
if ( ! session . empty ( ) ) {
2009-09-08 19:11:49 +02:00
// reconnecting to old connection is not implemented yet
throw std : : runtime_error ( " not implemented " ) ;
}
2009-09-23 09:44:01 +02:00
std : : string new_session = getNextSession ( ) ;
2009-09-08 19:11:49 +02:00
boost : : shared_ptr < Connection > c ( new Connection ( * this ,
getConnection ( ) ,
new_session ,
peer ,
must_authenticate ) ) ;
2009-11-09 21:10:10 +01:00
SE_LOG_DEBUG ( NULL , NULL , " connecting D-Bus client %s with connection %s '%s' " ,
2009-09-08 19:11:49 +02:00
caller . c_str ( ) ,
2009-11-09 21:10:10 +01:00
c - > getPath ( ) ,
2009-09-08 19:11:49 +02:00
c - > m_description . c_str ( ) ) ;
boost : : shared_ptr < Client > client = addClient ( getConnection ( ) ,
caller ,
watch ) ;
client - > attach ( c ) ;
c - > activate ( ) ;
object = c - > getPath ( ) ;
}
2010-08-25 10:37:58 +02:00
void DBusServer : : startSessionWithFlags ( const Caller_t & caller ,
const boost : : shared_ptr < Watch > & watch ,
const std : : string & server ,
const std : : vector < std : : string > & flags ,
DBusObject_t & object )
2009-09-08 19:11:49 +02:00
{
boost : : shared_ptr < Client > client = addClient ( getConnection ( ) ,
caller ,
watch ) ;
2009-09-23 09:44:01 +02:00
std : : string new_session = getNextSession ( ) ;
2010-09-30 10:32:13 +02:00
boost : : shared_ptr < Session > session = Session : : createSession ( * this ,
" is this a client or server session? " ,
server ,
new_session ,
flags ) ;
2009-09-08 19:11:49 +02:00
client - > attach ( session ) ;
session - > activate ( ) ;
enqueue ( session ) ;
object = session - > getPath ( ) ;
}
2009-09-22 15:09:31 +02:00
void DBusServer : : checkPresence ( const std : : string & server ,
std : : string & status ,
std : : vector < std : : string > & transports )
{
2010-02-03 02:50:56 +01:00
return m_presence . checkPresence ( server , status , transports ) ;
2009-09-22 15:09:31 +02:00
}
2010-04-01 14:20:05 +02:00
void DBusServer : : getSessions ( std : : vector < DBusObject_t > & sessions )
2009-11-20 09:36:26 +01:00
{
sessions . reserve ( m_workQueue . size ( ) + 1 ) ;
if ( m_activeSession ) {
sessions . push_back ( m_activeSession - > getPath ( ) ) ;
}
BOOST_FOREACH ( boost : : weak_ptr < Session > & session , m_workQueue ) {
boost : : shared_ptr < Session > s = session . lock ( ) ;
if ( s ) {
sessions . push_back ( s - > getPath ( ) ) ;
}
}
}
2010-01-13 02:47:42 +01:00
DBusServer : : DBusServer ( GMainLoop * loop , const DBusConnectionPtr & conn , int duration ) :
DBusObjectHelper ( conn . get ( ) ,
" /org/syncevolution/Server " ,
" org.syncevolution.Server " ,
boost : : bind ( & DBusServer : : autoTermCallback , this ) ) ,
2009-09-14 12:58:22 +02:00
m_loop ( loop ) ,
2009-09-08 19:11:49 +02:00
m_lastSession ( time ( NULL ) ) ,
m_activeSession ( NULL ) ,
2009-12-21 03:03:00 +01:00
m_lastInfoReq ( 0 ) ,
2010-02-23 02:26:58 +01:00
m_bluezManager ( * this ) ,
2009-09-22 15:09:31 +02:00
sessionChanged ( * this , " SessionChanged " ) ,
2009-12-21 03:03:00 +01:00
presence ( * this , " Presence " ) ,
2010-02-23 09:56:56 +01:00
templatesChanged ( * this , " TemplatesChanged " ) ,
2010-08-25 21:03:31 +02:00
configChanged ( * this , " ConfigChanged " ) ,
2010-02-03 02:50:56 +01:00
infoRequest ( * this , " InfoRequest " ) ,
2010-03-18 06:38:06 +01:00
logOutput ( * this , " LogOutput " ) ,
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
m_presence ( * this ) ,
2010-04-01 04:15:20 +02:00
m_connman ( * this ) ,
2010-03-02 06:34:42 +01:00
m_autoSync ( * this ) ,
2010-03-18 06:38:06 +01:00
m_autoTerm ( m_autoSync . preventTerm ( ) ? - 1 : duration ) , //if there is any task in auto sync, prevent auto termination
m_parentLogger ( LoggerBase : : instance ( ) )
2009-10-12 21:01:02 +02:00
{
2009-11-05 17:49:01 +01:00
struct timeval tv ;
gettimeofday ( & tv , NULL ) ;
srand ( tv . tv_usec ) ;
2010-08-24 21:39:00 +02:00
add ( this , & DBusServer : : getCapabilities , " GetCapabilities " ) ;
add ( this , & DBusServer : : getVersions , " GetVersions " ) ;
2009-10-12 21:01:02 +02:00
add ( this , & DBusServer : : attachClient , " Attach " ) ;
add ( this , & DBusServer : : detachClient , " Detach " ) ;
2010-08-25 14:54:11 +02:00
add ( this , & DBusServer : : enableNotifications , " EnableNotifications " ) ;
add ( this , & DBusServer : : disableNotifications , " DisableNotifications " ) ;
2009-10-12 21:01:02 +02:00
add ( this , & DBusServer : : connect , " Connect " ) ;
add ( this , & DBusServer : : startSession , " StartSession " ) ;
2010-08-25 10:37:58 +02:00
add ( this , & DBusServer : : startSessionWithFlags , " StartSessionWithFlags " ) ;
2010-02-03 14:17:37 +01:00
add ( this , & DBusServer : : getConfigs , " GetConfigs " ) ;
2009-10-12 21:01:02 +02:00
add ( this , & DBusServer : : getConfig , " GetConfig " ) ;
add ( this , & DBusServer : : getReports , " GetReports " ) ;
2009-11-19 14:07:23 +01:00
add ( this , & DBusServer : : checkSource , " CheckSource " ) ;
add ( this , & DBusServer : : getDatabases , " GetDatabases " ) ;
2009-10-12 21:01:02 +02:00
add ( this , & DBusServer : : checkPresence , " CheckPresence " ) ;
2009-11-20 09:36:26 +01:00
add ( this , & DBusServer : : getSessions , " GetSessions " ) ;
2009-12-21 03:03:00 +01:00
add ( this , & DBusServer : : infoResponse , " InfoResponse " ) ;
2009-10-12 21:01:02 +02:00
add ( sessionChanged ) ;
2010-02-23 09:56:56 +01:00
add ( templatesChanged ) ;
2010-08-25 21:03:31 +02:00
add ( configChanged ) ;
2009-10-12 21:01:02 +02:00
add ( presence ) ;
2009-12-21 03:03:00 +01:00
add ( infoRequest ) ;
2010-03-18 06:38:06 +01:00
add ( logOutput ) ;
LoggerBase : : pushLogger ( this ) ;
setLevel ( LoggerBase : : DEBUG ) ;
2009-10-12 21:01:02 +02:00
}
2009-09-08 19:11:49 +02:00
DBusServer : : ~ DBusServer ( )
{
// make sure all other objects are gone before destructing ourselves
m_syncSession . reset ( ) ;
m_workQueue . clear ( ) ;
m_clients . clear ( ) ;
2010-03-18 06:38:06 +01:00
LoggerBase : : popLogger ( ) ;
2009-09-08 19:11:49 +02:00
}
2009-09-14 12:58:22 +02:00
void DBusServer : : run ( )
{
2009-10-30 07:25:53 +01:00
while ( ! shutdownRequested ) {
2009-09-14 12:58:22 +02:00
if ( ! m_activeSession | |
! m_activeSession - > readyToRun ( ) ) {
g_main_loop_run ( m_loop ) ;
}
if ( m_activeSession & &
m_activeSession - > readyToRun ( ) ) {
// this session must be owned by someone, otherwise
2009-09-17 21:03:53 +02:00
// it would not be set as active session
boost : : shared_ptr < Session > session = m_activeSessionRef . lock ( ) ;
if ( ! session ) {
throw runtime_error ( " internal error: session no longer available " ) ;
2009-09-14 12:58:22 +02:00
}
try {
2009-09-17 21:03:53 +02:00
// ensure that the session doesn't go away
2009-09-14 12:58:22 +02:00
m_syncSession . swap ( session ) ;
m_activeSession - > run ( ) ;
} catch ( const std : : exception & ex ) {
SE_LOG_ERROR ( NULL , NULL , " %s " , ex . what ( ) ) ;
} catch ( . . . ) {
SE_LOG_ERROR ( NULL , NULL , " unknown error " ) ;
}
session . swap ( m_syncSession ) ;
dequeue ( session . get ( ) ) ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
}
if ( m_autoSync . hasTask ( ) ) {
// if there is at least one pending task and no session is created for auto sync,
// pick one task and create a session
m_autoSync . startTask ( ) ;
}
// Make sure check whether m_activeSession is owned by autosync
// Otherwise activeSession is owned by AutoSyncManager but it never
// be ready to run. Because methods of Session, like 'sync', are able to be
// called when it is active.
if ( m_autoSync . hasActiveSession ( ) )
{
// if the autosync is the active session, then invoke 'sync'
// to make it ready to run
m_autoSync . prepare ( ) ;
2009-10-30 07:25:53 +01:00
}
2009-09-14 12:58:22 +02:00
}
}
2009-09-08 19:11:49 +02:00
/**
* look up client by its ID
*/
boost : : shared_ptr < Client > DBusServer : : findClient ( const Caller_t & ID )
{
for ( Clients_t : : iterator it = m_clients . begin ( ) ;
it ! = m_clients . end ( ) ;
+ + it ) {
if ( it - > second - > m_ID = = ID ) {
return it - > second ;
}
}
return boost : : shared_ptr < Client > ( ) ;
}
boost : : shared_ptr < Client > DBusServer : : addClient ( const DBusConnectionPtr & conn ,
const Caller_t & ID ,
const boost : : shared_ptr < Watch > & watch )
{
boost : : shared_ptr < Client > client ( findClient ( ID ) ) ;
if ( client ) {
return client ;
}
2010-09-30 14:27:02 +02:00
client . reset ( new Client ( * this , ID ) ) ;
2009-09-08 19:11:49 +02:00
// add to our list *before* checking that peer exists, so
// that clientGone() can remove it if the check fails
m_clients . push_back ( std : : make_pair ( watch , client ) ) ;
watch - > setCallback ( boost : : bind ( & DBusServer : : clientGone , this , client . get ( ) ) ) ;
return client ;
}
2009-09-17 21:07:55 +02:00
void DBusServer : : detach ( Resource * resource )
{
BOOST_FOREACH ( const Clients_t : : value_type & client_entry ,
m_clients ) {
client_entry . second - > detachAll ( resource ) ;
}
}
2009-09-08 19:11:49 +02:00
void DBusServer : : enqueue ( const boost : : shared_ptr < Session > & session )
{
WorkQueue_t : : iterator it = m_workQueue . end ( ) ;
while ( it ! = m_workQueue . begin ( ) ) {
- - it ;
if ( it - > lock ( ) - > getPriority ( ) < = session - > getPriority ( ) ) {
+ + it ;
break ;
}
}
m_workQueue . insert ( it , session ) ;
checkQueue ( ) ;
}
2009-11-09 21:10:10 +01:00
int DBusServer : : killSessions ( const std : : string & peerDeviceID )
{
int count = 0 ;
WorkQueue_t : : iterator it = m_workQueue . begin ( ) ;
while ( it ! = m_workQueue . end ( ) ) {
boost : : shared_ptr < Session > session = it - > lock ( ) ;
if ( session & & session - > getPeerDeviceID ( ) = = peerDeviceID ) {
SE_LOG_DEBUG ( NULL , NULL , " removing pending session %s because it matches deviceID %s " ,
session - > getSessionID ( ) . c_str ( ) ,
peerDeviceID . c_str ( ) ) ;
// remove session and its corresponding connection
boost : : shared_ptr < Connection > c = session - > getConnection ( ) . lock ( ) ;
if ( c ) {
c - > shutdown ( ) ;
}
it = m_workQueue . erase ( it ) ;
count + + ;
} else {
+ + it ;
}
}
if ( m_activeSession & &
m_activeSession - > getPeerDeviceID ( ) = = peerDeviceID ) {
SE_LOG_DEBUG ( NULL , NULL , " aborting active session %s because it matches deviceID %s " ,
m_activeSession - > getSessionID ( ) . c_str ( ) ,
peerDeviceID . c_str ( ) ) ;
try {
// abort, even if not necessary right now
m_activeSession - > abort ( ) ;
} catch ( . . . ) {
// TODO: catch only that exception which indicates
// incorrect use of the function
}
dequeue ( m_activeSession ) ;
count + + ;
}
return count ;
}
2009-09-08 19:11:49 +02:00
void DBusServer : : dequeue ( Session * session )
{
if ( m_syncSession . get ( ) = = session ) {
// This is the running sync session.
// It's not in the work queue and we have to
// keep it active, so nothing to do.
return ;
}
for ( WorkQueue_t : : iterator it = m_workQueue . begin ( ) ;
it ! = m_workQueue . end ( ) ;
+ + it ) {
if ( it - > lock ( ) . get ( ) = = session ) {
// remove from queue
m_workQueue . erase ( it ) ;
// session was idle, so nothing else to do
return ;
}
}
if ( m_activeSession = = session ) {
// The session is releasing the lock, so someone else might
// run now.
session - > setActive ( false ) ;
sessionChanged ( session - > getPath ( ) , false ) ;
m_activeSession = NULL ;
2009-09-17 21:03:53 +02:00
m_activeSessionRef . reset ( ) ;
2009-09-08 19:11:49 +02:00
checkQueue ( ) ;
return ;
}
}
void DBusServer : : checkQueue ( )
{
if ( m_activeSession ) {
// still busy
return ;
}
while ( ! m_workQueue . empty ( ) ) {
boost : : shared_ptr < Session > session = m_workQueue . front ( ) . lock ( ) ;
m_workQueue . pop_front ( ) ;
if ( session ) {
// activate the session
m_activeSession = session . get ( ) ;
2009-09-17 21:03:53 +02:00
m_activeSessionRef = session ;
2009-09-08 19:11:49 +02:00
session - > setActive ( true ) ;
sessionChanged ( session - > getPath ( ) , true ) ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
//if the active session is changed, give a chance to quit the main loop
//and make it ready to run if it is owned by AutoSyncManager.
//Otherwise, server might be blocked.
g_main_loop_quit ( m_loop ) ;
2009-09-08 19:11:49 +02:00
return ;
}
}
}
2010-09-30 14:21:49 +02:00
bool DBusServer : : callTimeout ( const boost : : shared_ptr < Timeout > & timeout , const boost : : function < bool ( ) > & callback )
{
if ( ! callback ( ) ) {
m_timeouts . remove ( timeout ) ;
return false ;
} else {
return true ;
}
}
void DBusServer : : addTimeout ( const boost : : function < bool ( ) > & callback ,
int seconds )
{
boost : : shared_ptr < Timeout > timeout ( new Timeout ) ;
m_timeouts . push_back ( timeout ) ;
timeout - > activate ( seconds ,
boost : : bind ( & DBusServer : : callTimeout ,
this ,
// avoid copying the shared pointer here,
// otherwise the Timeout will never be deleted
boost : : ref ( m_timeouts . back ( ) ) ,
callback ) ) ;
}
2009-12-21 03:03:00 +01:00
void DBusServer : : infoResponse ( const Caller_t & caller ,
const std : : string & id ,
const std : : string & state ,
const std : : map < string , string > & response )
{
InfoReqMap : : iterator it = m_infoReqMap . find ( id ) ;
// if not found, ignore
if ( it ! = m_infoReqMap . end ( ) ) {
boost : : shared_ptr < InfoReq > infoReq = it - > second . lock ( ) ;
infoReq - > setResponse ( caller , state , response ) ;
}
}
boost : : shared_ptr < InfoReq > DBusServer : : createInfoReq ( const string & type ,
const std : : map < string , string > & parameters ,
const Session * session )
{
boost : : shared_ptr < InfoReq > infoReq ( new InfoReq ( * this , type , parameters , session ) ) ;
boost : : weak_ptr < InfoReq > item ( infoReq ) ;
m_infoReqMap . insert ( pair < string , boost : : weak_ptr < InfoReq > > ( infoReq - > getId ( ) , item ) ) ;
return infoReq ;
}
std : : string DBusServer : : getNextInfoReq ( )
{
return StringPrintf ( " %u " , + + m_lastInfoReq ) ;
}
void DBusServer : : emitInfoReq ( const InfoReq & req )
{
infoRequest ( req . getId ( ) ,
req . getSessionPath ( ) ,
req . getInfoStateStr ( ) ,
req . getHandler ( ) ,
req . getType ( ) ,
req . getParam ( ) ) ;
}
void DBusServer : : removeInfoReq ( const InfoReq & req )
{
// remove InfoRequest from hash map
InfoReqMap : : iterator it = m_infoReqMap . find ( req . getId ( ) ) ;
if ( it ! = m_infoReqMap . end ( ) ) {
m_infoReqMap . erase ( it ) ;
}
}
2010-02-03 14:17:37 +01:00
void DBusServer : : getDeviceList ( SyncConfig : : DeviceList & devices )
{
2010-02-23 05:06:05 +01:00
//wait bluez or other device managers
while ( ! m_bluezManager . isDone ( ) ) {
g_main_loop_run ( m_loop ) ;
}
devices . clear ( ) ;
devices = m_syncDevices ;
2010-02-03 14:17:37 +01:00
}
void DBusServer : : addPeerTempl ( const string & templName ,
const boost : : shared_ptr < SyncConfig : : TemplateDescription > peerTempl )
{
std : : string lower = templName ;
boost : : to_lower ( lower ) ;
m_matchedTempls . insert ( MatchedTemplates : : value_type ( lower , peerTempl ) ) ;
}
boost : : shared_ptr < SyncConfig : : TemplateDescription > DBusServer : : getPeerTempl ( const string & peer )
{
std : : string lower = peer ;
boost : : to_lower ( lower ) ;
MatchedTemplates : : iterator it = m_matchedTempls . find ( lower ) ;
if ( it ! = m_matchedTempls . end ( ) ) {
return it - > second ;
} else {
return boost : : shared_ptr < SyncConfig : : TemplateDescription > ( ) ;
}
}
2010-02-23 05:06:05 +01:00
bool DBusServer : : getDevice ( const string & deviceId , SyncConfig : : DeviceDescription & device )
{
SyncConfig : : DeviceList : : iterator syncDevIt ;
for ( syncDevIt = m_syncDevices . begin ( ) ; syncDevIt ! = m_syncDevices . end ( ) ; + + syncDevIt ) {
if ( boost : : equals ( syncDevIt - > m_deviceId , deviceId ) ) {
device = * syncDevIt ;
return true ;
}
}
return false ;
}
void DBusServer : : addDevice ( const SyncConfig : : DeviceDescription & device )
{
SyncConfig : : DeviceList : : iterator it ;
for ( it = m_syncDevices . begin ( ) ; it ! = m_syncDevices . end ( ) ; + + it ) {
if ( boost : : iequals ( it - > m_deviceId , device . m_deviceId ) ) {
break ;
}
}
if ( it = = m_syncDevices . end ( ) ) {
m_syncDevices . push_back ( device ) ;
2010-02-23 09:56:56 +01:00
templatesChanged ( ) ;
2010-02-23 05:06:05 +01:00
}
}
void DBusServer : : removeDevice ( const string & deviceId )
{
SyncConfig : : DeviceList : : iterator syncDevIt ;
for ( syncDevIt = m_syncDevices . begin ( ) ; syncDevIt ! = m_syncDevices . end ( ) ; + + syncDevIt ) {
if ( boost : : equals ( syncDevIt - > m_deviceId , deviceId ) ) {
m_syncDevices . erase ( syncDevIt ) ;
2010-02-23 09:56:56 +01:00
templatesChanged ( ) ;
2010-02-23 05:06:05 +01:00
break ;
}
}
}
void DBusServer : : updateDevice ( const string & deviceId ,
const SyncConfig : : DeviceDescription & device )
{
SyncConfig : : DeviceList : : iterator it ;
for ( it = m_syncDevices . begin ( ) ; it ! = m_syncDevices . end ( ) ; + + it ) {
if ( boost : : iequals ( it - > m_deviceId , deviceId ) ) {
( * it ) = device ;
2010-02-23 09:56:56 +01:00
templatesChanged ( ) ;
2010-02-23 05:06:05 +01:00
break ;
}
}
}
2010-03-18 06:38:06 +01:00
void DBusServer : : messagev ( Level level ,
const char * prefix ,
const char * file ,
int line ,
const char * function ,
const char * format ,
va_list args )
{
2010-03-26 18:08:28 +01:00
// iterating over args in messagev() is destructive, must make a copy first
va_list argsCopy ;
va_copy ( argsCopy , args ) ;
2010-03-18 06:38:06 +01:00
m_parentLogger . messagev ( level , prefix , file , line , function , format , args ) ;
2010-03-26 18:08:28 +01:00
string log = StringPrintfV ( format , argsCopy ) ;
va_end ( argsCopy ) ;
2010-03-18 06:38:06 +01:00
// prefix is used to set session path
// for general server output, the object path field is dbus server
// the object path can't be empty for object paths prevent using empty string.
2010-03-26 21:41:07 +01:00
string strLevel = Logger : : levelToStr ( level ) ;
2010-03-18 06:38:06 +01:00
if ( m_activeSession ) {
2010-03-26 21:41:07 +01:00
logOutput ( m_activeSession - > getPath ( ) , strLevel , log ) ;
2010-03-18 06:38:06 +01:00
} else {
2010-03-26 21:41:07 +01:00
logOutput ( getPath ( ) , strLevel , log ) ;
2010-03-18 06:38:06 +01:00
}
}
2009-12-21 03:03:00 +01:00
/********************** InfoReq implementation ******************/
InfoReq : : InfoReq ( DBusServer & server ,
const string & type ,
const InfoMap & parameters ,
const Session * session ,
uint32_t timeout ) :
m_server ( server ) , m_session ( session ) , m_infoState ( IN_REQ ) ,
m_status ( ST_RUN ) , m_type ( type ) , m_param ( parameters ) ,
m_timeout ( timeout ) , m_timer ( m_timeout * 1000 )
{
m_id = m_server . getNextInfoReq ( ) ;
m_server . emitInfoReq ( * this ) ;
m_param . clear ( ) ;
}
InfoReq : : ~ InfoReq ( )
{
m_handler = " " ;
done ( ) ;
m_server . removeInfoReq ( * this ) ;
}
InfoReq : : Status InfoReq : : check ( )
{
if ( m_status = = ST_RUN ) {
// give an opportunity to poll the sources on the main context
g_main_context_iteration ( g_main_loop_get_context ( m_server . getLoop ( ) ) , false ) ;
checkTimeout ( ) ;
}
return m_status ;
}
bool InfoReq : : getResponse ( InfoMap & response )
{
if ( m_status = = ST_OK ) {
response = m_response ;
return true ;
}
return false ;
}
InfoReq : : Status InfoReq : : wait ( InfoMap & response , uint32_t interval )
{
// give a chance to check whether it has been timeout
check ( ) ;
if ( m_status = = ST_RUN ) {
guint checkSource = g_timeout_add_seconds ( interval ,
( GSourceFunc ) checkCallback ,
static_cast < gpointer > ( this ) ) ;
while ( m_status = = ST_RUN ) {
g_main_context_iteration ( g_main_loop_get_context ( m_server . getLoop ( ) ) , true ) ;
}
// if the source is not removed
if ( m_status ! = ST_TIMEOUT & & m_status ! = ST_CANCEL ) {
g_source_remove ( checkSource ) ;
}
}
if ( m_status = = ST_OK ) {
response = m_response ;
}
return m_status ;
}
void InfoReq : : cancel ( )
{
if ( m_status = = ST_RUN ) {
m_handler = " " ;
done ( ) ;
m_status = ST_CANCEL ;
}
}
string InfoReq : : statusToString ( Status status )
{
switch ( status ) {
case ST_RUN :
return " running " ;
case ST_OK :
return " ok " ;
case ST_CANCEL :
return " cancelled " ;
case ST_TIMEOUT :
return " timeout " ;
default :
return " " ;
} ;
}
string InfoReq : : infoStateToString ( InfoState state )
{
switch ( state ) {
case IN_REQ :
return " request " ;
case IN_WAIT :
return " waiting " ;
case IN_DONE :
return " done " ;
default :
return " " ;
}
}
gboolean InfoReq : : checkCallback ( gpointer data )
{
// TODO: check abort and suspend(MB#8730)
// if InfoRequest("request") is sent and waiting for InfoResponse("working"),
// add a timeout mechanism
InfoReq * req = static_cast < InfoReq * > ( data ) ;
if ( req - > checkTimeout ( ) ) {
return FALSE ;
}
return TRUE ;
}
bool InfoReq : : checkTimeout ( )
{
2010-04-01 13:59:51 +02:00
// if waiting for client response, check time out
if ( m_status = = ST_RUN ) {
2009-12-21 03:03:00 +01:00
if ( m_timer . timeout ( ) ) {
m_status = ST_TIMEOUT ;
return true ;
}
}
return false ;
}
void InfoReq : : setResponse ( const Caller_t & caller , const string & state , const InfoMap & response )
{
if ( m_status ! = ST_RUN ) {
return ;
} else if ( m_infoState = = IN_REQ & & state = = " working " ) {
m_handler = caller ;
m_infoState = IN_WAIT ;
m_server . emitInfoReq ( * this ) ;
2010-04-01 13:59:51 +02:00
//reset the timer, used to check timeout
m_timer . reset ( ) ;
2009-12-21 03:03:00 +01:00
} else if ( m_infoState = = IN_WAIT & & state = = " response " ) {
m_response = response ;
m_handler = caller ;
done ( ) ;
m_status = ST_OK ;
}
}
void InfoReq : : done ( )
{
if ( m_infoState ! = IN_DONE ) {
m_infoState = IN_DONE ;
m_server . emitInfoReq ( * this ) ;
}
}
2010-02-23 02:26:58 +01:00
/********************** BluezManager implementation ******************/
BluezManager : : BluezManager ( DBusServer & server ) :
2010-03-26 21:41:07 +01:00
m_server ( server ) ,
m_adapterChanged ( * this , " DefaultAdapterChanged " )
2010-02-03 14:17:37 +01:00
{
2010-08-09 16:10:17 +02:00
m_bluezConn = b_dbus_setup_bus ( DBUS_BUS_SYSTEM , NULL , true , NULL ) ;
2010-02-03 14:17:37 +01:00
if ( m_bluezConn ) {
2010-02-23 02:26:58 +01:00
m_done = false ;
2010-03-15 02:33:43 +01:00
DBusClientCall1 < DBusObject_t > getAdapter ( * this , " DefaultAdapter " ) ;
2010-02-23 02:26:58 +01:00
getAdapter ( boost : : bind ( & BluezManager : : defaultAdapterCb , this , _1 , _2 ) ) ;
2010-03-26 21:41:07 +01:00
m_adapterChanged . activate ( boost : : bind ( & BluezManager : : defaultAdapterChanged , this , _1 ) ) ;
2010-02-03 14:17:37 +01:00
} else {
m_done = true ;
}
}
2010-02-23 05:06:05 +01:00
void BluezManager : : defaultAdapterChanged ( const DBusObject_t & adapter )
2010-02-03 14:17:37 +01:00
{
2010-02-23 05:06:05 +01:00
m_done = false ;
//remove devices that belong to this original adapter
2010-02-23 02:26:58 +01:00
if ( m_adapter ) {
2010-02-23 05:06:05 +01:00
BOOST_FOREACH ( boost : : shared_ptr < BluezDevice > & device , m_adapter - > getDevices ( ) ) {
m_server . removeDevice ( device - > getMac ( ) ) ;
}
2010-02-23 02:26:58 +01:00
}
string error ;
defaultAdapterCb ( adapter , error ) ;
2010-02-03 14:17:37 +01:00
}
2010-02-23 02:26:58 +01:00
void BluezManager : : defaultAdapterCb ( const DBusObject_t & adapter , const string & error )
2010-02-03 14:17:37 +01:00
{
if ( ! error . empty ( ) ) {
SE_LOG_DEBUG ( NULL , NULL , " Error in calling DefaultAdapter of Interface org.bluez.Manager: %s " , error . c_str ( ) ) ;
2010-02-23 02:26:58 +01:00
m_done = true ;
2010-02-03 14:17:37 +01:00
return ;
}
2010-02-23 02:26:58 +01:00
m_adapter . reset ( new BluezAdapter ( * this , adapter ) ) ;
2010-02-03 14:17:37 +01:00
}
2010-02-23 02:26:58 +01:00
BluezManager : : BluezAdapter : : BluezAdapter ( BluezManager & manager , const string & path )
2010-02-23 05:06:05 +01:00
: m_manager ( manager ) , m_path ( path ) , m_devNo ( 0 ) , m_devReplies ( 0 ) ,
m_deviceRemoved ( * this , " DeviceRemoved " ) , m_deviceAdded ( * this , " DeviceCreated " )
2010-02-23 02:26:58 +01:00
{
2010-03-15 02:33:43 +01:00
DBusClientCall1 < std : : vector < DBusObject_t > > listDevices ( * this , " ListDevices " ) ;
2010-02-23 02:26:58 +01:00
listDevices ( boost : : bind ( & BluezAdapter : : listDevicesCb , this , _1 , _2 ) ) ;
2010-03-26 21:41:07 +01:00
m_deviceRemoved . activate ( boost : : bind ( & BluezAdapter : : deviceRemoved , this , _1 ) ) ;
m_deviceAdded . activate ( boost : : bind ( & BluezAdapter : : deviceCreated , this , _1 ) ) ;
2010-02-23 02:26:58 +01:00
}
void BluezManager : : BluezAdapter : : listDevicesCb ( const std : : vector < DBusObject_t > & devices , const string & error )
2010-02-03 14:17:37 +01:00
{
if ( ! error . empty ( ) ) {
SE_LOG_DEBUG ( NULL , NULL , " Error in calling ListDevices of Interface org.bluez.Adapter: %s " , error . c_str ( ) ) ;
2010-02-23 02:26:58 +01:00
checkDone ( true ) ;
2010-02-03 14:17:37 +01:00
return ;
}
m_devNo = devices . size ( ) ;
BOOST_FOREACH ( const DBusObject_t & device , devices ) {
2010-02-23 02:26:58 +01:00
boost : : shared_ptr < BluezDevice > bluezDevice ( new BluezDevice ( * this , device ) ) ;
m_devices . push_back ( bluezDevice ) ;
}
checkDone ( ) ;
}
void BluezManager : : BluezAdapter : : deviceRemoved ( const DBusObject_t & object )
{
string address ;
std : : vector < boost : : shared_ptr < BluezDevice > > : : iterator devIt ;
for ( devIt = m_devices . begin ( ) ; devIt ! = m_devices . end ( ) ; + + devIt ) {
if ( boost : : equals ( ( * devIt ) - > getPath ( ) , object ) ) {
address = ( * devIt ) - > m_mac ;
if ( ( * devIt ) - > m_reply ) {
m_devReplies - - ;
}
m_devNo - - ;
m_devices . erase ( devIt ) ;
break ;
}
}
2010-02-23 05:06:05 +01:00
m_manager . m_server . removeDevice ( address ) ;
2010-02-23 02:26:58 +01:00
}
void BluezManager : : BluezAdapter : : deviceCreated ( const DBusObject_t & object )
{
m_devNo + + ;
boost : : shared_ptr < BluezDevice > bluezDevice ( new BluezDevice ( * this , object ) ) ;
m_devices . push_back ( bluezDevice ) ;
}
BluezManager : : BluezDevice : : BluezDevice ( BluezAdapter & adapter , const string & path )
2010-02-23 05:06:05 +01:00
: m_adapter ( adapter ) , m_path ( path ) , m_reply ( false ) , m_propertyChanged ( * this , " PropertyChanged " )
2010-02-23 02:26:58 +01:00
{
2010-03-15 02:33:43 +01:00
DBusClientCall1 < PropDict > getProperties ( * this , " GetProperties " ) ;
2010-02-23 02:26:58 +01:00
getProperties ( boost : : bind ( & BluezDevice : : getPropertiesCb , this , _1 , _2 ) ) ;
2010-03-26 21:41:07 +01:00
m_propertyChanged . activate ( boost : : bind ( & BluezDevice : : propertyChanged , this , _1 , _2 ) ) ;
2010-02-23 02:26:58 +01:00
}
void BluezManager : : BluezDevice : : checkSyncService ( const std : : vector < std : : string > & uuids )
{
static const char * SYNCML_CLIENT_UUID = " 00000002-0000-1000-8000-0002ee000002 " ;
bool hasSyncService = false ;
2010-02-23 05:06:05 +01:00
DBusServer & server = m_adapter . m_manager . m_server ;
2010-02-23 02:26:58 +01:00
BOOST_FOREACH ( const string & uuid , uuids ) {
//if the device has sync service, add it to the device list
if ( boost : : iequals ( uuid , SYNCML_CLIENT_UUID ) ) {
hasSyncService = true ;
if ( ! m_mac . empty ( ) ) {
2010-02-23 05:06:05 +01:00
server . addDevice ( SyncConfig : : DeviceDescription ( m_mac , m_name , SyncConfig : : MATCH_FOR_SERVER_MODE ) ) ;
2010-02-23 02:26:58 +01:00
}
break ;
}
}
2010-02-23 05:06:05 +01:00
// if sync service is not available now, possible to remove device
if ( ! hasSyncService & & ! m_mac . empty ( ) ) {
server . removeDevice ( m_mac ) ;
2010-02-03 14:17:37 +01:00
}
}
2010-02-23 02:26:58 +01:00
void BluezManager : : BluezDevice : : getPropertiesCb ( const PropDict & props , const string & error )
2010-02-03 14:17:37 +01:00
{
2010-02-23 02:26:58 +01:00
m_adapter . m_devReplies + + ;
m_reply = true ;
2010-02-03 14:17:37 +01:00
if ( ! error . empty ( ) ) {
SE_LOG_DEBUG ( NULL , NULL , " Error in calling GetProperties of Interface org.bluez.Device: %s " , error . c_str ( ) ) ;
2010-02-23 02:26:58 +01:00
} else {
PropDict : : const_iterator it = props . find ( " Name " ) ;
if ( it ! = props . end ( ) ) {
m_name = boost : : get < string > ( it - > second ) ;
}
it = props . find ( " Address " ) ;
if ( it ! = props . end ( ) ) {
m_mac = boost : : get < string > ( it - > second ) ;
}
PropDict : : const_iterator uuids = props . find ( " UUIDs " ) ;
if ( uuids ! = props . end ( ) ) {
const std : : vector < std : : string > uuidVec = boost : : get < std : : vector < std : : string > > ( uuids - > second ) ;
checkSyncService ( uuidVec ) ;
}
2010-02-03 14:17:37 +01:00
}
2010-02-23 02:26:58 +01:00
m_adapter . checkDone ( ) ;
}
void BluezManager : : BluezDevice : : propertyChanged ( const string & name ,
const boost : : variant < vector < string > , string > & prop )
{
2010-02-23 05:06:05 +01:00
DBusServer & server = m_adapter . m_manager . m_server ;
2010-02-23 02:26:58 +01:00
if ( boost : : iequals ( name , " Name " ) ) {
m_name = boost : : get < std : : string > ( prop ) ;
2010-02-23 05:06:05 +01:00
SyncConfig : : DeviceDescription device ;
if ( server . getDevice ( m_mac , device ) ) {
device . m_fingerprint = m_name ;
server . updateDevice ( m_mac , device ) ;
2010-02-03 14:17:37 +01:00
}
2010-02-23 02:26:58 +01:00
} else if ( boost : : iequals ( name , " UUIDs " ) ) {
const std : : vector < std : : string > uuidVec = boost : : get < std : : vector < std : : string > > ( prop ) ;
checkSyncService ( uuidVec ) ;
} else if ( boost : : iequals ( name , " Address " ) ) {
string mac = boost : : get < std : : string > ( prop ) ;
2010-02-23 05:06:05 +01:00
SyncConfig : : DeviceDescription device ;
if ( server . getDevice ( m_mac , device ) ) {
device . m_deviceId = mac ;
server . updateDevice ( m_mac , device ) ;
2010-02-23 02:26:58 +01:00
}
m_mac = mac ;
2010-02-03 14:17:37 +01:00
}
}
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
/************************ AutoSyncManager ******************/
void AutoSyncManager : : init ( )
{
m_peerMap . clear ( ) ;
SyncConfig : : ConfigList list = SyncConfig : : getConfigs ( ) ;
BOOST_FOREACH ( const SyncConfig : : ConfigList : : value_type & server , list ) {
initConfig ( server . first ) ;
}
}
void AutoSyncManager : : initConfig ( const string & configName )
{
SyncConfig config ( configName ) ;
if ( ! config . exists ( ) ) {
return ;
}
vector < string > urls = config . getSyncURL ( ) ;
string autoSync = config . getAutoSync ( ) ;
//enable http and bt?
bool http = false , bt = false ;
if ( autoSync . empty ( ) | | boost : : iequals ( autoSync , " 0 " )
| | boost : : iequals ( autoSync , " f " ) ) {
http = false ;
bt = false ;
} else if ( boost : : iequals ( autoSync , " 1 " ) | | boost : : iequals ( autoSync , " t " ) ) {
http = true ;
bt = true ;
} else {
vector < string > options ;
boost : : split ( options , autoSync , boost : : is_any_of ( " , " ) ) ;
BOOST_FOREACH ( string op , options ) {
if ( boost : : iequals ( op , " http " ) ) {
http = true ;
} else if ( boost : : iequals ( op , " obex-bt " ) ) {
bt = true ;
}
}
}
unsigned int interval = config . getAutoSyncInterval ( ) ;
unsigned int duration = config . getAutoSyncDelay ( ) ;
BOOST_FOREACH ( string url , urls ) {
if ( ( boost : : istarts_with ( url , " http " ) & & http )
| | ( boost : : istarts_with ( url , " obex-bt " ) & & bt ) ) {
AutoSyncTask syncTask ( configName , duration , url ) ;
PeerMap : : iterator it = m_peerMap . find ( interval ) ;
if ( it ! = m_peerMap . end ( ) ) {
it - > second - > push_back ( syncTask ) ;
} else {
boost : : shared_ptr < AutoSyncTaskList > list ( new AutoSyncTaskList ( * this , interval ) ) ;
list - > push_back ( syncTask ) ;
list - > createTimeoutSource ( ) ;
m_peerMap . insert ( std : : make_pair ( interval , list ) ) ;
}
}
}
}
void AutoSyncManager : : remove ( const string & configName )
{
//wipe out tasks in the m_peerMap
PeerMap : : iterator it = m_peerMap . begin ( ) ;
while ( it ! = m_peerMap . end ( ) ) {
boost : : shared_ptr < AutoSyncTaskList > & list = it - > second ;
AutoSyncTaskList : : iterator taskIt = list - > begin ( ) ;
while ( taskIt ! = list - > end ( ) ) {
if ( boost : : iequals ( taskIt - > m_peer , configName ) ) {
taskIt = list - > erase ( taskIt ) ;
} else {
+ + taskIt ;
}
}
//if list is empty, remove the list from map
if ( list - > empty ( ) ) {
2010-03-19 02:47:23 +01:00
PeerMap : : iterator erased = it + + ;
m_peerMap . erase ( erased ) ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
} else {
+ + it ;
}
}
//wipe out scheduled tasks in the working queue based on configName
list < AutoSyncTask > : : iterator qit = m_workQueue . begin ( ) ;
while ( qit ! = m_workQueue . end ( ) ) {
if ( boost : : iequals ( qit - > m_peer , configName ) ) {
qit = m_workQueue . erase ( qit ) ;
} else {
+ + qit ;
}
}
}
void AutoSyncManager : : update ( const string & configName )
{
// remove task from m_peerMap and tasks in the working queue for this config
remove ( configName ) ;
// re-load the config and re-init peer map
initConfig ( configName ) ;
//don't clear if the task is running
if ( m_session & & ! hasActiveSession ( )
& & boost : : iequals ( m_session - > getConfigName ( ) , configName ) ) {
m_server . dequeue ( m_session . get ( ) ) ;
m_session . reset ( ) ;
m_activeTask . reset ( ) ;
startTask ( ) ;
}
}
void AutoSyncManager : : scheduleAll ( )
{
BOOST_FOREACH ( PeerMap : : value_type & elem , m_peerMap ) {
elem . second - > scheduleTaskList ( ) ;
}
}
bool AutoSyncManager : : addTask ( const AutoSyncTask & syncTask )
{
if ( taskLikelyToRun ( syncTask ) ) {
m_workQueue . push_back ( syncTask ) ;
return true ;
}
return false ;
}
bool AutoSyncManager : : findTask ( const AutoSyncTask & syncTask )
{
if ( m_activeTask & & * m_activeTask = = syncTask ) {
return true ;
}
BOOST_FOREACH ( const AutoSyncTask & task , m_workQueue ) {
if ( task = = syncTask ) {
return true ;
}
}
return false ;
}
bool AutoSyncManager : : taskLikelyToRun ( const AutoSyncTask & syncTask )
{
PresenceStatus & status = m_server . getPresenceStatus ( ) ;
// avoid doing any checking of task list if http and bt presence are false
if ( ! status . getHttpPresence ( ) & & ! status . getBtPresence ( ) ) {
return false ;
}
if ( boost : : istarts_with ( syncTask . m_url , " http " ) & & status . getHttpPresence ( ) ) {
// don't add duplicate tasks
if ( ! findTask ( syncTask ) ) {
Timer & timer = status . getHttpTimer ( ) ;
// if the time peer have been around is longer than 'autoSyncDelay',
// then return true
2010-03-25 17:58:54 +01:00
if ( timer . timeout ( syncTask . m_delay * 1000 /* seconds to milliseconds */ ) ) {
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
return true ;
}
}
} else if ( boost : : istarts_with ( syncTask . m_url , " obex-bt " ) & & status . getBtPresence ( ) ) {
// don't add duplicate tasks
if ( ! findTask ( syncTask ) ) {
return true ;
}
}
return false ;
}
void AutoSyncManager : : startTask ( )
{
// get the front task and run a sync
// if there has been a session for the front task, do nothing
if ( hasTask ( ) & & ! m_session ) {
m_activeTask . reset ( new AutoSyncTask ( m_workQueue . front ( ) ) ) ;
m_workQueue . pop_front ( ) ;
string newSession = m_server . getNextSession ( ) ;
2010-09-30 10:32:13 +02:00
m_session = Session : : createSession ( m_server ,
" " ,
m_activeTask - > m_peer ,
newSession ) ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
m_session - > setPriority ( Session : : PRI_AUTOSYNC ) ;
2010-03-04 02:53:04 +01:00
m_session - > addListener ( this ) ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
m_server . enqueue ( m_session ) ;
}
}
bool AutoSyncManager : : hasActiveSession ( )
{
return m_session & & m_session - > getActive ( ) ;
}
void AutoSyncManager : : prepare ( )
{
if ( m_session & & m_session - > getActive ( ) ) {
// now a config may contain many urls, so replace it with our own temporarily
// otherwise it only picks the first one
ReadOperations : : Config_t config ;
StringMap stringMap ;
stringMap [ " syncURL " ] = m_activeTask - > m_url ;
config [ " " ] = stringMap ;
m_session - > setConfig ( true , true , config ) ;
string mode ;
Session : : SourceModes_t sourceModes ;
m_session - > sync ( mode , sourceModes ) ;
}
}
2010-03-04 02:53:04 +01:00
void AutoSyncManager : : syncSuccessStart ( )
{
m_syncSuccessStart = true ;
SE_LOG_INFO ( NULL , NULL , " Automatic sync for '%s' has been successfully started. \n " , m_activeTask - > m_peer . c_str ( ) ) ;
# ifdef HAS_NOTIFY
2010-08-25 14:54:11 +02:00
if ( m_server . notificationsEnabled ( ) ) {
string summary = StringPrintf ( _ ( " %s is syncing " ) , m_activeTask - > m_peer . c_str ( ) ) ;
string body = StringPrintf ( _ ( " We have just started to sync your computer with the %s sync service. " ) , m_activeTask - > m_peer . c_str ( ) ) ;
//TODO: set config information for 'sync-ui'
m_notify . send ( summary . c_str ( ) , body . c_str ( ) ) ;
}
2010-03-04 02:53:04 +01:00
# endif
}
void AutoSyncManager : : syncDone ( SyncMLStatus status )
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
{
2010-02-26 07:38:13 +01:00
SE_LOG_INFO ( NULL , NULL , " Automatic sync for '%s' has been done. \n " , m_activeTask - > m_peer . c_str ( ) ) ;
# ifdef HAS_NOTIFY
2010-08-25 14:54:11 +02:00
if ( m_server . notificationsEnabled ( ) ) {
// send a notification to notification server
string summary , body ;
if ( m_syncSuccessStart & & status = = STATUS_OK ) {
// if sync is successfully started and done
summary = StringPrintf ( _ ( " %s sync complete " ) , m_activeTask - > m_peer . c_str ( ) ) ;
body = StringPrintf ( _ ( " We have just finished syncing your computer with the %s sync service. " ) , m_activeTask - > m_peer . c_str ( ) ) ;
//TODO: set config information for 'sync-ui'
m_notify . send ( summary . c_str ( ) , body . c_str ( ) ) ;
} else if ( m_syncSuccessStart | | ( ! m_syncSuccessStart & & status = = STATUS_FATAL ) ) {
//if sync is successfully started and has errors, or not started successful with a fatal problem
summary = StringPrintf ( _ ( " Sync problem. " ) ) ;
body = StringPrintf ( _ ( " Sorry, there's a problem with your sync that you need to attend to. " ) ) ;
//TODO: set config information for 'sync-ui'
m_notify . send ( summary . c_str ( ) , body . c_str ( ) ) ;
}
}
2010-02-26 07:38:13 +01:00
# endif
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
m_session . reset ( ) ;
m_activeTask . reset ( ) ;
2010-03-04 02:53:04 +01:00
m_syncSuccessStart = false ;
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
}
2010-03-04 02:53:04 +01:00
# ifdef HAS_NOTIFY
AutoSyncManager : : Notification : : Notification ( )
{
bindtextdomain ( GETTEXT_PACKAGE , SYNCEVOLUTION_LOCALEDIR ) ;
bind_textdomain_codeset ( GETTEXT_PACKAGE , " UTF-8 " ) ;
textdomain ( GETTEXT_PACKAGE ) ;
m_init = notify_init ( " SyncEvolution " ) ;
m_actions = false ;
m_notification = NULL ;
// check whether 'actions' are supported by notification server
if ( m_init ) {
GList * list = notify_get_server_caps ( ) ;
if ( list ) {
for ( ; list ! = NULL ; list = list - > next ) {
if ( boost : : iequals ( ( char * ) list - > data , " actions " ) ) {
m_actions = true ;
}
}
}
}
}
AutoSyncManager : : Notification : : ~ Notification ( )
{
if ( m_init ) {
notify_uninit ( ) ;
}
}
void AutoSyncManager : : Notification : : notifyAction ( NotifyNotification * notify ,
gchar * action ,
gpointer userData )
{
if ( boost : : iequals ( " view " , action ) ) {
pid_t pid ;
if ( ( pid = fork ( ) ) = = 0 ) {
//search sync-ui from $PATH
if ( execlp ( " sync-ui " , " sync-ui " , ( const char * ) 0 ) < 0 ) {
exit ( 0 ) ;
}
}
}
//if dismiss, ignore
}
void AutoSyncManager : : Notification : : send ( const char * summary ,
const char * body ,
const char * viewParams )
{
if ( ! m_init )
return ;
if ( m_notification ) {
notify_notification_clear_actions ( m_notification ) ;
notify_notification_close ( m_notification , NULL ) ;
}
m_notification = notify_notification_new ( summary , body , NULL , NULL ) ;
//if actions are not supported, don't add actions
//An example is Ubuntu Notify OSD. It uses an alert box
//instead of a bubble when a notification is appended with actions.
//the alert box won't be closed until user inputs.
//so disable it in case of no support of actions
if ( m_actions ) {
notify_notification_add_action ( m_notification , " view " , _ ( " View " ) , notifyAction , ( gpointer ) viewParams , NULL ) ;
2010-04-19 16:35:00 +02:00
// Use "default" as ID because that is what mutter-moblin
// recognizes: it then skips the action instead of adding it
// in addition to its own "Dismiss" button (always added).
notify_notification_add_action ( m_notification , " default " , _ ( " Dismiss " ) , notifyAction , ( gpointer ) viewParams , NULL ) ;
2010-03-04 02:53:04 +01:00
}
notify_notification_show ( m_notification , NULL ) ;
}
# endif
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
void AutoSyncManager : : AutoSyncTaskList : : createTimeoutSource ( )
{
2010-03-02 06:34:42 +01:00
//if interval is 0, only run auto sync when changes are detected.
if ( m_interval ) {
2010-03-25 17:58:54 +01:00
m_source = g_timeout_add_seconds ( m_interval , taskListTimeoutCb , static_cast < gpointer > ( this ) ) ;
2010-03-02 06:34:42 +01:00
}
DBus Server: implement automatic sync (MB#6378)
Implement automatic sync feature. 3 scenarios are:
1) For a config enables autosync, an interval has passed.
2) Once users log in or resume and an interval has passed. Not
implemented yet.
3) Evolution data server notify any changes. Not implemented yet.
Auto sync manager is to manage all issues of auto sync. In its
initialization step, it reads all configs and check their properties.
If a configuration is enabled with automatic sync, possibly http
or obex-bt or both, one or more tasks for different URLs of each config
are added in the task map grouped by their intervals, holding configured
tasks. Tasks in the map are updated once any config is changed.
Tasks in the map are scheduled and checked when some conditions are met,
such as the interval has passed(the first scenario). Scheduled tasks are
put in the working queue to wait for doing sync. Before a scheduled task
is put in the working queue, it has to be checked whether there is
any existing same task in the working queue.
Once there is any task in the working queue, auto sync manager picks one
task from the working queue and create a session with a lower priority for
it than normal. However, at any time, there is at most one session.
If the session is scheduled and activated by dbus server, it gets
the privilege to do sync. After completion, auto sync manager picks another
task if has and creates a new session. Do this repeatedly unless the
working queue is empty.
2010-02-25 09:42:17 +01:00
}
gboolean AutoSyncManager : : AutoSyncTaskList : : taskListTimeoutCb ( gpointer data )
{
AutoSyncTaskList * list = static_cast < AutoSyncTaskList * > ( data ) ;
list - > scheduleTaskList ( ) ;
return TRUE ;
}
void AutoSyncManager : : AutoSyncTaskList : : scheduleTaskList ( )
{
BOOST_FOREACH ( AutoSyncTask & syncTask , * this ) {
m_manager . addTask ( syncTask ) ;
}
g_main_loop_quit ( m_manager . m_server . getLoop ( ) ) ;
}
2009-09-08 19:11:49 +02:00
/**************************** main *************************/
2009-03-30 17:54:47 +02:00
void niam ( int sig )
{
2009-10-30 07:25:53 +01:00
shutdownRequested = true ;
SyncContext : : handleSignal ( sig ) ;
2009-09-08 19:11:49 +02:00
g_main_loop_quit ( loop ) ;
2009-03-30 17:54:47 +02:00
}
2010-01-13 02:47:42 +01:00
static bool parseDuration ( int & duration , const char * value )
{
if ( value = = NULL ) {
return false ;
} else if ( boost : : iequals ( value , " unlimited " ) ) {
duration = - 1 ;
return true ;
} else if ( ( duration = atoi ( value ) ) > 0 ) {
return true ;
} else {
return false ;
}
}
2009-10-13 16:12:34 +02:00
int main ( int argc , char * * argv )
2009-03-30 17:54:47 +02:00
{
2010-01-13 02:47:42 +01:00
int duration = 600 ;
int opt = 1 ;
while ( opt < argc ) {
if ( argv [ opt ] [ 0 ] ! = ' - ' ) {
break ;
}
if ( boost : : iequals ( argv [ opt ] , " --duration " ) | |
boost : : iequals ( argv [ opt ] , " -d " ) ) {
opt + + ;
if ( ! parseDuration ( duration , opt = = argc ? NULL : argv [ opt ] ) ) {
std : : cout < < argv [ opt - 1 ] < < " : unknown parameter value or not set " < < std : : endl ;
return false ;
}
} else {
std : : cout < < argv [ opt ] < < " : unknown parameter " < < std : : endl ;
return false ;
}
opt + + ;
}
2009-09-08 19:11:49 +02:00
try {
g_type_init ( ) ;
g_thread_init ( NULL ) ;
g_set_application_name ( " SyncEvolution " ) ;
2010-05-26 18:44:04 +02:00
// Initializing a potential use of EDS early is necessary for
// libsynthesis when compiled with
// --enable-evolution-compatibility: in that mode libical will
// only be found by libsynthesis after EDSAbiWrapperInit()
// pulls it into the process by loading libecal.
EDSAbiWrapperInit ( ) ;
2009-09-08 19:11:49 +02:00
loop = g_main_loop_new ( NULL , FALSE ) ;
2009-03-30 17:54:47 +02:00
2009-11-05 17:53:32 +01:00
setvbuf ( stderr , NULL , _IONBF , 0 ) ;
setvbuf ( stdout , NULL , _IONBF , 0 ) ;
2009-09-08 19:11:49 +02:00
signal ( SIGTERM , niam ) ;
signal ( SIGINT , niam ) ;
2009-03-30 17:54:47 +02:00
2010-03-31 19:57:17 +02:00
LogRedirect redirect ( true ) ;
2010-03-31 20:04:28 +02:00
redirectPtr = & redirect ;
2010-03-19 02:20:46 +01:00
2010-03-26 18:34:32 +01:00
// make daemon less chatty - long term this should be a command line option
LoggerBase : : instance ( ) . setLevel ( LoggerBase : : INFO ) ;
2009-03-30 17:54:47 +02:00
2009-09-08 19:11:49 +02:00
DBusErrorCXX err ;
2010-08-09 16:10:17 +02:00
DBusConnectionPtr conn = b_dbus_setup_bus ( DBUS_BUS_SESSION ,
2009-09-08 19:11:49 +02:00
" org.syncevolution " ,
2009-12-04 12:47:47 +01:00
true ,
2009-09-08 19:11:49 +02:00
& err ) ;
if ( ! conn ) {
2010-08-09 16:10:17 +02:00
err . throwFailure ( " b_dbus_setup_bus() " , " failed - server already running? " ) ;
2009-09-08 19:11:49 +02:00
}
2009-03-30 17:54:47 +02:00
2010-01-13 02:47:42 +01:00
DBusServer server ( loop , conn , duration ) ;
2009-09-08 19:11:49 +02:00
server . activate ( ) ;
2009-10-13 16:12:34 +02:00
2010-03-31 19:57:17 +02:00
SE_LOG_INFO ( NULL , NULL , " %s: ready to run " , argv [ 0 ] ) ;
2009-09-14 12:58:22 +02:00
server . run ( ) ;
2009-03-30 17:54:47 +02:00
return 0 ;
2009-09-08 19:11:49 +02:00
} catch ( const std : : exception & ex ) {
SE_LOG_ERROR ( NULL , NULL , " %s " , ex . what ( ) ) ;
} catch ( . . . ) {
SE_LOG_ERROR ( NULL , NULL , " unknown error " ) ;
}
return 1 ;
2009-03-30 17:54:47 +02:00
}