Imported Upstream version 1.0beta2a upstream/1.0beta2a
authorPatrick Ohly <patrick.ohly@intel.com>
Fri, 28 Jun 2013 11:40:23 +0000 (11:40 +0000)
committerPatrick Ohly <patrick.ohly@intel.com>
Fri, 28 Jun 2013 11:40:23 +0000 (11:40 +0000)
301 files changed:
ChangeLog
Makefile-gen.am
Makefile.am
Makefile.in
NEWS
README
config.h.in
configure
configure-post.in
configure-pre.in
configure.in
po/POTFILES.in
po/it.po
po/zh_TW.po
src/Makefile-gen.am
src/Makefile.am
src/Makefile.in
src/backends/addressbook/Makefile.in
src/backends/evolution/EvolutionCalendarSource.cpp
src/backends/evolution/EvolutionCalendarSource.h
src/backends/evolution/EvolutionCalendarSourceRegister.cpp
src/backends/evolution/EvolutionContactSource.cpp
src/backends/evolution/EvolutionContactSource.h
src/backends/evolution/EvolutionSyncSource.cpp
src/backends/evolution/Makefile.in
src/backends/evolution/configure-sub.in
src/backends/evolution/libical/icalstrdup.c
src/backends/evolution/libical/icalstrdup.h
src/backends/file/FileSyncSource.cpp
src/backends/file/FileSyncSource.h
src/backends/file/Makefile.in
src/backends/maemo/MaemoCalendarSource.cpp
src/backends/maemo/MaemoCalendarSource.h
src/backends/maemo/Makefile.in
src/backends/sqlite/Makefile.in
src/backends/sqlite/SQLiteContactSource.cpp
src/backends/sqlite/SQLiteContactSource.h
src/backends/xmlrpc/Makefile.in
src/backends/xmlrpc/XMLRPCSyncSource.cpp
src/backends/xmlrpc/XMLRPCSyncSource.h
src/backends/xmlrpc/XMLRPCSyncSourceRegister.cpp
src/dbus/Makefile.in
src/dbus/interfaces/Makefile.in
src/dbus/interfaces/syncevo-marshal.list
src/dbus/interfaces/syncevo-server-full.xml
src/dbus/syncevo-dbus-types.c
src/dbus/syncevo-dbus-types.h
src/dbus/syncevo-server.c
src/dbus/syncevo-server.h
src/dbus/syncevo-session.c
src/dbus/syncevo-session.h
src/gdbus/Makefile.in
src/gdbus/gdbus-cxx-bridge.h
src/gnome-bluetooth/Makefile.am [new file with mode: 0644]
src/gnome-bluetooth/Makefile.in [new file with mode: 0644]
src/gnome-bluetooth/syncevolution.c [new file with mode: 0644]
src/gtk-ui/Makefile.am
src/gtk-ui/Makefile.in
src/gtk-ui/main.c
src/gtk-ui/mux-window.c
src/gtk-ui/mux-window.h
src/gtk-ui/sync-config-widget.c
src/gtk-ui/sync-config-widget.h
src/gtk-ui/sync-spinner.gif [new file with mode: 0644]
src/gtk-ui/sync-ui-config.c
src/gtk-ui/sync-ui-config.h
src/gtk-ui/sync-ui.c
src/gtk-ui/sync-ui.h
src/gtk-ui/ui.glade
src/syncclient_sample_config.xml [deleted file]
src/syncevo-dbus-server.cpp
src/syncevo/Cmdline.cpp
src/syncevo/ConfigNode.h
src/syncevo/Makefile.am
src/syncevo/Makefile.in
src/syncevo/ObexTransportAgent.cpp
src/syncevo/ObexTransportAgent.h
src/syncevo/SmartPtr.h
src/syncevo/SoupTransportAgent.cpp
src/syncevo/SoupTransportAgent.h
src/syncevo/SyncConfig.cpp
src/syncevo/SyncConfig.h
src/syncevo/SyncContext.cpp
src/syncevo/SyncContext.h
src/syncevo/SyncEvolutionXML.c [deleted file]
src/syncevo/SyncML.cpp
src/syncevo/SyncML.h
src/syncevo/SyncSource.cpp
src/syncevo/SyncSource.h
src/syncevo/SynthesisDBPlugin.cpp
src/syncevo/TrackingSyncSource.cpp
src/syncevo/TrackingSyncSource.h
src/syncevo/configs/Makefile.am [new file with mode: 0644]
src/syncevo/configs/Makefile.in [new file with mode: 0644]
src/syncevo/configs/README [new file with mode: 0644]
src/syncevo/configs/datatypes/00vcard-fieldlist.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/01vcard-profile.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/02vcard-types.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/10calendar-fieldlist.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/11calendar-profile.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/12calendar-types.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/20note-fieldlist.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/21note-profile.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/22notes-types.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/30bookmark-fieldlist.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/31bookmark-profile.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/32bookmark-type.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/server/40email-fieldlist.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/server/41email-profile.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/server/42email-type-zipped.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/server/42email-type.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/server/43email-sonyericsson.xml [new file with mode: 0644]
src/syncevo/configs/datatypes/server/44email-nokia9500.xml [new file with mode: 0644]
src/syncevo/configs/debug/00default.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/client/00zyb.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/evolution.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/00_t39m.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/01_t68.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/02_V3.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/03_V3i.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/04_6230.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/05_9210.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/06_9210i.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/07_3220.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/08_3230.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/09_3600.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/10_3620.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/11_3650.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/12_3660.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/13_6260.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/14_6600.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/15_6620.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/16_6630.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/17_6670.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/18_7250.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/19_7250i.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/20_7260.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/21_7610.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/22_7650.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/23_N-Gage.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/24_N-Gage_QD.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/25_9300.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/26_9500.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/27_E90.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/28_X.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/29_SX1.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/30_M55.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/31_SL55.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/32_S55.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/33_S65.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/34_SL65.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/35_K700.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/36_T610_T630.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/37_M600i.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/38_P800.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/39_P900.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/40_P910.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/41_P910i.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/42_P990i.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/43_t68i.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/44_Funambol_Outlook.xml [new file with mode: 0644]
src/syncevo/configs/remoterules/server/45_SyncJe_Outlook.xml [new file with mode: 0644]
src/syncevo/configs/scripting/05vcard-evolution.xml [new file with mode: 0644]
src/syncevo/configs/scripting/06todo-priorities.xml [new file with mode: 0644]
src/syncevo/configs/scripting/06vcard-fullname.xml [new file with mode: 0644]
src/syncevo/configs/scripting/10newuid.xml [new file with mode: 0644]
src/syncevo/configs/scripting/11calendar.xml [new file with mode: 0644]
src/syncevo/configs/scripting/client/00timeout.xml [new file with mode: 0644]
src/syncevo/configs/scripting/server/12email.xml [new file with mode: 0644]
src/syncevo/configs/syncevolution.xml [new file with mode: 0644]
src/syncevo/configs/update-samples.pl [new file with mode: 0755]
src/syncevo/eds_abi_wrapper.cpp
src/syncevo/eds_abi_wrapper.h
src/syncevo/util.cpp
src/syncevo/util.h
src/synthesis/ChangeLog
src/synthesis/doc/SySync_script_call_flow.numbers/Contents/PkgInfo [new file with mode: 0644]
src/synthesis/doc/SySync_script_call_flow.numbers/QuickLook/Thumbnail.jpg [new file with mode: 0644]
src/synthesis/doc/SySync_script_call_flow.numbers/SySync diagram_plugin_small.jpg [new file with mode: 0644]
src/synthesis/doc/SySync_script_call_flow.numbers/document-thumbnail.tiff [new file with mode: 0644]
src/synthesis/doc/SySync_script_call_flow.numbers/index.xml.gz [new file with mode: 0644]
src/synthesis/doc/SySync_script_call_flow.pdf [new file with mode: 0644]
src/synthesis/src/DB_interfaces/api_db/dbapi.cpp
src/synthesis/src/DB_interfaces/api_db/dbapi.h
src/synthesis/src/DB_interfaces/api_db/dbapi_include.h
src/synthesis/src/DB_interfaces/api_db/sync_dbapiconnect.cpp
src/synthesis/src/Makefile.am
src/synthesis/src/Makefile.am.in
src/synthesis/src/Makefile.in
src/synthesis/src/Targets/clientEngine_dbg/target_options.h
src/synthesis/src/client_engine_linux.mk
src/synthesis/src/global_options.h
src/synthesis/src/platform_adapters/linux/configfiles.cpp
src/synthesis/src/syncml_tk/src/sml/lib/all/libstr.c
src/synthesis/src/syncml_tk/src/sml/lib/inc/libstr.h
src/synthesis/src/syncml_tk/src/sml/xlt/all/xltdecxml.c
src/synthesis/src/syncml_tk/src/sml/xlt/all/xlttags.c
src/synthesis/src/sysync/binfileimplclient.cpp
src/synthesis/src/sysync/customimplds.h
src/synthesis/src/sysync/debuglogger.cpp
src/synthesis/src/sysync/engineinterface.cpp
src/synthesis/src/sysync/localengineds.cpp
src/synthesis/src/sysync/localengineds.h
src/synthesis/src/sysync/mimedirprofile.cpp
src/synthesis/src/sysync/mimedirprofile.h
src/synthesis/src/sysync/rrules.cpp
src/synthesis/src/sysync/scriptcontext.cpp
src/synthesis/src/sysync/stdlogicds.cpp
src/synthesis/src/sysync/stdlogicds.h
src/synthesis/src/sysync/superdatastore.cpp
src/synthesis/src/sysync/superdatastore.h
src/synthesis/src/sysync/syncagent.cpp
src/synthesis/src/sysync/syncappbase.cpp
src/synthesis/src/sysync/syncappbase.h
src/synthesis/src/sysync/synccommand.cpp
src/synthesis/src/sysync/syncsession.cpp
src/synthesis/src/sysync/syncsession.h
src/synthesis/src/sysync/syserial.h
src/synthesis/src/sysync/tz_table.h
src/synthesis/src/sysync_SDK/Sources/SDK_support.cpp
src/synthesis/src/sysync_SDK/Sources/SDK_support.h
src/synthesis/src/sysync_SDK/Sources/SDK_util.c
src/synthesis/src/sysync_SDK/Sources/enginemodulebase.cpp
src/synthesis/src/sysync_SDK/Sources/syerror.h
src/synthesis/src/sysync_SDK/Sources/sync_dbapidef.h
src/synthesis/src/sysync_SDK/configs/README [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/00vcard-fieldlist.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/01vcard-profile.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/02vcard-types.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/10calendar-fieldlist.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/11calendar-profile.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/12calendar-types.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/20note-fieldlist.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/21note-profile.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/22notes-types.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/30bookmark-fieldlist.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/31bookmark-profile.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/32bookmark-type.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/server/40email-fieldlist.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/server/41email-profile.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/server/42email-type-zipped.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/server/42email-type.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/server/43email-sonyericsson.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/datatypes/server/44email-nokia9500.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/debug/00default.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/00_t39m.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/01_t68.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/02_V3.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/03_V3i.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/04_6230.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/05_9210.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/06_9210i.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/07_3220.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/08_3230.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/09_3600.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/10_3620.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/11_3650.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/12_3660.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/13_6260.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/14_6600.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/15_6620.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/16_6630.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/17_6670.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/18_7250.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/19_7250i.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/20_7260.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/21_7610.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/22_7650.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/23_N-Gage.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/24_N-Gage_QD.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/25_9300.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/26_9500.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/27_E90.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/28_X.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/29_SX1.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/30_M55.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/31_SL55.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/32_S55.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/33_S65.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/34_SL65.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/35_K700.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/36_T610_T630.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/37_M600i.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/38_P800.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/39_P900.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/40_P910.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/41_P910i.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/42_P990i.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/43_t68i.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/44_Funambol_Outlook.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/remoterules/server/45_SyncJe_Outlook.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/scripting/10newuid.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/scripting/11calendar.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/scripting/client/00timeout.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/scripting/server/12email.xml [new file with mode: 0644]
src/synthesis/src/sysync_SDK/configs/syncclient_sample_config.xml
src/synthesis/src/sysync_SDK/configs/syncserv_sample_config.xml
src/synthesis/src/sysync_SDK/configs/update-samples.pl [new file with mode: 0755]
test/ClientTest.cpp
test/Makefile.in
test/synccompare.pl

index 359e804..62b8db5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 # Generated by configure.  Do no edit.
 
+2010-02-24  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * configure-pre.in:
+
+       minor version bump to 1.0beta2a because of Bluetooth dependency
+
+2010-02-24  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/eds_abi_wrapper.cpp:
+       * src/syncevo/eds_abi_wrapper.h:
+
+       libbluetooth: avoid dependency because of str2ba (MB #9289)
+
+2010-02-24  GLS_ITA  <lorenzo.gennaro@ptiglobal.net>
+
+       * po/it.po:
+
+       l10n: Updates to Italian (it) translation
+
+2010-02-24  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: fix double free in error handling (MB #9869)
+
+2010-02-24  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: handle error 22002 (syncevolution died unexpectedly)
+
+2010-02-23  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: use bluetooth panel on Moblin
+
+2010-02-24  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       Server alerted sync: better error status (MB#8879)
+
+2010-02-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * NEWS:
+
+       NEWS: updated for 1.0 beta 2
+
+2010-02-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/backends/evolution/EvolutionCalendarSource.cpp:
+       * src/syncevo/TrackingSyncSource.h:
+       * test/ClientTest.cpp:
+
+       EvolutionCalendarSource: change tracking when deleting a child
+       event
+
+2010-02-23  GLS_ITA  <lorenzo.gennaro@ptiglobal.net>
+
+       * po/it.po:
+
+       l10n: Updates to Italian (it) translation
+
+2010-02-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       LogDir::startSession(): avoid [ERROR] when logdir does not exist
+
+2010-02-23  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: fix possible crasher
+
+2010-02-23  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: use TemplatesChanged to update device list
+
+2010-02-23  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/dbus/syncevo-server.c:
+       * src/dbus/syncevo-server.h:
+
+       dbus client bindings: implement TemplatesChanged
+
+2010-02-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/dbus/interfaces/syncevo-server-full.xml:
+       * src/syncevo-dbus-server.cpp:
+
+       D-Bus interface: added Server.TemplatesChanged (MB #9841)
+
+2010-02-23  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo-dbus-server.cpp:
+
+       DBus server: clear existing templates for GetConfigs
+
+2010-02-23  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo-dbus-server.cpp:
+       * src/syncevo/Cmdline.cpp:
+       * src/syncevo/SyncConfig.cpp:
+       * src/syncevo/SyncConfig.h:
+
+       DBus server: refine implementation of bluetooth devices
+
+2010-02-23  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/gdbus/gdbus-cxx-bridge.h:
+       * src/syncevo-dbus-server.cpp:
+
+       dbus server: listen to signals sent from bluez
+
+2010-02-22  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/eds_abi_wrapper.h:
+
+       libical _r patch: fix for compilation with older libecal
+
+2010-02-22  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/backends/evolution/EvolutionContactSource.cpp:
+       * src/backends/evolution/EvolutionSyncSource.cpp:
+
+       Evolution Address Book: avoid picking CouchDB by default, again
+       (MB #7877)
+
+2010-02-22  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * Makefile-gen.am:
+
+       syncevolution.org packages: conflict with system libs (MB #9811)
+
+2010-02-22  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncML.h:
+
+       sync session error: added STATUS_DIED_PREMATURELY = 22002 (MB
+       #9844)
+
+2010-02-20  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: don't use gtk_dialog_get_content_area()
+
+2010-02-22  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/backends/evolution/configure-sub.in:
+       * src/backends/evolution/libical/icalstrdup.c:
+       * src/backends/evolution/libical/icalstrdup.h:
+       * src/syncevo/eds_abi_wrapper.cpp:
+       * src/syncevo/eds_abi_wrapper.h:
+
+       workaround for libical 0.43 memory handling bug
+
+2010-02-19  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/util.cpp:
+
+       SHA-256 + Mozilla NSS: must call init
+
+2010-02-19  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       less output (MB #8092)
+
+2010-02-19  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       shorter logs (MB #8092)
+
+2010-02-19  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncConfig.cpp:
+
+       deviceId: use syncevolution- prefix, helps Horde (MB #9347)
+
+2010-02-19  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncConfig.cpp:
+
+       preventSlowSync: enabled by default (MB #2416)
+
+2010-02-19  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncConfig.cpp:
+
+       maxMsgSize: increased from 20000 to 150000
+
+2010-02-10  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: implement InfoRequest handling for passwords
+
+2010-02-10  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/dbus/interfaces/syncevo-marshal.list:
+       * src/dbus/syncevo-server.c:
+       * src/dbus/syncevo-server.h:
+
+       dbus client bindings: update InfoRequest signature
+
+2010-02-18  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/backends/evolution/EvolutionCalendarSourceRegister.cpp:
+       * src/backends/xmlrpc/XMLRPCSyncSourceRegister.cpp:
+
+       text/x-calendar -> text/x-vcalendar renaming was incomplete
+
+2010-02-18  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       SyncContext: fixed broken virtual source data format check
+
+2010-02-18  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       LogDir::expire(): CPPUnit testing (MB #7708)
+
+2010-02-18  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       LogDir::expire(): more intelligent removal of session dirs (MB
+       #7708)
+
+2010-02-18  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       SyncContext.cpp: added testing of session handling
+
+2010-02-18  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncML.cpp:
+       * src/syncevo/SyncML.h:
+
+       LogDir::startSession(): fixed collision check
+
+2010-02-18  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       LogDir::haveDifferentContent() - detect differences between
+       backups based on inodes
+
+2010-02-18  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       LogDir/SourceList::startSession(): removed obsolete "logname"
+       parameter
+
+2010-02-16  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * test/synccompare.pl:
+
+       synccompare: bug fix for recent hard linkining improvement
+
+2010-02-16  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncSource.h:
+       * src/syncevo/TrackingSyncSource.h:
+
+       database comparison: also delay it in clients (MB #7710)
+
+2010-02-16  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/backends/evolution/EvolutionCalendarSource.cpp:
+       * src/backends/evolution/EvolutionCalendarSource.h:
+       * src/backends/evolution/EvolutionContactSource.cpp:
+       * src/backends/evolution/EvolutionContactSource.h:
+       * src/backends/file/FileSyncSource.cpp:
+       * src/backends/file/FileSyncSource.h:
+       * src/backends/maemo/MaemoCalendarSource.cpp:
+       * src/backends/maemo/MaemoCalendarSource.h:
+       * src/backends/xmlrpc/XMLRPCSyncSource.cpp:
+       * src/backends/xmlrpc/XMLRPCSyncSource.h:
+       * src/syncevo/TrackingSyncSource.cpp:
+       * src/syncevo/TrackingSyncSource.h:
+
+       TrackingSyncSource: added isEmpty() pure virtual method (MB
+       #7708)
+
+2010-02-16  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/backends/sqlite/SQLiteContactSource.cpp:
+       * src/backends/sqlite/SQLiteContactSource.h:
+
+       SQLite backend: implement m_isEmpty operation (MB #7708)
+
+2010-02-16  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncSource.h:
+
+       SyncSource API: added m_isEmpty operation (MB #7708)
+
+2010-02-16  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncSource.cpp:
+       * src/syncevo/SyncSource.h:
+       * test/ClientTest.cpp:
+
+       SyncSourceRevisions: cache result of listAllItems() (MB #7708)
+
+2010-02-16  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       SAN + virtual source: segfault fixed (MB #9737)
+
+2010-02-15  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncContext.h:
+
+       virtual sources: avoid name collisions with normal sources (MB
+       #9664)
+
+2010-02-15  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       XMLFiles::addFragments(): avoid throwing exceptions by checking
+       for dir first
+
+2010-02-15  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/Cmdline.cpp:
+       * src/syncevo/SyncConfig.cpp:
+       * src/syncevo/SyncConfig.h:
+       * src/syncevo/SyncContext.cpp:
+
+       <dbtypeid>: handle hash collisisions
+
+2010-02-15  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncContext.h:
+
+       SyncContext::prepare(sources): removed, obsolete
+
+2010-02-15  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       virtual datastore: allow alias (MB #9664)
+
+2010-02-15  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/ObexTransportAgent.cpp:
+       * src/syncevo/eds_abi_wrapper.cpp:
+       * src/syncevo/eds_abi_wrapper.h:
+
+       libbluetooth compatibility layer (MB #9289)
+
+2010-02-10  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       virtual source in client: tell engine about superdatastore and
+       URI
+
+2010-02-12  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncML.cpp:
+       * src/syncevo/SyncML.h:
+
+       slow sync detection + virtual source: fixed user message
+
+2010-02-12  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       virtual data sources: improved error handling
+
+2010-02-10  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncContext.h:
+
+       SyncML server: delayed checking of sources (MB #7710)
+
+2010-02-10  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncSource.h:
+       * src/syncevo/SynthesisDBPlugin.cpp:
+
+       SyncSource::Operations: added callback for starting to use source
+
+2010-02-19  zerng07  <pswo10680@gmail.com>
+
+       * po/zh_TW.po:
+
+       l10n: Updates to Chinese (Taiwan) (zh_TW) translation
+
+2010-02-19  zerng07  <pswo10680@gmail.com>
+
+       * po/zh_TW.po:
+
+       l10n: Updates to Chinese (Taiwan) (zh_TW) translation
+
+2010-02-18  zerng07  <pswo10680@gmail.com>
+
+       * po/zh_TW.po:
+
+       l10n: Updates to Chinese (Taiwan) (zh_TW) translation
+
+2010-02-18  zerng07  <pswo10680@gmail.com>
+
+       * po/zh_TW.po:
+
+       l10n: Updates to Chinese (Taiwan) (zh_TW) translation
+
+2010-02-10  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/sync-ui.h:
+
+       gtk-ui: backup-restore improvements
+
+2010-02-10  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: fix keyboard access in configuration
+
+2010-02-10  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * configure-pre.in:
+
+       gtk-ui: fix build with --enable-gui again (MB #9633)
+
+2010-02-10  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: show main view when restoring backup (MB #9617)
+
+2010-02-10  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * build/export-synthesis-xml.sh:
+
+       export-synthesis-xml.sh: export Synthesis XML fragment patches
+
+2010-02-10  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * build/export-gdbus.sh:
+
+       export-gdbus.sh: fixed incorrect comment
+
+2010-02-10  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/syncevo/ObexTransportAgent.cpp:
+
+       ObexTransportAgent: error handling
+
+2010-02-10  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/syncevo/ObexTransportAgent.cpp:
+
+       ObexTransportAgent: remove the non-blocking
+       g_main_context_iteration
+
+2010-02-04  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       detecting slow sync: use LOCERR_DATASTORE_ABORT (MB #2416)
+
+2010-01-29  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       SyncML server: remove redundant SourceConfigSpecials
+
+2010-01-29  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       slow sync detection: use <datastoreinitscript> instead of
+       <alertscript> (MB #2416)
+
+2010-01-29  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncSource.h:
+
+       SAN + forced slow sync: move flag into SyncSource
+
+2010-02-09  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo-dbus-server.cpp:
+       * src/syncevo/ObexTransportAgent.cpp:
+       * src/syncevo/ObexTransportAgent.h:
+       * src/syncevo/SoupTransportAgent.cpp:
+       * src/syncevo/SoupTransportAgent.h:
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncContext.h:
+
+       syncevo-dbus-server + OBEX: transport was not enabled (MB #9436)
+
+2010-02-09  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * configure-post.in:
+
+       autotools: fixed check for glib > 2.16
+
+2010-02-09  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/Makefile-gen.am:
+       * src/gnome-bluetooth/Makefile.am:
+       * src/syncevo/configs/Makefile.am:
+
+       autotools: some more fixes
+
+2010-02-09  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/syncevo-dbus-server.cpp:
+
+       syncevo-dbus-server: fix GetConfigs() hang
+
+2010-02-09  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: start bluetooth wizard when Add device clicked
+
+2010-02-09  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/dbus/syncevo-dbus-types.c:
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: use "fingerprint" for device template selector
+
+2010-02-08  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo-dbus-server.cpp:
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncSource.cpp:
+       * src/syncevo/SyncSource.h:
+
+       virtual source: support D-Bus CheckSource() (MB #9535)
+
+2010-02-08  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncSource.cpp:
+
+       database dumps: use SHA-256 when available (MB #7708)
+
+2010-02-08  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * README:
+       * README.packagers:
+       * configure-post.in:
+       * src/syncevo/Makefile.am:
+       * src/syncevo/util.cpp:
+       * src/syncevo/util.h:
+
+       SHA-256: use glib or Mozilla NSS
+
+2010-02-08  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SmartPtr.h:
+
+       SmartPtr: added smart pointer for glib char string
+
+2010-02-08  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       command line: compare against dump in last session involving
+       source (MB #7708)
+
+2010-02-07  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * test/synccompare.pl:
+
+       synccompare: don't include identical files in comparison (MB
+       #7708)
+
+2010-02-06  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncSource.cpp:
+
+       SyncSourceRevisions: reuse data files from previous backup (MB
+       #7708)
+
+2010-02-06  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/util.cpp:
+       * src/syncevo/util.h:
+
+       Hash(): added version for std::string
+
+2010-02-06  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/ConfigNode.h:
+
+       ConfigNode::getProperty(): added const declaration
+
+2010-02-05  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       SourceList: determine most recent backup of source when making
+       next backup (MB #7708)
+
+2010-02-05  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       LogDir: fixed finding sessions inside non-standard context with
+       escaped chars
+
+2010-02-05  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncSource.cpp:
+       * src/syncevo/SyncSource.h:
+       * test/ClientTest.cpp:
+
+       SyncSource API: access to previous backup (MB #7708)
+
+2010-02-08  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncContext.cpp:
+
+       LogDir: cleanup
+
+2010-02-08  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/sync-ui.h:
+
+       gtk-ui: workaround for missing "active" in MxGtkLightSwitch
+
+2010-02-08  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: ask for 1024 window width for gtk windows
+
+2010-02-08  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/sync-ui.h:
+
+       gtk-ui: improve confirmation dialogs
+
+2010-02-08  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: improve virtual source display in config
+
+2010-02-08  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/dbus/syncevo-dbus-types.c:
+
+       dbus client bindings: handle no mode as "none"
+
+2010-02-08  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-config-widget.h:
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: changes in config api
+
+2010-02-06  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+
+       gtk-ui: hide virtual sources when not usable + other tweaks
+
+2010-02-06  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: escape source names when using markup
+
+2010-02-05  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/sync-ui.h:
+
+       gtk-ui: improve slow sync messages
+
+2010-02-03  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-config-widget.h:
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: very rough draft of device template selection
+
+2010-02-03  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+
+       gtk-ui: don't require username/password for device configs
+
+2010-02-02  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-config-widget.h:
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: layout & code updates for device config UI
+
+2010-02-02  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-config-widget.h:
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: modify SyncConfigWidget for device sync changes
+
+2010-02-03  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/dbus/interfaces/syncevo-server-full.xml:
+       * src/gdbus/gdbus-cxx-bridge.h:
+       * src/syncevo-dbus-server.cpp:
+       * src/syncevo/Cmdline.cpp:
+       * src/syncevo/SyncConfig.cpp:
+       * src/syncevo/SyncConfig.h:
+
+       DBus server: Implement GetConfigs with device querying(MB#9216)
+
+2010-02-04  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/Makefile-gen.am:
+
+       autotools: gnome-bluetooth + make dist
+
+2010-02-04  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/Makefile.am:
+
+       SyncEvolutionXML.c: treat it as generated file
+
+2010-02-04  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncConfig.cpp:
+
+       syncURL: updated description (MB #9446)
+
+2010-02-04  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncConfig.cpp:
+
+       SyncConfig::getSyncURL(): workaround for g++ 4.3/4.4 bug
+
+2010-02-04  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * README:
+       * README.packagers:
+       * configure-post.in:
+       * configure-pre.in:
+       * po/POTFILES.in:
+       * src/Makefile-gen.am:
+       * src/gnome-bluetooth/Makefile.am:
+       * src/gnome-bluetooth/configure-sub.in:
+
+       autotools: build GNOME Bluetooth panel plugin (MB #7089)
+
+2010-02-03  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/gnome-bluetooth/syncevolution.c:
+
+       Bluetooth pairing integration (MB#7089)
+
+2010-02-04  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/syncevo-dbus-server.cpp:
+
+       Presence detection: bug fix
+
+2010-02-04  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * configure-pre.in:
+
+       fix build with "--enable-gui=all"
+
+2010-02-04  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/main.c:
+
+       gtk-ui: don't open two main windows (MB #9449)
+
+2010-02-04  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: fix suspicious string array handling (MB #9434)
+
+2010-02-03  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/syncevo-dbus-server.cpp:
+
+       DBusServer: coding convention changes
+
+2010-02-03  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/gdbus/gdbus-cxx-bridge.h:
+
+       gdbus-cxx-bridge: Copy DBusCallObject member to be more safe
+
+2010-02-03  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/main.c:
+
+       gtk-ui: fix another problem with non-libunique build
+
+2010-02-03  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/gtk-ui/main.c:
+
+       gtk-gui: fixed compiler error when not having libunique
+
+2010-02-03  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * configure-pre.in:
+
+       post-release version bump
+
+2010-02-02  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: forgot to change source naming Memo->Notes
+
+2010-02-01  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+
+       gtk-ui: --show-settings should match url prefix (MB #9284)
+
+2010-02-01  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: ellipsize very long service name in main view
+
+2010-02-01  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: make config usable with long strings (MB #9278)
+
+2010-01-28  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: remember to update service list when it is shown
+
+2010-01-28  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/dbus/syncevo-dbus-types.c:
+       * src/dbus/syncevo-dbus-types.h:
+       * src/gtk-ui/sync-ui-config.h:
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: handle source phases correctly (MB #9320)
+
+2010-01-28  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: Fix error output
+
+2010-01-28  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/dbus/syncevo-dbus-types.c:
+       * src/dbus/syncevo-dbus-types.h:
+       * src/gtk-ui/Makefile.am:
+       * src/gtk-ui/sync-spinner.gif:
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: show status "waiting" with a progress spinner
+
+2010-01-28  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui-config.c:
+       * src/gtk-ui/sync-ui-config.h:
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: don't allow non-defined sources in emergency
+
+2010-01-27  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-ui-config.c:
+       * src/gtk-ui/sync-ui-config.h:
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: use PeerName property for user visible names
+
+2010-01-27  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: tweak emergency view layout, modify strings
+
+2010-01-27  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/dbus/syncevo-dbus-types.c:
+
+       dbus client bindings: fix syncevo_config_get_value() return value
+
+2010-01-26  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/main.c:
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-config-widget.h:
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/sync-ui.h:
+
+       gtk-ui: support "--show-settings <id>"
+
+2010-01-26  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: only ask for backups for correct context
+
+2010-01-26  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: string and style changes
+
+2010-01-26  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/mux-window.c:
+       * src/gtk-ui/mux-window.h:
+       * src/gtk-ui/sync-config-widget.c:
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: change bread-crumb functionality in moblin
+
+2010-01-25  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: don't show "last synced ..." when last sync failed
+
+2010-01-25  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+
+       gtk-ui: only show backups that contain selected sources
+
+2010-01-25  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: implement restoring backups
+
+2010-01-25  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/dbus/syncevo-session.c:
+       * src/dbus/syncevo-session.h:
+
+       dbus bindings: add Session.Restore()
+
+2010-01-25  Jussi Kukkonen  <jku@linux.intel.com>
+
+       * src/gtk-ui/sync-ui.c:
+       * src/gtk-ui/ui.glade:
+
+       gtk-ui: remove uneeded "Data" text from main view
+
+2010-01-29  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/syncevo-dbus-server.cpp:
+       * src/syncevo/SyncConfig.cpp:
+       * src/syncevo/SyncConfig.h:
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncContext.h:
+
+       SyncURL: support multiple transport values in SyncURL property
+
+2010-02-03  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/syncevo-dbus-server.cpp:
+       * test/test-dbus.py:
+
+       Presence detection: connman part (MB#7700)
+
+2010-02-02  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/gdbus/gdbus-cxx-bridge.h:
+
+       gdbus: cxxbridge for asynchronous dbus client call
+
+2010-02-02  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * configure-post.in:
+       * src/Makefile-gen.am:
+       * src/sunbird_client.xml:
+       * src/syncclient_sample_config.xml:
+       * src/syncevo/Makefile.am:
+       * src/syncevo/SyncContext.cpp:
+       * src/syncevo/SyncContext.h:
+       * src/syncevo/configs/Makefile.am:
+       * src/syncevo/configs/syncevolution.xml:
+       * src/syncevo/configs/update-samples.pl:
+
+       XML config: use configuration composed from fragments (MB #7712)
+
+2009-06-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/datatypes/11calendar-profile.xml:
+
+       XML config: avoid empty LOCATION in VEVENT
+
+2010-01-22  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/datatypes/00vcard-fieldlist.xml:
+       * src/syncevo/configs/datatypes/01vcard-profile.xml:
+
+       syncclient_sample_config.xml: added "GENDER" and "SIP"
+
+2009-12-18  Rajyalakshmi Bommaraju  <Rajyalakshmi.Bommaraju@intel.com>
+
+       * src/syncevo/configs/datatypes/02vcard-types.xml:
+       * src/syncevo/configs/scripting/06vcard-fullname.xml:
+
+       syncclient_sample_config.xml: create fullname if empty (MB#5664)
+
+2010-01-06  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo/configs/datatypes/00vcard-fieldlist.xml:
+       * src/syncevo/configs/datatypes/01vcard-profile.xml:
+
+       Config: add support for 'X-SKYPE' for evolution (MB#8948)
+
+2009-12-11  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/syncevo/configs/datatypes/11calendar-profile.xml:
+
+       synthesis config: add "STATUS" property to "VEVENT" profile.
+
+2009-12-01  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo/configs/remoterules/client/00zyb.xml:
+
+       syncclient config: disable anchors checking for ZYB(MB#8138)
+
+2009-10-15  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo/configs/scripting/06todo-priorities.xml:
+       * src/syncevo/configs/scripting/11calendar.xml:
+
+       SyncSource: Add 'prioprity' conversion between vCalendar1.0 and
+       2.0
+
+2009-10-15  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo/configs/scripting/05vcard-evolution.xml:
+
+       SyncSource: change names for m_incomingScript and
+       m_outgointScript
+
+2009-10-06  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/scripting/05vcard-evolution.xml:
+
+       Google->Evolution: make sure that WORK/HOME numbers are displayed
+       (MB #6501)
+
+2009-08-29  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo/configs/scripting/05vcard-evolution.xml:
+
+       Memotoo: add a workaround for 'TEL' with 'cell' type(bug#5633)
+
+2009-08-06  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo/configs/scripting/11calendar.xml:
+
+       Mobical: Strip time information for EXDATE if not needed  (Bug
+       #3009)
+
+2009-07-29  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/scripting/11calendar.xml:
+
+       calendar support: sanitize incoming EXDATEs (Bugzilla #4457)
+
+2009-07-15  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo/configs/scripting/11calendar.xml:
+
+       Funambol: A workaround for 'ACTION' lost by Funambol server (Bug
+       #2422)
+
+2009-06-25  zhu, yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo/configs/datatypes/11calendar-profile.xml:
+
+       iCal20: Add 'suppressempty="yes"' for 'LAST-MODIFIED'
+       property(Bug #2422)
+
+2009-06-24  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/datatypes/01vcard-profile.xml:
+
+       vCard: support X-MANAGER/ASSISTANT/SPOUSE/ANNIVERSARY (Bugzilla
+       #2418)
+
+2009-06-24  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/remoterules/evolution.xml:
+
+       data conversion: different data profiles for SyncML peer + local
+       DB with remote rules
+
+2009-06-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/scripting/11calendar.xml:
+
+       XML config: keep alarm times as they are
+
+2009-06-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/datatypes/11calendar-profile.xml:
+
+       XML config: be conservative about encoding of EXDATEs
+
+2009-06-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/datatypes/10calendar-fieldlist.xml:
+       * src/syncevo/configs/datatypes/11calendar-profile.xml:
+
+       XML config: enhanced calendar data formats
+
+2009-06-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/datatypes/00vcard-fieldlist.xml:
+       * src/syncevo/configs/datatypes/01vcard-profile.xml:
+
+       XML config: added several vCard 3.0/Evolution specific properties
+
+2009-06-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/datatypes/00vcard-fieldlist.xml:
+
+       XML config: don't enforce http:// prefix in vCard 2.1 URL
+
+2009-06-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/datatypes/01vcard-profile.xml:
+
+       XML config: use NICKNAME also in vCard 2.1
+
+2009-06-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/datatypes/11calendar-profile.xml:
+       * src/syncevo/configs/scripting/11calendar.xml:
+
+       XML config: don't encode empty DESCRIPTION in VEVENT/VTODO
+
+2010-02-02  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/update-samples.pl:
+
+       update-samples.pl: accept command line parameters for updating a
+       specific file
+
+2010-02-01  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/configs/README:
+       * src/syncevo/configs/datatypes/00vcard-fieldlist.xml:
+       * src/syncevo/configs/datatypes/01vcard-profile.xml:
+       * src/syncevo/configs/datatypes/02vcard-types.xml:
+       * src/syncevo/configs/datatypes/10calendar-fieldlist.xml:
+       * src/syncevo/configs/datatypes/11calendar-profile.xml:
+       * src/syncevo/configs/datatypes/12calendar-types.xml:
+       * src/syncevo/configs/datatypes/20note-fieldlist.xml:
+       * src/syncevo/configs/datatypes/21note-profile.xml:
+       * src/syncevo/configs/datatypes/22notes-types.xml:
+       * src/syncevo/configs/datatypes/30bookmark-fieldlist.xml:
+       * src/syncevo/configs/datatypes/31bookmark-profile.xml:
+       * src/syncevo/configs/datatypes/32bookmark-type.xml:
+       * src/syncevo/configs/datatypes/server/40email-fieldlist.xml:
+       * src/syncevo/configs/datatypes/server/41email-profile.xml:
+       * src/syncevo/configs/datatypes/server/42email-type-zipped.xml:
+       * src/syncevo/configs/datatypes/server/42email-type.xml:
+       * src/syncevo/configs/datatypes/server/43email-sonyericsson.xml:
+       * src/syncevo/configs/datatypes/server/44email-nokia9500.xml:
+       * src/syncevo/configs/debug/00default.xml:
+       * src/syncevo/configs/remoterules/server/00_t39m.xml:
+       * src/syncevo/configs/remoterules/server/01_t68.xml:
+       * src/syncevo/configs/remoterules/server/02_V3.xml:
+       * src/syncevo/configs/remoterules/server/03_V3i.xml:
+       * src/syncevo/configs/remoterules/server/04_6230.xml:
+       * src/syncevo/configs/remoterules/server/05_9210.xml:
+       * src/syncevo/configs/remoterules/server/06_9210i.xml:
+       * src/syncevo/configs/remoterules/server/07_3220.xml:
+       * src/syncevo/configs/remoterules/server/08_3230.xml:
+       * src/syncevo/configs/remoterules/server/09_3600.xml:
+       * src/syncevo/configs/remoterules/server/10_3620.xml:
+       * src/syncevo/configs/remoterules/server/11_3650.xml:
+       * src/syncevo/configs/remoterules/server/12_3660.xml:
+       * src/syncevo/configs/remoterules/server/13_6260.xml:
+       * src/syncevo/configs/remoterules/server/14_6600.xml:
+       * src/syncevo/configs/remoterules/server/15_6620.xml:
+       * src/syncevo/configs/remoterules/server/16_6630.xml:
+       * src/syncevo/configs/remoterules/server/17_6670.xml:
+       * src/syncevo/configs/remoterules/server/18_7250.xml:
+       * src/syncevo/configs/remoterules/server/19_7250i.xml:
+       * src/syncevo/configs/remoterules/server/20_7260.xml:
+       * src/syncevo/configs/remoterules/server/21_7610.xml:
+       * src/syncevo/configs/remoterules/server/22_7650.xml:
+       * src/syncevo/configs/remoterules/server/23_N-Gage.xml:
+       * src/syncevo/configs/remoterules/server/24_N-Gage_QD.xml:
+       * src/syncevo/configs/remoterules/server/25_9300.xml:
+       * src/syncevo/configs/remoterules/server/26_9500.xml:
+       * src/syncevo/configs/remoterules/server/27_E90.xml:
+       * src/syncevo/configs/remoterules/server/28_X.xml:
+       * src/syncevo/configs/remoterules/server/29_SX1.xml:
+       * src/syncevo/configs/remoterules/server/30_M55.xml:
+       * src/syncevo/configs/remoterules/server/31_SL55.xml:
+       * src/syncevo/configs/remoterules/server/32_S55.xml:
+       * src/syncevo/configs/remoterules/server/33_S65.xml:
+       * src/syncevo/configs/remoterules/server/34_SL65.xml:
+       * src/syncevo/configs/remoterules/server/35_K700.xml:
+       * src/syncevo/configs/remoterules/server/36_T610_T630.xml:
+       * src/syncevo/configs/remoterules/server/37_M600i.xml:
+       * src/syncevo/configs/remoterules/server/38_P800.xml:
+       * src/syncevo/configs/remoterules/server/39_P900.xml:
+       * src/syncevo/configs/remoterules/server/40_P910.xml:
+       * src/syncevo/configs/remoterules/server/41_P910i.xml:
+       * src/syncevo/configs/remoterules/server/42_P990i.xml:
+       * src/syncevo/configs/remoterules/server/43_t68i.xml:
+       * src/syncevo/configs/remoterules/server/44_Funambol_Outlook.xml:
+       * src/syncevo/configs/remoterules/server/45_SyncJe_Outlook.xml:
+       * src/syncevo/configs/scripting/10newuid.xml:
+       * src/syncevo/configs/scripting/11calendar.xml:
+       * src/syncevo/configs/scripting/client/00timeout.xml:
+       * src/syncevo/configs/scripting/server/12email.xml:
+       * src/syncevo/configs/update-samples.pl:
+
+       XML config samples: split up into individual fragments
+
+2010-02-02  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * build/import-synthesis-xml.sh:
+
+       import-synthesis-xml.sh: import shared XML config fragments (MB
+       #7712)
+
+2010-02-02  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * build/import-foreign-git.sh:
+
+       import-foreign-git.sh: allow non-existing files
+
+2010-02-02  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * test/test-dbus.py:
+
+       Testing: apply default and user setting local databases (MB#9332)
+
+2010-02-01  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * src/syncevo-dbus-server.cpp:
+       * test/test-dbus.py:
+
+       DBus server: return real passwords for GetConfig (MB#9169)
+
+2010-01-29  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/Cmdline.cpp:
+       * src/syncevo/SyncConfig.cpp:
+
+       config: adding sources affects peers in the same context (MB
+       #9329)
+
+2010-01-29  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/SyncConfig.cpp:
+       * src/syncevo/SyncConfig.h:
+
+       command line: wrong context during --configure (MB #9338)
+
+2010-01-29  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/syncevo/Cmdline.cpp:
+       * src/syncevo/util.cpp:
+       * src/syncevo/util.h:
+
+       ScopedEnvChange: moved from Cmdline.cpp to util
+
+2010-01-28  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * configure-pre.in:
+
+       autotools: removed obsolete libbluetooth2/3 check (MB #9260)
+
+2010-01-21  Zhu, Yongsheng  <yongsheng.zhu@intel.com>
+
+       * test/test-dbus.py:
+
+       Testing: fix failures and remove dependency for test-dbus.py
+       (MB#9065)
+
 2010-01-27  Patrick Ohly  <patrick.ohly@intel.com>
 
        * configure-pre.in:
index 83cf00a..efeb78b 100644 (file)
@@ -99,7 +99,13 @@ PKGS = $(addprefix syncevolution-evolution-, 2.6 2.8 2.12)
 #
 # --replaces is necessary for migrating from syncevolution-evolution-<evover>
 # to syncevolution-evolution (as per http://wiki.debian.org/Renaming_a_Package)
+#
+# When we build shared objects, then conflict with the corresponding
+# system libs. The assumption is that the system library is named
+# after the lib and its major version, which holds for libsmltk and
+# libsynthesis in Debian.
 deb rpm : dist/$(distdir) dist/debian/control description-pak
+       conflicts=`ls -1 dist/$(distdir)/usr/lib/*.so.[0123456789] | sed -e 's;.*/;;' -e 's/\.so\.//' -e 's/$$/, /'` && \
        tmpdir=`mktemp -d $$HOME/syncevolution.XXXXXXXXXX` && \
        trap "rm -rf $$tmpdir" EXIT && \
        fakeroot checkinstall </dev/null \
@@ -113,7 +119,7 @@ deb rpm : dist/$(distdir) dist/debian/control description-pak
                --pkgarch=$(PKGARCH) \
                --provides=syncevolution \
                --replaces="'syncevolution, `echo $(filter-out $(PKGNAME), $(PKGS)) | sed -e 's/  */ (<< 1:0.8.1-2), /g'` (<< 1:0.8.1-2)'" \
-               --conflicts="'syncevolution, `echo $(filter-out $(PKGNAME), $(PKGS)) | sed -e 's/  */ (<< 1:0.8.1-2), /g'` (<< 1:0.8.1-2)'" \
+               --conflicts="'$${conflicts}syncevolution, `echo $(filter-out $(PKGNAME), $(PKGS)) | sed -e 's/  */ (<< 1:0.8.1-2), /g'` (<< 1:0.8.1-2)'" \
                --maintainer="'Patrick Ohly <patrick.ohly@gmx.de>'" \
                --pkgsource='http://sourceforge.net/project/showfiles.php?group_id=146288' \
                --pkgaltsource='http://www.estamos.de/projects/SyncML/' \
index 08b4ce3..9312b4c 100644 (file)
@@ -99,7 +99,13 @@ PKGS = $(addprefix syncevolution-evolution-, 2.6 2.8 2.12)
 #
 # --replaces is necessary for migrating from syncevolution-evolution-<evover>
 # to syncevolution-evolution (as per http://wiki.debian.org/Renaming_a_Package)
+#
+# When we build shared objects, then conflict with the corresponding
+# system libs. The assumption is that the system library is named
+# after the lib and its major version, which holds for libsmltk and
+# libsynthesis in Debian.
 deb rpm : dist/$(distdir) dist/debian/control description-pak
+       conflicts=`ls -1 dist/$(distdir)/usr/lib/*.so.[0123456789] | sed -e 's;.*/;;' -e 's/\.so\.//' -e 's/$$/, /'` && \
        tmpdir=`mktemp -d $$HOME/syncevolution.XXXXXXXXXX` && \
        trap "rm -rf $$tmpdir" EXIT && \
        fakeroot checkinstall </dev/null \
@@ -113,7 +119,7 @@ deb rpm : dist/$(distdir) dist/debian/control description-pak
                --pkgarch=$(PKGARCH) \
                --provides=syncevolution \
                --replaces="'syncevolution, `echo $(filter-out $(PKGNAME), $(PKGS)) | sed -e 's/  */ (<< 1:0.8.1-2), /g'` (<< 1:0.8.1-2)'" \
-               --conflicts="'syncevolution, `echo $(filter-out $(PKGNAME), $(PKGS)) | sed -e 's/  */ (<< 1:0.8.1-2), /g'` (<< 1:0.8.1-2)'" \
+               --conflicts="'$${conflicts}syncevolution, `echo $(filter-out $(PKGNAME), $(PKGS)) | sed -e 's/  */ (<< 1:0.8.1-2), /g'` (<< 1:0.8.1-2)'" \
                --maintainer="'Patrick Ohly <patrick.ohly@gmx.de>'" \
                --pkgsource='http://sourceforge.net/project/showfiles.php?group_id=146288' \
                --pkgaltsource='http://www.estamos.de/projects/SyncML/' \
index 223bf81..40bce63 100644 (file)
@@ -140,11 +140,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -174,6 +179,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -193,6 +200,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
@@ -864,7 +873,13 @@ $(IPHONE_FILENAME) : all
 #
 # --replaces is necessary for migrating from syncevolution-evolution-<evover>
 # to syncevolution-evolution (as per http://wiki.debian.org/Renaming_a_Package)
+#
+# When we build shared objects, then conflict with the corresponding
+# system libs. The assumption is that the system library is named
+# after the lib and its major version, which holds for libsmltk and
+# libsynthesis in Debian.
 deb rpm : dist/$(distdir) dist/debian/control description-pak
+       conflicts=`ls -1 dist/$(distdir)/usr/lib/*.so.[0123456789] | sed -e 's;.*/;;' -e 's/\.so\.//' -e 's/$$/, /'` && \
        tmpdir=`mktemp -d $$HOME/syncevolution.XXXXXXXXXX` && \
        trap "rm -rf $$tmpdir" EXIT && \
        fakeroot checkinstall </dev/null \
@@ -878,7 +893,7 @@ deb rpm : dist/$(distdir) dist/debian/control description-pak
                --pkgarch=$(PKGARCH) \
                --provides=syncevolution \
                --replaces="'syncevolution, `echo $(filter-out $(PKGNAME), $(PKGS)) | sed -e 's/  */ (<< 1:0.8.1-2), /g'` (<< 1:0.8.1-2)'" \
-               --conflicts="'syncevolution, `echo $(filter-out $(PKGNAME), $(PKGS)) | sed -e 's/  */ (<< 1:0.8.1-2), /g'` (<< 1:0.8.1-2)'" \
+               --conflicts="'$${conflicts}syncevolution, `echo $(filter-out $(PKGNAME), $(PKGS)) | sed -e 's/  */ (<< 1:0.8.1-2), /g'` (<< 1:0.8.1-2)'" \
                --maintainer="'Patrick Ohly <patrick.ohly@gmx.de>'" \
                --pkgsource='http://sourceforge.net/project/showfiles.php?group_id=146288' \
                --pkgaltsource='http://www.estamos.de/projects/SyncML/' \
diff --git a/NEWS b/NEWS
index 3be8b44..8052d7f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,130 @@
+SyncEvolution 1.0 beta 1 -> beta 2, 23.02.2010
+==============================================
+
+Several new features and some bug fixing. Despite some open issues
+(see below), this release is ready for getting packaged in staging
+areas of distros as replacement for 0.9.2.
+
+As before, documentation for 1.0 is only available in the
+"Development" section of syncevolution.org, including HOWTOs for
+setting up the HTTP SyncML server and phones manually.
+
+Setting up a phone became a bit easier with beta 2, because
+SyncEvolution is now integrated with the GNOME Bluetooth panel: once a
+device with SyncML client support is paired, a button offers to bring
+up the sync-UI and configure or synchronize with that device. We do a
+fuzzy match against the Bluetooth device name to find a suitable
+template (not manufacturer/model, because that is not readily
+available). Still not many (read: hardly any) templates available,
+though.
+
+The binaries on syncevolution.org are compiled with Bluetooth support.
+libbluetooth2 or libbluetooth3 should be installed, but are not
+essential. If there is no suitable version of it, the Bluetooth
+channel has to be selected manually as part of the syncURL.
+
+Unexpected slow syncs are prevented by default, in contrast to beta 1
+where this feature was available but turned off. When an unexpected
+slow sync is detected in a client, users have to follow the
+instructions provided by the command line or sync-ui and choose how to
+proceed (explicitly request slow sync, refresh from server or client,
+restore from backup). SyncEvolution as server currently cannot prevent
+slow syncs, even when initiating the sync with a phone.
+
+In preparation for syncing automatically, logdir and database handling
+was improved considerably. Backups use less disk space because
+identical files share the same file content via hard links. This also
+speeds up the synccompare Perl script. Database dumps and the
+corresponding comparison are delayed until the session really runs,
+which avoids doing needless work a) when the server a client tries to
+contact is unreachable or down and b) by only including sources that
+are really in use during a sync on the server side.
+
+The Synthesis XML configuration was split up into different parts
+which are assembled from /usr/share/syncevolution/xml. Files in
+~/.config/syncevolution-xml override and extend the default files,
+which my be useful when adding support for a new phone.
+
+Summary of changes since 1.0 beta 1:
+
+* sync-ui: recovery dialog (MB #8050), device setup, config usable with
+  long strings (MB #9278), fixed displaying of source phases during sync
+  (MB #9320)
+* sync-ui + syncevo-dbus-server: integration with Bluez to detect paired
+  devices (MB #9216, MB #7089), select template based on device name (MB #7838),
+  detect network and Bluetooth connectivity (only with ConnMan, MB #7700),
+  passwords stored in GNOME keyring by syncevo-dbus-server are shown with
+  dots in sync-ui (MB #9169)
+* Evolution addressbook backend: avoid picking CouchDB, second try (MB #7877)
+* Evolution calendar backend: minor fix for change tracking when deleting
+  a single instance of a recurring event
+* build fixes: Bluetooth compatibility (MB #9289), use libical _r variant
+  of calls because 0.43 has issues in the normal version, conflict with
+  system libsynthesis and libsmltk (MB #9811)
+* Horde: avoid confusing the server with a deviceId that starts like the
+  ones used in old Funambol clients, helps with calendar sync (MB #9347)
+* better reporting when SyncEvolution dies during a sync (only happend once
+  when it wasn't installed properly, but still... MB #9844)
+* performance improvements: synccompare much faster/database dumps consume
+  less disk space/more intelligent about expiring obsolete session directories
+  and backups/database accesses are reduced in several backends (MB #7708),
+  shorter logs (MB #8092)
+* slow sync detection: now also works in the case where the client detects
+  an anchor mismatch and enabled by default (MB #2416)
+* OBEX transport: some error handling changes and removal of polling, now
+  also possible via sync-ui + syncevo-dbus-server (MB #9436)
+* API changes: SyncSource introduces an "isEmpty" operation which is
+  needed for the slow sync detection
+* SyncML: split up configuration (MB #7712), increased default message size
+  because the old one might have been too small for large DevInf structures
+* several fixes for virtual data sources ("calendar+todo"): now works
+  on client side, fixed naming on server (MB #9664), fixed error message
+  for slow sync detection, supported in combination with sync-UI (MB #9535)
+* fixes for shared configuration layout: finding sessions of peers in
+  non-default context, adding sources affected peers in the same context
+  (MB #9329), wrong context during --configure when using shortcut for peers
+  in non-default context (MB #9338)
+
+
+Known gaps for 1.0 final and beyond:
+
+Redesigned and reimplemented D-Bus API, required by sync-UI:
+- 'syncevolution' command line tool bypasses D-Bus server and runs
+  sync sessions itself (MB #5043)
+- availability of peers not detected when using NetworkManager
+  (connected for HTTP, paired for Bluetooth; MB #7700)
+
+SyncML server in general:
+- suspend/resume support is untested (MB #2425)
+- the progress events and statistics reported for a SyncML client
+  are not generated when running as SyncML server, will require
+  a fair amount of refactoring in the Synthesis engine (MB #7709)
+
+HTTP SyncML server:
+- a configuration must be created for each peer manually, including
+  a remoteDeviceId value that contains the peer's SyncML device ID
+  (MB #7838)
+
+OBEX SyncML server ("sync with phones"):
+- does not support phones which require a SAN 1.0 message (MB #9312)
+- determining a working configuration for an unknown phone requires
+  a bit of experimenting, which should be automated (MB #9862)
+
+OBEX SyncML client:
+- parsing of SAN message is rudimentary and depends on an existing local
+  configuration, needs to be refined depending on which SyncML server software
+  it is meant to work with (MB #6175)
+
+Automatic sync (MB #6378):
+- no support for the various server push notification mechanisms
+- no intelligent detection of local changes
+- no regular background sync, development is in progress
+
+Upgrading from 1.0 beta 1: moving back and forth should work seamlessly
+Upgrading from 0.9.x: see under beta 1
+
+
 SyncEvolution 0.9.2 -> 1.0 beta 1, 26.01.2010
 ==============================================
 
diff --git a/README b/README
index 6594f0a..7d5b8e3 100644 (file)
--- a/README
+++ b/README
@@ -774,6 +774,9 @@ Optional (enables reading proxy settings from GNOME preferences):
 Optional (enables direct sync with phones):
    apt-get install libopenobex-dev libbluetooth-dev
 
+Optional (only used for SHA-256 when glib is not already a dependency):
+   apt-get install libnss3-dev
+
 For compiling libsynthesis:
    apt-get install libpcre3-dev libsqlite3-dev libexpat-dev libz-dev
 
@@ -795,6 +798,12 @@ Optional packages for GUI:
 
 libunique = ensure that GTK GUI only runs once per user
 
+Optional packages for GNOME Bluetooth Panel plugin:
+   apt-get install libgnome-bluetooth-dev
+
+The plugin adds a button to invoke sync-UI after a device
+was paired which supports SyncML.
+
 The build system is the normal autotools system. See INSTALL for
 general instructions how to use that and "./configure --help" for
 SyncEvolution specific options.
index 8bb1c8f..2f7a639 100644 (file)
 /* Define if your <locale.h> file defines LC_MESSAGES. */
 #undef HAVE_LC_MESSAGES
 
+/* have recent enough libical with _r variants */
+#undef HAVE_LIBICAL_R
+
 /* enable GNOME specific libsoup */
 #undef HAVE_LIBSOUP_SOUP_GNOME_FEATURES_H
 
 /* Use Moblin UI widgets */
 #undef USE_MOBLIN_UX
 
+/* choose implementation of SHA-256 */
+#undef USE_SHA256
+
 /* Version number of package */
 #undef VERSION
index 9719bfc..53a8188 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.61 for syncevolution 1.0beta1.
+# Generated by GNU Autoconf 2.61 for syncevolution 1.0beta2a.
 #
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
 # 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
@@ -726,8 +726,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
 # Identity of this package.
 PACKAGE_NAME='syncevolution'
 PACKAGE_TARNAME='syncevolution'
-PACKAGE_VERSION='1.0beta1'
-PACKAGE_STRING='syncevolution 1.0beta1'
+PACKAGE_VERSION='1.0beta2a'
+PACKAGE_STRING='syncevolution 1.0beta2a'
 PACKAGE_BUGREPORT=''
 
 # Factoring default headers for most tests.
@@ -955,6 +955,11 @@ SYNTHESISSRC
 SYNCSOURCES
 BACKEND_DEFINES
 BACKEND_CPPFLAGS
+GNOMEBLUETOOTH_CFLAGS
+GNOMEBLUETOOTH_LIBS
+GNOMEBLUETOOTH_DIR
+ENABLE_GNOME_BLUETOOTH_PANEL_TRUE
+ENABLE_GNOME_BLUETOOTH_PANEL_FALSE
 ADDRESSBOOK_CFLAGS
 ADDRESSBOOK_LIBS
 EPACKAGE_CFLAGS
@@ -965,6 +970,8 @@ EBOOK_CFLAGS
 EBOOK_LIBS
 ENABLE_ECAL_TRUE
 ENABLE_ECAL_FALSE
+LIBICAL_AVAILABLE_CFLAGS
+LIBICAL_AVAILABLE_LIBS
 FILE_CFLAGS
 FILE_LIBS
 MCALB_CFLAGS
@@ -999,6 +1006,10 @@ GTHREAD_CFLAGS
 GTHREAD_LIBS
 GOBJECT_CFLAGS
 GOBJECT_LIBS
+GLIB216_CFLAGS
+GLIB216_LIBS
+NSS_CFLAGS
+NSS_LIBS
 ENABLE_MODULES_TRUE
 ENABLE_MODULES_FALSE
 SYNCEVOLUTION_LDADD
@@ -1045,12 +1056,16 @@ CXXFLAGS
 CCC
 SYNTHESIS_CFLAGS
 SYNTHESIS_LIBS
+GNOMEBLUETOOTH_CFLAGS
+GNOMEBLUETOOTH_LIBS
 EPACKAGE_CFLAGS
 EPACKAGE_LIBS
 ECAL_CFLAGS
 ECAL_LIBS
 EBOOK_CFLAGS
 EBOOK_LIBS
+LIBICAL_AVAILABLE_CFLAGS
+LIBICAL_AVAILABLE_LIBS
 MCALB_CFLAGS
 MCALB_LIBS
 SQLITE_CFLAGS
@@ -1063,7 +1078,11 @@ GLIB_LIBS
 GTHREAD_CFLAGS
 GTHREAD_LIBS
 GOBJECT_CFLAGS
-GOBJECT_LIBS'
+GOBJECT_LIBS
+GLIB216_CFLAGS
+GLIB216_LIBS
+NSS_CFLAGS
+NSS_LIBS'
 
 
 # Initialize some variables set by options.
@@ -1566,7 +1585,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures syncevolution 1.0beta1 to adapt to many kinds of systems.
+\`configure' configures syncevolution 1.0beta2a to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1636,7 +1655,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of syncevolution 1.0beta1:";;
+     short | recursive ) echo "Configuration of syncevolution 1.0beta2a:";;
    esac
   cat <<\_ACEOF
 
@@ -1684,6 +1703,9 @@ Optional Features:
   --enable-dbus-service   enables building the dbus service executable and the
                           wrapper library for it
   --disable-nls           do not use Native Language Support
+  --enable-gnome-bluetooth-panel-plugin
+                          GNOME Bluetooth panel plugin adding a "sync" button
+                          for paired devices (off by default)
 --enable-addressbook    enable access to Mac OS X address book (default off)
 --disable-ebook         disable access to Evolution addressbooks (must be
                           used to compile without it)
@@ -1804,6 +1826,10 @@ Some influential environment variables:
               C compiler flags for SYNTHESIS, overriding pkg-config
   SYNTHESIS_LIBS
               linker flags for SYNTHESIS, overriding pkg-config
+  GNOMEBLUETOOTH_CFLAGS
+              C compiler flags for GNOMEBLUETOOTH, overriding pkg-config
+  GNOMEBLUETOOTH_LIBS
+              linker flags for GNOMEBLUETOOTH, overriding pkg-config
   EPACKAGE_CFLAGS
               C compiler flags for EPACKAGE, overriding pkg-config
   EPACKAGE_LIBS
@@ -1813,6 +1839,10 @@ Some influential environment variables:
   EBOOK_CFLAGS
               C compiler flags for EBOOK, overriding pkg-config
   EBOOK_LIBS  linker flags for EBOOK, overriding pkg-config
+  LIBICAL_AVAILABLE_CFLAGS
+              C compiler flags for LIBICAL_AVAILABLE, overriding pkg-config
+  LIBICAL_AVAILABLE_LIBS
+              linker flags for LIBICAL_AVAILABLE, overriding pkg-config
   MCALB_CFLAGS
               C compiler flags for MCALB, overriding pkg-config
   MCALB_LIBS  linker flags for MCALB, overriding pkg-config
@@ -1832,6 +1862,12 @@ Some influential environment variables:
               C compiler flags for GOBJECT, overriding pkg-config
   GOBJECT_LIBS
               linker flags for GOBJECT, overriding pkg-config
+  GLIB216_CFLAGS
+              C compiler flags for GLIB216, overriding pkg-config
+  GLIB216_LIBS
+              linker flags for GLIB216, overriding pkg-config
+  NSS_CFLAGS  C compiler flags for NSS, overriding pkg-config
+  NSS_LIBS    linker flags for NSS, overriding pkg-config
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1896,7 +1932,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-syncevolution configure 1.0beta1
+syncevolution configure 1.0beta2a
 generated by GNU Autoconf 2.61
 
 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1910,7 +1946,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by syncevolution $as_me 1.0beta1, which was
+It was created by syncevolution $as_me 1.0beta2a, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   $ $0 $@
@@ -2603,7 +2639,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='syncevolution'
- VERSION='1.0beta1'
+ VERSION='1.0beta2a'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -5619,109 +5655,6 @@ else
 fi
 
 
-if test "$have_bluetooth"; then
-   ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-   CFLAGS_old="$CFLAGS"
-   CFLAGS="$CPPFLAGS $BLUEZ_CFLAGS"
-   # test in this order:
-   # - recent libbluetooth (no _safe variant, base function has bufsize)
-   # - intermediate with _safe
-   # - else assume old-style (no bufsize, no _safe)
-   #
-   # The source code checks the signature both by via pointer assignment and calling
-   # it (better safe than sorry). One these should fail if the signature is not right.
-   cat >conftest.$ac_ext <<_ACEOF
-#include <bluetooth/sdp.h>
-                      #include <bluetooth/sdp_lib.h>
-                      sdp_record_t *(*extract_pdu)(const uint8_t *pdata, int bufsize, int *scanned) =
-                           sdp_extract_pdu;
-                      void foo(void) {
-                          uint8_t *pdata = NULL;
-                          int scanned;
-                          sdp_extract_pdu(pdata, 100, &scanned);
-                      }
-
-_ACEOF
-rm -f conftest.$ac_objext
-if { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_compile") 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } && {
-        test -z "$ac_c_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; then
-
-cat >>confdefs.h <<\_ACEOF
-#define HAVE_BLUEZ_BUFSIZE 1
-_ACEOF
-
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-       cat >conftest.$ac_ext <<_ACEOF
-#include <bluetooth/sdp.h>
-                                        #include <bluetooth/sdp_lib.h>
-                                        sdp_record_t *(*extract_pdu)(const uint8_t *pdata, int bufsize, int *scanned) =
-                                               sdp_extract_pdu_safe;
-                                        void foo(void) {
-                                            uint8_t *pdata = NULL;
-                                            int scanned;
-                                            sdp_extract_pdu_safe(pdata, 100, &scanned);
-                                        }
-
-_ACEOF
-rm -f conftest.$ac_objext
-if { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_compile") 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } && {
-        test -z "$ac_c_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; then
-
-cat >>confdefs.h <<\_ACEOF
-#define HAVE_BLUEZ_SAFE 1
-_ACEOF
-
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-
-fi
-
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-   CFLAGS="$CFLAGS_old"
-fi
-
-
 if test ! "$TRANSPORT" &&
    test "$libsoup_disabled" != "yes" &&
    test "$libcurl_disabled" != "yes" &&
@@ -6393,13 +6326,15 @@ esac
 if test $enable_gui != "no"; then
     gui_modules="glib-2.0 dbus-glib-1 >= 0.60 gtk+-2.0 libglade-2.0 gio-2.0"
     if test $enable_gui == "moblin"; then
-        gui_modules="$guimodules mx-gtk-1.0"
 
 cat >>confdefs.h <<\_ACEOF
 #define USE_MOBLIN_UX 1
 _ACEOF
 
     fi
+    if test $enable_gui == "moblin" -o $enable_gui == "all"; then
+        gui_modules="$guimodules mx-gtk-1.0"
+    fi
 
 
 pkg_failed=no
@@ -10079,6 +10014,109 @@ BACKENDS=""
 BACKEND_CPPFLAGS="$SYNTHESIS_CFLAGS $EPACKAGE_CFLAGS $EBOOK_CFLAGS $ECAL_CFLAGS $GLIB_CFLAGS $BOOST_CPPFLAGS"
 
 
+# GNOME Bluetooth Panel plugin
+
+pkg_failed=no
+{ echo "$as_me:$LINENO: checking for GNOMEBLUETOOTH" >&5
+echo $ECHO_N "checking for GNOMEBLUETOOTH... $ECHO_C" >&6; }
+
+if test -n "$PKG_CONFIG"; then
+    if test -n "$GNOMEBLUETOOTH_CFLAGS"; then
+        pkg_cv_GNOMEBLUETOOTH_CFLAGS="$GNOMEBLUETOOTH_CFLAGS"
+    else
+        if test -n "$PKG_CONFIG" && \
+    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"gnome-bluetooth-1.0 >= 2.27.6\"") >&5
+  ($PKG_CONFIG --exists --print-errors "gnome-bluetooth-1.0 >= 2.27.6") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_GNOMEBLUETOOTH_CFLAGS=`$PKG_CONFIG --cflags "gnome-bluetooth-1.0 >= 2.27.6" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+    fi
+else
+       pkg_failed=untried
+fi
+if test -n "$PKG_CONFIG"; then
+    if test -n "$GNOMEBLUETOOTH_LIBS"; then
+        pkg_cv_GNOMEBLUETOOTH_LIBS="$GNOMEBLUETOOTH_LIBS"
+    else
+        if test -n "$PKG_CONFIG" && \
+    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"gnome-bluetooth-1.0 >= 2.27.6\"") >&5
+  ($PKG_CONFIG --exists --print-errors "gnome-bluetooth-1.0 >= 2.27.6") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_GNOMEBLUETOOTH_LIBS=`$PKG_CONFIG --libs "gnome-bluetooth-1.0 >= 2.27.6" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+    fi
+else
+       pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               GNOMEBLUETOOTH_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "gnome-bluetooth-1.0 >= 2.27.6"`
+        else
+               GNOMEBLUETOOTH_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "gnome-bluetooth-1.0 >= 2.27.6"`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$GNOMEBLUETOOTH_PKG_ERRORS" >&5
+
+       { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+                have_gbt="no"
+elif test $pkg_failed = untried; then
+       have_gbt="no"
+else
+       GNOMEBLUETOOTH_CFLAGS=$pkg_cv_GNOMEBLUETOOTH_CFLAGS
+       GNOMEBLUETOOTH_LIBS=$pkg_cv_GNOMEBLUETOOTH_LIBS
+        { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+       have_gbt="yes"
+                   GNOMEBLUETOOTH_DIR=`$PKG_CONFIG --variable=libdir gnome-bluetooth-1.0 2>/dev/null`/gnome-bluetooth
+fi
+
+
+
+# Check whether --enable-gnome-bluetooth-panel-plugin was given.
+if test "${enable_gnome_bluetooth_panel_plugin+set}" = set; then
+  enableval=$enable_gnome_bluetooth_panel_plugin; enable_gnome_bluetooth_panel="$enableval"
+else
+  enable_gnome_bluetooth_panel="no"
+
+fi
+
+if test "$enable_gnome_bluetooth_panel" = "yes"; then
+   test "$have_gbt" = "yes" || { { echo "$as_me:$LINENO: error: --enable-gnome-bluetooth-panel requires
+           pkg-config information for gnome-bluetooth-1.0 >= 2.27.6 which was not found" >&5
+echo "$as_me: error: --enable-gnome-bluetooth-panel requires
+           pkg-config information for gnome-bluetooth-1.0 >= 2.27.6 which was not found" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+ac_config_files="$ac_config_files src/gnome-bluetooth/Makefile"
+
+ if test "$have_gbt" = "yes" && test "$enable_gnome_bluetooth_panel" = "yes"; then
+  ENABLE_GNOME_BLUETOOTH_PANEL_TRUE=
+  ENABLE_GNOME_BLUETOOTH_PANEL_FALSE='#'
+else
+  ENABLE_GNOME_BLUETOOTH_PANEL_TRUE='#'
+  ENABLE_GNOME_BLUETOOTH_PANEL_FALSE=
+fi
+
+
+
 # vvvvvvvvvvvvvv src/backends/addressbook/configure-sub.in vvvvvvvvvvvvvv
 
 ADDRESSBOOK_CFLAGS=
@@ -10871,6 +10909,82 @@ else
         ECAL_LIBS=
 fi
 
+
+pkg_failed=no
+{ echo "$as_me:$LINENO: checking for LIBICAL_AVAILABLE" >&5
+echo $ECHO_N "checking for LIBICAL_AVAILABLE... $ECHO_C" >&6; }
+
+if test -n "$PKG_CONFIG"; then
+    if test -n "$LIBICAL_AVAILABLE_CFLAGS"; then
+        pkg_cv_LIBICAL_AVAILABLE_CFLAGS="$LIBICAL_AVAILABLE_CFLAGS"
+    else
+        if test -n "$PKG_CONFIG" && \
+    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libical >= 0.43\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libical >= 0.43") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_LIBICAL_AVAILABLE_CFLAGS=`$PKG_CONFIG --cflags "libical >= 0.43" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+    fi
+else
+       pkg_failed=untried
+fi
+if test -n "$PKG_CONFIG"; then
+    if test -n "$LIBICAL_AVAILABLE_LIBS"; then
+        pkg_cv_LIBICAL_AVAILABLE_LIBS="$LIBICAL_AVAILABLE_LIBS"
+    else
+        if test -n "$PKG_CONFIG" && \
+    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libical >= 0.43\"") >&5
+  ($PKG_CONFIG --exists --print-errors "libical >= 0.43") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_LIBICAL_AVAILABLE_LIBS=`$PKG_CONFIG --libs "libical >= 0.43" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+    fi
+else
+       pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               LIBICAL_AVAILABLE_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "libical >= 0.43"`
+        else
+               LIBICAL_AVAILABLE_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "libical >= 0.43"`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$LIBICAL_AVAILABLE_PKG_ERRORS" >&5
+
+       { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+                pass
+elif test $pkg_failed = untried; then
+       pass
+else
+       LIBICAL_AVAILABLE_CFLAGS=$pkg_cv_LIBICAL_AVAILABLE_CFLAGS
+       LIBICAL_AVAILABLE_LIBS=$pkg_cv_LIBICAL_AVAILABLE_LIBS
+        { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_LIBICAL_R 1
+_ACEOF
+
+fi
+
 if test "$enable_evo" = "yes"; then
         need_glib="yes"
         if test "$EDSFOUND" = "yes"; then
@@ -12327,7 +12441,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 12330 "configure"' > conftest.$ac_ext
+  echo '#line 12444 "configure"' > conftest.$ac_ext
   if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -14432,11 +14546,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:14435: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:14549: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:14439: \$? = $ac_status" >&5
+   echo "$as_me:14553: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -14722,11 +14836,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:14725: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:14839: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:14729: \$? = $ac_status" >&5
+   echo "$as_me:14843: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -14826,11 +14940,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:14829: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:14943: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:14833: \$? = $ac_status" >&5
+   echo "$as_me:14947: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -17203,7 +17317,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 17206 "configure"
+#line 17320 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -17303,7 +17417,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 17306 "configure"
+#line 17420 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -19704,11 +19818,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:19707: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:19821: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:19711: \$? = $ac_status" >&5
+   echo "$as_me:19825: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -19808,11 +19922,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:19811: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:19925: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:19815: \$? = $ac_status" >&5
+   echo "$as_me:19929: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -21406,11 +21520,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:21409: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:21523: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:21413: \$? = $ac_status" >&5
+   echo "$as_me:21527: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -21510,11 +21624,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:21513: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:21627: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:21517: \$? = $ac_status" >&5
+   echo "$as_me:21631: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -23730,11 +23844,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:23733: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:23847: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:23737: \$? = $ac_status" >&5
+   echo "$as_me:23851: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -24020,11 +24134,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:24023: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:24137: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:24027: \$? = $ac_status" >&5
+   echo "$as_me:24141: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -24124,11 +24238,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:24127: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:24241: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:24131: \$? = $ac_status" >&5
+   echo "$as_me:24245: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -27085,6 +27199,208 @@ echo "$as_me: error: not all GNOME libraries found" >&2;}
         BACKEND_CPPFLAGS="$BACKEND_CPPFLAGS $GLIB_CFLAGS $GTHREAD_CFLAGS $GOBJECT_CFLAGS"
 fi
 
+# determine from where we can get a SHA-256 implementation
+have_sha="no"
+if test "$GLIBFOUND" = "yes"; then
+   # only use glib if we need it anyway, also has to be at lease 2.16
+
+pkg_failed=no
+{ echo "$as_me:$LINENO: checking for GLIB216" >&5
+echo $ECHO_N "checking for GLIB216... $ECHO_C" >&6; }
+
+if test -n "$PKG_CONFIG"; then
+    if test -n "$GLIB216_CFLAGS"; then
+        pkg_cv_GLIB216_CFLAGS="$GLIB216_CFLAGS"
+    else
+        if test -n "$PKG_CONFIG" && \
+    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.16\"") >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.16") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_GLIB216_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0 >= 2.16" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+    fi
+else
+       pkg_failed=untried
+fi
+if test -n "$PKG_CONFIG"; then
+    if test -n "$GLIB216_LIBS"; then
+        pkg_cv_GLIB216_LIBS="$GLIB216_LIBS"
+    else
+        if test -n "$PKG_CONFIG" && \
+    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0 >= 2.16\"") >&5
+  ($PKG_CONFIG --exists --print-errors "glib-2.0 >= 2.16") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_GLIB216_LIBS=`$PKG_CONFIG --libs "glib-2.0 >= 2.16" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+    fi
+else
+       pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               GLIB216_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "glib-2.0 >= 2.16"`
+        else
+               GLIB216_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "glib-2.0 >= 2.16"`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$GLIB216_PKG_ERRORS" >&5
+
+       { echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6; }
+                true
+elif test $pkg_failed = untried; then
+       true
+else
+       GLIB216_CFLAGS=$pkg_cv_GLIB216_CFLAGS
+       GLIB216_LIBS=$pkg_cv_GLIB216_LIBS
+        { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define USE_SHA256 1
+_ACEOF
+
+                      have_sha="glib"
+fi
+fi
+if test "$have_sha" = "no"; then
+   # Fallback is Mozilla NSS. In contrast to libgcrypt it has a .pc
+   # file and a simple API.
+
+pkg_failed=no
+{ echo "$as_me:$LINENO: checking for NSS" >&5
+echo $ECHO_N "checking for NSS... $ECHO_C" >&6; }
+
+if test -n "$PKG_CONFIG"; then
+    if test -n "$NSS_CFLAGS"; then
+        pkg_cv_NSS_CFLAGS="$NSS_CFLAGS"
+    else
+        if test -n "$PKG_CONFIG" && \
+    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"\"nss\"\"") >&5
+  ($PKG_CONFIG --exists --print-errors ""nss"") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_NSS_CFLAGS=`$PKG_CONFIG --cflags ""nss"" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+    fi
+else
+       pkg_failed=untried
+fi
+if test -n "$PKG_CONFIG"; then
+    if test -n "$NSS_LIBS"; then
+        pkg_cv_NSS_LIBS="$NSS_LIBS"
+    else
+        if test -n "$PKG_CONFIG" && \
+    { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"\"nss\"\"") >&5
+  ($PKG_CONFIG --exists --print-errors ""nss"") 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  pkg_cv_NSS_LIBS=`$PKG_CONFIG --libs ""nss"" 2>/dev/null`
+else
+  pkg_failed=yes
+fi
+    fi
+else
+       pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               NSS_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors ""nss""`
+        else
+               NSS_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors ""nss""`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$NSS_PKG_ERRORS" >&5
+
+       { { echo "$as_me:$LINENO: error: Package requirements (\"nss\") were not met:
+
+$NSS_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables NSS_CFLAGS
+and NSS_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+" >&5
+echo "$as_me: error: Package requirements (\"nss\") were not met:
+
+$NSS_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables NSS_CFLAGS
+and NSS_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+" >&2;}
+   { (exit 1); exit 1; }; }
+elif test $pkg_failed = untried; then
+       { { echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables NSS_CFLAGS
+and NSS_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details." >&5
+echo "$as_me: error: The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables NSS_CFLAGS
+and NSS_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+else
+       NSS_CFLAGS=$pkg_cv_NSS_CFLAGS
+       NSS_LIBS=$pkg_cv_NSS_LIBS
+        { echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6; }
+
+cat >>confdefs.h <<\_ACEOF
+#define USE_SHA256 2
+_ACEOF
+
+                      have_sha="nss"
+fi
+fi
+
  if test "$enable_shared" == "yes"; then
   ENABLE_MODULES_TRUE=
   ENABLE_MODULES_FALSE='#'
@@ -27300,7 +27616,7 @@ if test ! "$docdir"; then
 
 fi
 
-ac_config_files="$ac_config_files Makefile src/dbus/interfaces/Makefile src/gdbus/Makefile src/dbus/Makefile src/Makefile src/syncevo/Makefile src/syncevo/syncevolution.pc src/gtk-ui/Makefile po/Makefile.in test/Makefile src/dbus/syncevo-dbus.pc"
+ac_config_files="$ac_config_files Makefile src/dbus/interfaces/Makefile src/gdbus/Makefile src/dbus/Makefile src/Makefile src/syncevo/Makefile src/syncevo/syncevolution.pc src/syncevo/configs/Makefile src/gtk-ui/Makefile po/Makefile.in test/Makefile src/dbus/syncevo-dbus.pc"
 
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
@@ -27465,6 +27781,13 @@ echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined.
 Usually this means the macro was only invoked conditionally." >&2;}
    { (exit 1); exit 1; }; }
 fi
+if test -z "${ENABLE_GNOME_BLUETOOTH_PANEL_TRUE}" && test -z "${ENABLE_GNOME_BLUETOOTH_PANEL_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"ENABLE_GNOME_BLUETOOTH_PANEL\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"ENABLE_GNOME_BLUETOOTH_PANEL\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
 if test -z "${ENABLE_ECAL_TRUE}" && test -z "${ENABLE_ECAL_FALSE}"; then
   { { echo "$as_me:$LINENO: error: conditional \"ENABLE_ECAL\" was never defined.
 Usually this means the macro was only invoked conditionally." >&5
@@ -27793,7 +28116,7 @@ exec 6>&1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by syncevolution $as_me 1.0beta1, which was
+This file was extended by syncevolution $as_me 1.0beta2a, which was
 generated by GNU Autoconf 2.61.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -27846,7 +28169,7 @@ Report bugs to <bug-autoconf@gnu.org>."
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF
 ac_cs_version="\\
-syncevolution config.status 1.0beta1
+syncevolution config.status 1.0beta2a
 configured by $0, generated by GNU Autoconf 2.61,
   with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
 
@@ -27964,6 +28287,7 @@ do
     "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
     "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
     "default-1") CONFIG_COMMANDS="$CONFIG_COMMANDS default-1" ;;
+    "src/gnome-bluetooth/Makefile") CONFIG_FILES="$CONFIG_FILES src/gnome-bluetooth/Makefile" ;;
     "src/backends/addressbook/Makefile") CONFIG_FILES="$CONFIG_FILES src/backends/addressbook/Makefile" ;;
     "src/backends/evolution/Makefile") CONFIG_FILES="$CONFIG_FILES src/backends/evolution/Makefile" ;;
     "src/backends/file/Makefile") CONFIG_FILES="$CONFIG_FILES src/backends/file/Makefile" ;;
@@ -27977,6 +28301,7 @@ do
     "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
     "src/syncevo/Makefile") CONFIG_FILES="$CONFIG_FILES src/syncevo/Makefile" ;;
     "src/syncevo/syncevolution.pc") CONFIG_FILES="$CONFIG_FILES src/syncevo/syncevolution.pc" ;;
+    "src/syncevo/configs/Makefile") CONFIG_FILES="$CONFIG_FILES src/syncevo/configs/Makefile" ;;
     "src/gtk-ui/Makefile") CONFIG_FILES="$CONFIG_FILES src/gtk-ui/Makefile" ;;
     "po/Makefile.in") CONFIG_FILES="$CONFIG_FILES po/Makefile.in" ;;
     "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
@@ -28274,11 +28599,11 @@ SYNTHESISSRC!$SYNTHESISSRC$ac_delim
 SYNCSOURCES!$SYNCSOURCES$ac_delim
 BACKEND_DEFINES!$BACKEND_DEFINES$ac_delim
 BACKEND_CPPFLAGS!$BACKEND_CPPFLAGS$ac_delim
-ADDRESSBOOK_CFLAGS!$ADDRESSBOOK_CFLAGS$ac_delim
-ADDRESSBOOK_LIBS!$ADDRESSBOOK_LIBS$ac_delim
-EPACKAGE_CFLAGS!$EPACKAGE_CFLAGS$ac_delim
-EPACKAGE_LIBS!$EPACKAGE_LIBS$ac_delim
-ECAL_CFLAGS!$ECAL_CFLAGS$ac_delim
+GNOMEBLUETOOTH_CFLAGS!$GNOMEBLUETOOTH_CFLAGS$ac_delim
+GNOMEBLUETOOTH_LIBS!$GNOMEBLUETOOTH_LIBS$ac_delim
+GNOMEBLUETOOTH_DIR!$GNOMEBLUETOOTH_DIR$ac_delim
+ENABLE_GNOME_BLUETOOTH_PANEL_TRUE!$ENABLE_GNOME_BLUETOOTH_PANEL_TRUE$ac_delim
+ENABLE_GNOME_BLUETOOTH_PANEL_FALSE!$ENABLE_GNOME_BLUETOOTH_PANEL_FALSE$ac_delim
 _ACEOF
 
   if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
@@ -28320,11 +28645,18 @@ _ACEOF
 ac_delim='%!_!# '
 for ac_last_try in false false false false false :; do
   cat >conf$$subs.sed <<_ACEOF
+ADDRESSBOOK_CFLAGS!$ADDRESSBOOK_CFLAGS$ac_delim
+ADDRESSBOOK_LIBS!$ADDRESSBOOK_LIBS$ac_delim
+EPACKAGE_CFLAGS!$EPACKAGE_CFLAGS$ac_delim
+EPACKAGE_LIBS!$EPACKAGE_LIBS$ac_delim
+ECAL_CFLAGS!$ECAL_CFLAGS$ac_delim
 ECAL_LIBS!$ECAL_LIBS$ac_delim
 EBOOK_CFLAGS!$EBOOK_CFLAGS$ac_delim
 EBOOK_LIBS!$EBOOK_LIBS$ac_delim
 ENABLE_ECAL_TRUE!$ENABLE_ECAL_TRUE$ac_delim
 ENABLE_ECAL_FALSE!$ENABLE_ECAL_FALSE$ac_delim
+LIBICAL_AVAILABLE_CFLAGS!$LIBICAL_AVAILABLE_CFLAGS$ac_delim
+LIBICAL_AVAILABLE_LIBS!$LIBICAL_AVAILABLE_LIBS$ac_delim
 FILE_CFLAGS!$FILE_CFLAGS$ac_delim
 FILE_LIBS!$FILE_LIBS$ac_delim
 MCALB_CFLAGS!$MCALB_CFLAGS$ac_delim
@@ -28359,6 +28691,10 @@ GTHREAD_CFLAGS!$GTHREAD_CFLAGS$ac_delim
 GTHREAD_LIBS!$GTHREAD_LIBS$ac_delim
 GOBJECT_CFLAGS!$GOBJECT_CFLAGS$ac_delim
 GOBJECT_LIBS!$GOBJECT_LIBS$ac_delim
+GLIB216_CFLAGS!$GLIB216_CFLAGS$ac_delim
+GLIB216_LIBS!$GLIB216_LIBS$ac_delim
+NSS_CFLAGS!$NSS_CFLAGS$ac_delim
+NSS_LIBS!$NSS_LIBS$ac_delim
 ENABLE_MODULES_TRUE!$ENABLE_MODULES_TRUE$ac_delim
 ENABLE_MODULES_FALSE!$ENABLE_MODULES_FALSE$ac_delim
 SYNCEVOLUTION_LDADD!$SYNCEVOLUTION_LDADD$ac_delim
@@ -28371,7 +28707,7 @@ LIBOBJS!$LIBOBJS$ac_delim
 LTLIBOBJS!$LTLIBOBJS$ac_delim
 _ACEOF
 
-  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 49; then
+  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 60; then
     break
   elif $ac_last_try; then
     { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
@@ -29004,4 +29340,6 @@ done
 echo "DBus service: $enable_dbus_service"
 echo "UI (DBus client): $enable_gui"
 echo "Bluetooth transport: $have_bluetooth"
+echo "GNOME Bluetooth panel plugin: $enable_gnome_bluetooth_panel"
+echo "SHA-256: $have_sha"
 echo
index 888e4ac..79f9b13 100644 (file)
@@ -51,6 +51,23 @@ if test "$need_glib" = "yes"; then
         BACKEND_CPPFLAGS="$BACKEND_CPPFLAGS $GLIB_CFLAGS $GTHREAD_CFLAGS $GOBJECT_CFLAGS"
 fi
 
+# determine from where we can get a SHA-256 implementation
+have_sha="no"
+if test "$GLIBFOUND" = "yes"; then
+   # only use glib if we need it anyway, also has to be at lease 2.16
+   PKG_CHECK_MODULES(GLIB216, [glib-2.0 >= 2.16],
+                     [AC_DEFINE(USE_SHA256, 1, [choose implementation of SHA-256])
+                      have_sha="glib"],
+                     [true])
+fi
+if test "$have_sha" = "no"; then
+   # Fallback is Mozilla NSS. In contrast to libgcrypt it has a .pc
+   # file and a simple API.
+   PKG_CHECK_MODULES(NSS, "nss",
+                     [AC_DEFINE(USE_SHA256, 2, [choose implementation of SHA-256])
+                      have_sha="nss"])
+fi
+
 dnl figure out whether we link all code statically or as modules
 AM_CONDITIONAL([ENABLE_MODULES], [test "$enable_shared" == "yes"])
 if test "$enable_shared" == "yes"; then
@@ -104,7 +121,7 @@ if test ! "$docdir"; then
    AC_SUBST(docdir)
 fi
 
-AC_CONFIG_FILES(Makefile src/dbus/interfaces/Makefile src/gdbus/Makefile src/dbus/Makefile src/Makefile src/syncevo/Makefile src/syncevo/syncevolution.pc src/gtk-ui/Makefile po/Makefile.in test/Makefile src/dbus/syncevo-dbus.pc)
+AC_CONFIG_FILES(Makefile src/dbus/interfaces/Makefile src/gdbus/Makefile src/dbus/Makefile src/Makefile src/syncevo/Makefile src/syncevo/syncevolution.pc src/syncevo/configs/Makefile src/gtk-ui/Makefile po/Makefile.in test/Makefile src/dbus/syncevo-dbus.pc)
 AC_OUTPUT
 
 echo
@@ -115,4 +132,6 @@ done
 echo "DBus service: $enable_dbus_service"
 echo "UI (DBus client): $enable_gui"
 echo "Bluetooth transport: $have_bluetooth"
+echo "GNOME Bluetooth panel plugin: $enable_gnome_bluetooth_panel"
+echo "SHA-256: $have_sha"
 echo
index e24185c..1184a6d 100644 (file)
@@ -5,7 +5,7 @@ dnl Invoke autogen.sh to produce a configure script.
 # Debian packages. For prereleases (beta, alpha),
 # set it to something like "0.9.2+" and the AC_INIT
 # VERSION to 1.0beta1 to produce 0.9.2+1.0beta1.
-AC_INIT([syncevolution], [1.0beta1])
+AC_INIT([syncevolution], [1.0beta2a])
 STABLE_VERSION=0.9.2+
 AC_SUBST(STABLE_VERSION)
 
@@ -280,43 +280,6 @@ fi
 AM_CONDITIONAL([ENABLE_OBEX], [test "$have_obex" = "yes" && test "$enable_bluetooth" = "yes"])
 AM_CONDITIONAL([ENABLE_BLUETOOTH], [test "$have_bluetooth" = "yes" && test "$enable_bluetooth" = "yes"])
 
-if test "$have_bluetooth"; then
-   AC_LANG(C)
-   CFLAGS_old="$CFLAGS"
-   CFLAGS="$CPPFLAGS $BLUEZ_CFLAGS"
-   # test in this order:
-   # - recent libbluetooth (no _safe variant, base function has bufsize)
-   # - intermediate with _safe
-   # - else assume old-style (no bufsize, no _safe)
-   #
-   # The source code checks the signature both by via pointer assignment and calling
-   # it (better safe than sorry). One these should fail if the signature is not right.
-   AC_COMPILE_IFELSE([#include <bluetooth/sdp.h>
-                      #include <bluetooth/sdp_lib.h>
-                      sdp_record_t *(*extract_pdu)(const uint8_t *pdata, int bufsize, int *scanned) =
-                           sdp_extract_pdu;
-                      void foo(void) {
-                          uint8_t *pdata = NULL;
-                          int scanned;
-                          sdp_extract_pdu(pdata, 100, &scanned);
-                      }
-                     ],
-                     AC_DEFINE(HAVE_BLUEZ_BUFSIZE, 1, [base libbluetooth functions accept bufsize parameter]),
-                     AC_COMPILE_IFELSE([#include <bluetooth/sdp.h>
-                                        #include <bluetooth/sdp_lib.h>
-                                        sdp_record_t *(*extract_pdu)(const uint8_t *pdata, int bufsize, int *scanned) =
-                                               sdp_extract_pdu_safe;
-                                        void foo(void) {
-                                            uint8_t *pdata = NULL;
-                                            int scanned;        
-                                            sdp_extract_pdu_safe(pdata, 100, &scanned);
-                                        }
-                                       ],
-                                       AC_DEFINE(HAVE_BLUEZ_SAFE, 1, [libbluetooth has _safe variants])))
-   CFLAGS="$CFLAGS_old"
-fi
-
-
 if test ! "$TRANSPORT" &&
    test "$libsoup_disabled" != "yes" &&
    test "$libcurl_disabled" != "yes" &&
@@ -439,9 +402,11 @@ esac
 if test $enable_gui != "no"; then
     gui_modules="glib-2.0 dbus-glib-1 >= 0.60 gtk+-2.0 libglade-2.0 gio-2.0"
     if test $enable_gui == "moblin"; then
-        gui_modules="$guimodules mx-gtk-1.0"
         AC_DEFINE(USE_MOBLIN_UX, 1, [Use Moblin UI widgets])
     fi
+    if test $enable_gui == "moblin" -o $enable_gui == "all"; then
+        gui_modules="$guimodules mx-gtk-1.0"
+    fi
 
     PKG_CHECK_MODULES(UNIQUE, unique-1.0,
                       have_unique="yes",
@@ -630,4 +595,26 @@ AC_SUBST(BACKEND_DEFINES)
 BACKEND_CPPFLAGS="$SYNTHESIS_CFLAGS $EPACKAGE_CFLAGS $EBOOK_CFLAGS $ECAL_CFLAGS $GLIB_CFLAGS $BOOST_CPPFLAGS"
 AC_SUBST(BACKEND_CPPFLAGS)
 
+# GNOME Bluetooth Panel plugin
+PKG_CHECK_MODULES(GNOMEBLUETOOTH, [gnome-bluetooth-1.0 >= 2.27.6],
+                  [have_gbt="yes"
+                   GNOMEBLUETOOTH_DIR=`$PKG_CONFIG --variable=libdir gnome-bluetooth-1.0 2>/dev/null`/gnome-bluetooth],
+                   have_gbt="no")
+AC_SUBST(GNOMEBLUETOOTH_CFLAGS)
+AC_SUBST(GNOMEBLUETOOTH_DIR)
+
+AC_ARG_ENABLE(gnome-bluetooth-panel-plugin, 
+        AC_HELP_STRING([--enable-gnome-bluetooth-panel-plugin],
+                       [GNOME Bluetooth panel plugin adding a "sync" button for paired devices (off by default)]),
+                       [enable_gnome_bluetooth_panel="$enableval"],
+        [enable_gnome_bluetooth_panel="no"]
+        )
+if test "$enable_gnome_bluetooth_panel" = "yes"; then
+   test "$have_gbt" = "yes" || AC_MSG_ERROR([--enable-gnome-bluetooth-panel requires
+           pkg-config information for gnome-bluetooth-1.0 >= 2.27.6 which was not found])
+fi
+AC_CONFIG_FILES(src/gnome-bluetooth/Makefile)
+AM_CONDITIONAL([ENABLE_GNOME_BLUETOOTH_PANEL], [test "$have_gbt" = "yes" && test "$enable_gnome_bluetooth_panel" = "yes"])
+
+
 dnl src/backends/*/configure-sub.in and configure-post.in follow
index fb41afd..00f756e 100644 (file)
@@ -5,7 +5,7 @@ dnl Invoke autogen.sh to produce a configure script.
 # Debian packages. For prereleases (beta, alpha),
 # set it to something like "0.9.2+" and the AC_INIT
 # VERSION to 1.0beta1 to produce 0.9.2+1.0beta1.
-AC_INIT([syncevolution], [1.0beta1])
+AC_INIT([syncevolution], [1.0beta2a])
 STABLE_VERSION=0.9.2+
 AC_SUBST(STABLE_VERSION)
 
@@ -280,43 +280,6 @@ fi
 AM_CONDITIONAL([ENABLE_OBEX], [test "$have_obex" = "yes" && test "$enable_bluetooth" = "yes"])
 AM_CONDITIONAL([ENABLE_BLUETOOTH], [test "$have_bluetooth" = "yes" && test "$enable_bluetooth" = "yes"])
 
-if test "$have_bluetooth"; then
-   AC_LANG(C)
-   CFLAGS_old="$CFLAGS"
-   CFLAGS="$CPPFLAGS $BLUEZ_CFLAGS"
-   # test in this order:
-   # - recent libbluetooth (no _safe variant, base function has bufsize)
-   # - intermediate with _safe
-   # - else assume old-style (no bufsize, no _safe)
-   #
-   # The source code checks the signature both by via pointer assignment and calling
-   # it (better safe than sorry). One these should fail if the signature is not right.
-   AC_COMPILE_IFELSE([#include <bluetooth/sdp.h>
-                      #include <bluetooth/sdp_lib.h>
-                      sdp_record_t *(*extract_pdu)(const uint8_t *pdata, int bufsize, int *scanned) =
-                           sdp_extract_pdu;
-                      void foo(void) {
-                          uint8_t *pdata = NULL;
-                          int scanned;
-                          sdp_extract_pdu(pdata, 100, &scanned);
-                      }
-                     ],
-                     AC_DEFINE(HAVE_BLUEZ_BUFSIZE, 1, [base libbluetooth functions accept bufsize parameter]),
-                     AC_COMPILE_IFELSE([#include <bluetooth/sdp.h>
-                                        #include <bluetooth/sdp_lib.h>
-                                        sdp_record_t *(*extract_pdu)(const uint8_t *pdata, int bufsize, int *scanned) =
-                                               sdp_extract_pdu_safe;
-                                        void foo(void) {
-                                            uint8_t *pdata = NULL;
-                                            int scanned;        
-                                            sdp_extract_pdu_safe(pdata, 100, &scanned);
-                                        }
-                                       ],
-                                       AC_DEFINE(HAVE_BLUEZ_SAFE, 1, [libbluetooth has _safe variants])))
-   CFLAGS="$CFLAGS_old"
-fi
-
-
 if test ! "$TRANSPORT" &&
    test "$libsoup_disabled" != "yes" &&
    test "$libcurl_disabled" != "yes" &&
@@ -439,9 +402,11 @@ esac
 if test $enable_gui != "no"; then
     gui_modules="glib-2.0 dbus-glib-1 >= 0.60 gtk+-2.0 libglade-2.0 gio-2.0"
     if test $enable_gui == "moblin"; then
-        gui_modules="$guimodules mx-gtk-1.0"
         AC_DEFINE(USE_MOBLIN_UX, 1, [Use Moblin UI widgets])
     fi
+    if test $enable_gui == "moblin" -o $enable_gui == "all"; then
+        gui_modules="$guimodules mx-gtk-1.0"
+    fi
 
     PKG_CHECK_MODULES(UNIQUE, unique-1.0,
                       have_unique="yes",
@@ -630,6 +595,28 @@ AC_SUBST(BACKEND_DEFINES)
 BACKEND_CPPFLAGS="$SYNTHESIS_CFLAGS $EPACKAGE_CFLAGS $EBOOK_CFLAGS $ECAL_CFLAGS $GLIB_CFLAGS $BOOST_CPPFLAGS"
 AC_SUBST(BACKEND_CPPFLAGS)
 
+# GNOME Bluetooth Panel plugin
+PKG_CHECK_MODULES(GNOMEBLUETOOTH, [gnome-bluetooth-1.0 >= 2.27.6],
+                  [have_gbt="yes"
+                   GNOMEBLUETOOTH_DIR=`$PKG_CONFIG --variable=libdir gnome-bluetooth-1.0 2>/dev/null`/gnome-bluetooth],
+                   have_gbt="no")
+AC_SUBST(GNOMEBLUETOOTH_CFLAGS)
+AC_SUBST(GNOMEBLUETOOTH_DIR)
+
+AC_ARG_ENABLE(gnome-bluetooth-panel-plugin, 
+        AC_HELP_STRING([--enable-gnome-bluetooth-panel-plugin],
+                       [GNOME Bluetooth panel plugin adding a "sync" button for paired devices (off by default)]),
+                       [enable_gnome_bluetooth_panel="$enableval"],
+        [enable_gnome_bluetooth_panel="no"]
+        )
+if test "$enable_gnome_bluetooth_panel" = "yes"; then
+   test "$have_gbt" = "yes" || AC_MSG_ERROR([--enable-gnome-bluetooth-panel requires
+           pkg-config information for gnome-bluetooth-1.0 >= 2.27.6 which was not found])
+fi
+AC_CONFIG_FILES(src/gnome-bluetooth/Makefile)
+AM_CONDITIONAL([ENABLE_GNOME_BLUETOOTH_PANEL], [test "$have_gbt" = "yes" && test "$enable_gnome_bluetooth_panel" = "yes"])
+
+
 dnl src/backends/*/configure-sub.in and configure-post.in follow
 # vvvvvvvvvvvvvv src/backends/addressbook/configure-sub.in vvvvvvvvvvvvvv
 dnl -*- mode: Autoconf; -*-
@@ -742,6 +729,11 @@ else
         ECAL_LIBS=
 fi
 
+PKG_CHECK_MODULES(LIBICAL_AVAILABLE,
+                  libical >= 0.43,
+                  AC_DEFINE(HAVE_LIBICAL_R, 1, [have recent enough libical with _r variants]),
+                  pass)
+
 if test "$enable_evo" = "yes"; then
         need_glib="yes"
         if test "$EDSFOUND" = "yes"; then
@@ -946,6 +938,23 @@ if test "$need_glib" = "yes"; then
         BACKEND_CPPFLAGS="$BACKEND_CPPFLAGS $GLIB_CFLAGS $GTHREAD_CFLAGS $GOBJECT_CFLAGS"
 fi
 
+# determine from where we can get a SHA-256 implementation
+have_sha="no"
+if test "$GLIBFOUND" = "yes"; then
+   # only use glib if we need it anyway, also has to be at lease 2.16
+   PKG_CHECK_MODULES(GLIB216, [glib-2.0 >= 2.16],
+                     [AC_DEFINE(USE_SHA256, 1, [choose implementation of SHA-256])
+                      have_sha="glib"],
+                     [true])
+fi
+if test "$have_sha" = "no"; then
+   # Fallback is Mozilla NSS. In contrast to libgcrypt it has a .pc
+   # file and a simple API.
+   PKG_CHECK_MODULES(NSS, "nss",
+                     [AC_DEFINE(USE_SHA256, 2, [choose implementation of SHA-256])
+                      have_sha="nss"])
+fi
+
 dnl figure out whether we link all code statically or as modules
 AM_CONDITIONAL([ENABLE_MODULES], [test "$enable_shared" == "yes"])
 if test "$enable_shared" == "yes"; then
@@ -999,7 +1008,7 @@ if test ! "$docdir"; then
    AC_SUBST(docdir)
 fi
 
-AC_CONFIG_FILES(Makefile src/dbus/interfaces/Makefile src/gdbus/Makefile src/dbus/Makefile src/Makefile src/syncevo/Makefile src/syncevo/syncevolution.pc src/gtk-ui/Makefile po/Makefile.in test/Makefile src/dbus/syncevo-dbus.pc)
+AC_CONFIG_FILES(Makefile src/dbus/interfaces/Makefile src/gdbus/Makefile src/dbus/Makefile src/Makefile src/syncevo/Makefile src/syncevo/syncevolution.pc src/syncevo/configs/Makefile src/gtk-ui/Makefile po/Makefile.in test/Makefile src/dbus/syncevo-dbus.pc)
 AC_OUTPUT
 
 echo
@@ -1010,4 +1019,6 @@ done
 echo "DBus service: $enable_dbus_service"
 echo "UI (DBus client): $enable_gui"
 echo "Bluetooth transport: $have_bluetooth"
+echo "GNOME Bluetooth panel plugin: $enable_gnome_bluetooth_panel"
+echo "SHA-256: $have_sha"
 echo
index 12ae40a..bb172d0 100644 (file)
@@ -5,3 +5,4 @@ src/gtk-ui/ui.glade
 src/gtk-ui/sync.desktop.in
 src/gtk-ui/sync-gtk.desktop.in
 src/gtk-ui/sync-config-widget.c
+src/gnome-bluetooth/syncevolution.c
index 4d5eb44..804686c 100644 (file)
--- a/po/it.po
+++ b/po/it.po
@@ -7,9 +7,9 @@ msgid ""
 msgstr ""
 "Project-Id-Version: syncevolution\n"
 "Report-Msgid-Bugs-To: http://moblin.org/projects/syncevolution\n"
-"POT-Creation-Date: 2009-11-19 18:24+0000\n"
-"PO-Revision-Date: 2009-11-24 15:33-0800\n"
-"Last-Translator: Tomás Galicia <tomas.galicia@intel.com>\n"
+"POT-Creation-Date: 2010-02-12 00:01+0000\n"
+"PO-Revision-Date: 2010-02-24 13:28+0100\n"
+"Last-Translator: Lorenzo <lorenzo.gennaro@ptiglobal.net>\n"
 "Language-Team: Italian <tp@lists.linux.it>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
@@ -19,451 +19,521 @@ msgstr ""
 # (ndt) titolo finestra
 #. TRANSLATORS: this is the application name that may be used by e.g.
 #. the windowmanager
-#: ../src/gtk-ui/main.c:31
-#: ../src/gtk-ui/ui.glade.h:28
+#: ../src/gtk-ui/main.c:40
+#: ../src/gtk-ui/ui.glade.h:36
 #: ../src/gtk-ui/sync.desktop.in.h:1
+#: ../src/gnome-bluetooth/syncevolution.c:112
 msgid "Sync"
 msgstr "Sincronizzazione"
 
-#: ../src/gtk-ui/sync-ui.c:259
-msgid "Addressbook"
-msgstr "Rubrica"
+#: ../src/gtk-ui/sync-ui.c:234
+msgid "Contacts"
+msgstr "Contatti"
 
-#: ../src/gtk-ui/sync-ui.c:261
-msgid "Calendar"
-msgstr "Calendario"
-
-#: ../src/gtk-ui/sync-ui.c:263
-msgid "Todo"
-msgstr "Attività"
-
-#: ../src/gtk-ui/sync-ui.c:265
-msgid "Memo"
-msgstr "Memo"
-
-#: ../src/gtk-ui/sync-ui.c:320
-msgid "Failed to save current service in GConf configuration system"
-msgstr "Salvataggio del servizio attuale nel sistema di configurazione GConf non riuscito"
+#: ../src/gtk-ui/sync-ui.c:236
+msgid "Appointments"
+msgstr "Appuntamenti"
 
-#: ../src/gtk-ui/sync-ui.c:331
-msgid "Failed to save service configuration to SyncEvolution"
-msgstr "Salvataggio della configurazione del servizio in SyncEvolution non riuscito"
-
-#: ../src/gtk-ui/sync-ui.c:416
-msgid "Failed to get service configuration from SyncEvolution"
-msgstr "Recupero della configurazione del servizio da SyncEvolution non riuscito"
-
-#: ../src/gtk-ui/sync-ui.c:480
-msgid "Failed to remove service configuration from SyncEvolution"
-msgstr "Rimozione della configurazione del servizio da SyncEvolution non riuscita"
-
-#: ../src/gtk-ui/sync-ui.c:600
-msgid "Service must have a name and server URL"
-msgstr "Il servizio deve avere un nome e un indirizzo del server"
+#: ../src/gtk-ui/sync-ui.c:238
+#: ../src/gtk-ui/ui.glade.h:38
+msgid "Tasks"
+msgstr "Compiti"
 
-#. sync is no longer in progress for some reason
-#: ../src/gtk-ui/sync-ui.c:676
-msgid "Failed to cancel: sync was no longer in progress"
-msgstr "Annullamento non riuscito: la sincronizzazione non era più in corso"
+#: ../src/gtk-ui/sync-ui.c:240
+msgid "Notes"
+msgstr "Note"
 
-#: ../src/gtk-ui/sync-ui.c:680
-msgid "Failed to cancel sync"
-msgstr "Annullamento sincronizzazione non riuscito"
+#. TRANSLATORS: This is a "combination source" for syncing with devices
+#. * that combine appointments and tasks. the name should match the ones
+#. * used for calendar and todo above
+#: ../src/gtk-ui/sync-ui.c:245
+msgid "Appointments & Tasks"
+msgstr "Appuntamenti e compiti"
 
-#: ../src/gtk-ui/sync-ui.c:684
-msgid "Canceling sync"
-msgstr "Annullamento sincronizzazione"
-
-#: ../src/gtk-ui/sync-ui.c:698
-msgid "Trying to cancel sync"
-msgstr "Tentativo di annullare la sincronizzazione"
+#: ../src/gtk-ui/sync-ui.c:317
+msgid "Starting sync"
+msgstr "Avvio sincronizzazione"
 
-#: ../src/gtk-ui/sync-ui.c:705
+#. TRANSLATORS: slow sync confirmation dialog message. Placeholder
+#. * is service/device name
+#: ../src/gtk-ui/sync-ui.c:355
 #, c-format
-msgid "Do you want to delete all local data and replace it with data from %s? This is not usually advised."
-msgstr "Eliminare i dati locali e sostituirli con quelli da %s? Solitamente questo non è consigliato."
+msgid "Do you want to slow sync with %s?"
+msgstr "Vuoi effettuare la sincronizzazione lenta con %s?"
 
-#: ../src/gtk-ui/sync-ui.c:710
-#, c-format
-msgid "Do you want to delete all data in %s and replace it with your local data? This is not usually advised."
-msgstr "Eliminare i dati in %s e sostituirli con quelli locali? Solitamente questo non è consigliato."
+#: ../src/gtk-ui/sync-ui.c:359
+msgid "Yes, do slow sync"
+msgstr "Sì, effettua la sincronizzazione lenta"
 
 # (ndt) pulsante
-#: ../src/gtk-ui/sync-ui.c:727
+#: ../src/gtk-ui/sync-ui.c:359
 msgid "No, cancel sync"
 msgstr "Annulla sincronizzazione"
 
+#. TRANSLATORS: confirmation dialog for refresh-from-server. Placeholder
+#. * is service/device name
+#: ../src/gtk-ui/sync-ui.c:392
+#, c-format
+msgid "Do you want to delete all local data and replace it with data from %s? This is not usually advised."
+msgstr "Eliminare i dati locali e sostituirli con quelli da %s? Solitamente questo non è consigliato."
+
 # (ndt) pulsante
-#: ../src/gtk-ui/sync-ui.c:728
+#: ../src/gtk-ui/sync-ui.c:397
+#: ../src/gtk-ui/sync-ui.c:428
 msgid "Yes, delete and replace"
 msgstr "Elimia e sostituisci"
 
-#: ../src/gtk-ui/sync-ui.c:750
-msgid "No sources are enabled, not syncing"
-msgstr "Nessuna sorgente è abilitata, niente da sincronizzare"
+#: ../src/gtk-ui/sync-ui.c:397
+#: ../src/gtk-ui/sync-ui.c:428
+#: ../src/gtk-ui/sync-ui.c:1217
+msgid "No"
+msgstr "No"
 
-#: ../src/gtk-ui/sync-ui.c:767
-msgid "A sync is already in progress"
-msgstr "È già in corso una sincronizzazione"
+#. TRANSLATORS: confirmation dialog for refresh-from-client. Placeholder
+#. * is service/device name
+#: ../src/gtk-ui/sync-ui.c:423
+#, c-format
+msgid "Do you want to delete all data in %s and replace it with your local data? This is not usually advised."
+msgstr "Eliminare i dati in %s e sostituirli con quelli locali? Solitamente questo non è consigliato."
 
-#: ../src/gtk-ui/sync-ui.c:769
-msgid "Failed to start sync"
-msgstr "Avvio della sincronizzazione non riuscito"
+#: ../src/gtk-ui/sync-ui.c:455
+msgid "Trying to cancel sync"
+msgstr "Tentativo di annullare la sincronizzazione"
 
-#: ../src/gtk-ui/sync-ui.c:774
-msgid "Starting sync"
-msgstr "Avvio sincronizzazione"
+#: ../src/gtk-ui/sync-ui.c:481
+msgid "No service selected"
+msgstr "Nessun servizio selezionato"
 
-#: ../src/gtk-ui/sync-ui.c:799
-msgid "Last synced just seconds ago"
+#. TRANSLATORS: This is the title on main view. Placeholder is
+#. * the service name. Example: "Google - synced just now"
+#: ../src/gtk-ui/sync-ui.c:489
+#, fuzzy, c-format
+msgid "%s - synced just now"
 msgstr "Ultima sincronizzazione pochi secondi fa"
 
-#: ../src/gtk-ui/sync-ui.c:802
-msgid "Last synced a minute ago"
+#: ../src/gtk-ui/sync-ui.c:493
+#, fuzzy, c-format
+msgid "%s - synced a minute ago"
 msgstr "Ultima sincronizzazione un minuto fa"
 
-#: ../src/gtk-ui/sync-ui.c:805
-#, c-format
-msgid "Last synced %ld minutes ago"
+#: ../src/gtk-ui/sync-ui.c:497
+#, fuzzy, c-format
+msgid "%s - synced %ld minutes ago"
 msgstr "Ultima sincronizzazione %ld minuti fa"
 
-#: ../src/gtk-ui/sync-ui.c:808
-msgid "Last synced an hour ago"
+#: ../src/gtk-ui/sync-ui.c:502
+#, fuzzy, c-format
+msgid "%s - synced an hour ago"
 msgstr "Ultima sincronizzazione un'ora fa"
 
-#: ../src/gtk-ui/sync-ui.c:811
-#, c-format
-msgid "Last synced %ld hours ago"
+#: ../src/gtk-ui/sync-ui.c:506
+#, fuzzy, c-format
+msgid "%s - synced %ld hours ago"
 msgstr "Ultima sincronizzazione %ld ore fa"
 
-#: ../src/gtk-ui/sync-ui.c:814
-msgid "Last synced a day ago"
+#: ../src/gtk-ui/sync-ui.c:511
+#, fuzzy, c-format
+msgid "%s - synced a day ago"
 msgstr "Ultima sincronizzazione un giorno fa"
 
-#: ../src/gtk-ui/sync-ui.c:817
-#, c-format
-msgid "Last synced %ld days ago"
+#: ../src/gtk-ui/sync-ui.c:515
+#, fuzzy, c-format
+msgid "%s - synced %ld days ago"
 msgstr "Ultima sincronizzazione %ld giorni fa"
 
+# (ndt) titolo finestra
+#. TRANSLATORS: Action buttons in error/info bars in main view.
+#: ../src/gtk-ui/sync-ui.c:563
+#: ../src/gtk-ui/ui.glade.h:35
+#, fuzzy
+msgid "Slow sync"
+msgstr "Sincronizzazione"
+
+#: ../src/gtk-ui/sync-ui.c:564
+msgid "Other options..."
+msgstr "Altre opzioni..."
+
+#: ../src/gtk-ui/sync-ui.c:567
+msgid "Select sync service"
+msgstr "Seleziona servizio di sincronizzazione"
+
+# (ndt) pulsante, sto sul corto
+#: ../src/gtk-ui/sync-ui.c:570
+msgid "Edit service settings"
+msgstr "Modifica impostazioni"
+
+#: ../src/gtk-ui/sync-ui.c:618
+#, fuzzy
+msgid "You haven't selected a sync service yet. Sync services let you synchronize your data between your netbook and a web service"
+msgstr ""
+"Non è ancora stato selezionato un servizio di sincronizzazione. Questi servizi\n"
+"consentono di sincronizzare i dati tra il proprio netbook e un servizio web."
+
 # (ndt) pulsante
-#: ../src/gtk-ui/sync-ui.c:902
+#: ../src/gtk-ui/sync-ui.c:662
 msgid "Sync again"
 msgstr "Sincronizza ancora"
 
 # (ndt) pulsante
-#: ../src/gtk-ui/sync-ui.c:904
-#: ../src/gtk-ui/ui.glade.h:29
+#: ../src/gtk-ui/sync-ui.c:664
 msgid "Sync now"
 msgstr "Sincronizza ora"
 
-#: ../src/gtk-ui/sync-ui.c:913
+#: ../src/gtk-ui/sync-ui.c:674
 msgid "Syncing"
 msgstr "Sincronizzazione"
 
 # (ndt) pulsante
-#: ../src/gtk-ui/sync-ui.c:919
+#. TRANSLATORS: This is for the button in main view, right side.
+#. Keep line length below ~20 characters, use two lines if needed
+#: ../src/gtk-ui/sync-ui.c:683
 msgid "Cancel sync"
 msgstr "Annulla sincronizzazione"
 
-#. TRANSLATORS: placeholder is a source name, shown with checkboxes in main window
-#: ../src/gtk-ui/sync-ui.c:1266
+# (ndt) pulsante, dovrebbe tornare alla finestra principale
+#. TRANSLATORS: button in the Moblin window title bar when main view is
+#. * not visible
+#: ../src/gtk-ui/sync-ui.c:763
+msgid "Back to sync"
+msgstr "Torna indietro"
+
+#. This is the expander label in emergency view. It summarizes the
+#. * currently selected data sources. First placeholder is service/device
+#. * name, second a comma separeted list of sources.
+#. * E.g. "Affected data: Google Contacts, Appointments"
+#: ../src/gtk-ui/sync-ui.c:1134
+#, c-format
+msgid "Affected data: %s %s"
+msgstr "Dati interessati: %s %s"
+
+#: ../src/gtk-ui/sync-ui.c:1139
+#, c-format
+msgid "Affected data: none"
+msgstr "Dati interessati: nessuno"
+
+#. TRANSLATORS: confirmation for restoring a backup. placeholder is the
+#. * backup time string defined below
+#: ../src/gtk-ui/sync-ui.c:1214
 #, c-format
-msgid "%s (not supported by this service)"
-msgstr "%s (non supportato da questo servizio)"
+msgid "Do you want to restore the backup from %s? All changes you have made since then will be lost."
+msgstr "Vuoi ripristinare il backup da% s? Tutte le modifiche apportate da allora saranno perse."
 
-#: ../src/gtk-ui/sync-ui.c:1299
+#: ../src/gtk-ui/sync-ui.c:1217
+msgid "Yes, restore"
+msgstr "Sì, ripristina"
+
+#: ../src/gtk-ui/sync-ui.c:1249
+#, c-format
+msgid "%x %X"
+msgstr "%x %X"
+
+#. TRANSLATORS: label for a backup in emergency view. Placeholder is
+#. * service or device name
+#: ../src/gtk-ui/sync-ui.c:1268
+#, c-format
+msgid "Backed up before syncing with %s"
+msgstr "Back up effettuato prima della sincronizzazione con %s"
+
+#: ../src/gtk-ui/sync-ui.c:1285
+msgid "Restore"
+msgstr "Ripristina"
+
+#. TRANSLATORS: this is an explanation in Emergency view.
+#. * Placeholder is a service/device name
+#: ../src/gtk-ui/sync-ui.c:1392
+#, c-format
+msgid "A normal sync with %s is not possible at this time. You can do a slow two-way sync, start from scratch or restore from backup."
+msgstr "Una sincronizzazione normale con % s non è possibile in questo momento. Si può fare una sincronizzazione lenta a due vie, iniziare da zero o ripristinare dal backup."
+
+#: ../src/gtk-ui/sync-ui.c:1400
 #, c-format
+msgid "If something has gone horribly wrong, you can try a slow sync, start from scratch or restore from backup."
+msgstr "Se qualcosa è andato terribilmente storto, puoi provare una sincronizzazione lenta, iniziare da zero o ripristinare dal backup"
+
+#. TRANSLATORS: These are a buttons in Emergency view. Placeholder is a
+#. * service/device name. Please don't use too long lines, but feel free to
+#. * use several lines.
+#: ../src/gtk-ui/sync-ui.c:1409
+#, fuzzy, c-format
+msgid ""
+"Delete all your local\n"
+"data and replace with\n"
+"data from %s"
+msgstr "Eliminare i dati locali e sostituirli con quelli remoti"
+
+#: ../src/gtk-ui/sync-ui.c:1415
+#, fuzzy, c-format
+msgid ""
+"Delete all data on\n"
+"%s and replace\n"
+"with your local data"
+msgstr "Eliminare i dati remoti e sostituirli con quelli locali"
+
+#: ../src/gtk-ui/sync-ui.c:1854
+msgid "Failed to get list of supported services from SyncEvolution"
+msgstr "Recupero elenco dei servizi supportati da SyncEvolution non riuscito"
+
+#: ../src/gtk-ui/sync-ui.c:1958
+#: ../src/gtk-ui/sync-ui.c:2854
+#, fuzzy
+msgid "Sync failed"
+msgstr "Sincronizzazione non riuscita"
+
+#: ../src/gtk-ui/sync-ui.c:1962
+msgid "Sync complete"
+msgstr "Sincronizzazione completata"
+
+#: ../src/gtk-ui/sync-ui.c:2053
+#, c-format
+msgid "Preparing '%s'"
+msgstr "Preparazione di «%s»"
+
+#: ../src/gtk-ui/sync-ui.c:2056
+#, c-format
+msgid "Receiving '%s'"
+msgstr "Ricezione di «%s»"
+
+#: ../src/gtk-ui/sync-ui.c:2059
+#, c-format
+msgid "Sending '%s'"
+msgstr "Invio di «%s»"
+
+#: ../src/gtk-ui/sync-ui.c:2180
+#, fuzzy, c-format
 msgid "There was one remote rejection."
-msgid_plural "There were %d remote rejections."
+msgid_plural "There were %ld remote rejections."
 msgstr[0] "Si è verificato un rifiuto remoto."
 msgstr[1] "Si sono verificati %d rifiuti remoti."
 
-#: ../src/gtk-ui/sync-ui.c:1304
-#, c-format
+#: ../src/gtk-ui/sync-ui.c:2185
+#, fuzzy, c-format
 msgid "There was one local rejection."
-msgid_plural "There were %d local rejections."
+msgid_plural "There were %ld local rejections."
 msgstr[0] "Si è verificato un rifiuto locale."
 msgstr[1] "Si sono verificati %d rifiuti locali."
 
-#: ../src/gtk-ui/sync-ui.c:1309
-#, c-format
-msgid "There were %d local rejections and %d remote rejections."
+#: ../src/gtk-ui/sync-ui.c:2190
+#, fuzzy, c-format
+msgid "There were %ld local rejections and %ld remote rejections."
 msgstr "Si sono verificati %d rifiuti locali e %d remoti."
 
-#: ../src/gtk-ui/sync-ui.c:1314
+#: ../src/gtk-ui/sync-ui.c:2195
 #, c-format
 msgid "Last time: No changes."
 msgstr "Ultima volta: nessuna modifica."
 
-#: ../src/gtk-ui/sync-ui.c:1316
-#, c-format
+#: ../src/gtk-ui/sync-ui.c:2197
+#, fuzzy, c-format
 msgid "Last time: Sent one change."
-msgid_plural "Last time: Sent %d changes."
+msgid_plural "Last time: Sent %ld changes."
 msgstr[0] "Ultima volta: inviata una modifica."
 msgstr[1] "Ultima volta: inviate %d modifiche."
 
 #. This is about changes made to the local data. Not all of these
 #. changes were requested by the remote server, so "applied"
 #. is a better word than "received" (bug #5185).
-#: ../src/gtk-ui/sync-ui.c:1324
-#, c-format
+#: ../src/gtk-ui/sync-ui.c:2205
+#, fuzzy, c-format
 msgid "Last time: Applied one change."
-msgid_plural "Last time: Applied %d changes."
+msgid_plural "Last time: Applied %ld changes."
 msgstr[0] "Ultima volta: applicata una modifica."
 msgstr[1] "Ultima volta: applicate %d modifiche."
 
-#: ../src/gtk-ui/sync-ui.c:1329
-#, c-format
-msgid "Last time: Applied %d changes and sent %d changes."
+#: ../src/gtk-ui/sync-ui.c:2210
+#, fuzzy, c-format
+msgid "Last time: Applied %ld changes and sent %ld changes."
 msgstr "Ultima volta: applicate %d modifiche e inviate %d."
 
-#: ../src/gtk-ui/sync-ui.c:1421
-msgid "Failed to get server configuration from SyncEvolution"
-msgstr "Recupero della configurazione del server da SyncEvolution non riuscito"
-
-#: ../src/gtk-ui/sync-ui.c:1473
-msgid "ScheduleWorld enables you to keep your contacts, events, tasks, and notes in sync."
-msgstr "ScheduleWorld consente di tenere sincronizzati contatti, eventi, attività e note."
-
-#: ../src/gtk-ui/sync-ui.c:1476
-msgid "Google Sync can backup and synchronize your Address Book with your Gmail contacts."
-msgstr "Google Sync è in grado di eseguire delle copie di sicurezza e di sincronizzare la propria rubrica con i contatti di Gmail."
-
-#. TRANSLATORS: Please include the word "demo" (or the equivalent in
-#. your language): Funambol is going to be a 90 day demo service
-#. in the future
-#: ../src/gtk-ui/sync-ui.c:1482
-msgid "Backup your contacts and calendar. Sync with a singleclick, anytime, anywhere (DEMO)."
-msgstr "Eseguire una copia di sicurezza dei propri contatti e del proprio calendario. Sincronizzare con un solo clic, sempre, ovunque. (dimostrativo)"
-
-#: ../src/gtk-ui/sync-ui.c:1510
-msgid "New service"
-msgstr "Nuovo servizio"
+#: ../src/gtk-ui/sync-ui.c:2711
+msgid "Waiting for current operation to finish..."
+msgstr "In attesa che termini l'operazione..."
 
-#: ../src/gtk-ui/sync-ui.c:1557
-msgid "Server URL"
-msgstr "URL del server"
+#: ../src/gtk-ui/sync-ui.c:2784
+msgid "A normal sync is not possible at this time. The server suggests a slow sync, but this might not always be what you want if both ends already have data."
+msgstr "Una sincronizzazione normale non è possibile in questo momento. Il server suggerisce una sincronizzazione lenta, ma questo potrebbe non essere sempre quello che vuoi, se entrambe le estremità hanno dati."
 
-#. TRANSLATORS: placeholder is a source name in settings window
-#: ../src/gtk-ui/sync-ui.c:1579
-#, c-format
-msgid "%s URI"
-msgstr "URI di %s"
-
-# (ndt) è un collegamento
-#: ../src/gtk-ui/sync-ui.c:1716
-#: ../src/gtk-ui/ui.glade.h:17
-msgid "Launch website"
-msgstr "Visita il sito web"
-
-# (ndt) pulsante
-#: ../src/gtk-ui/sync-ui.c:1720
-msgid "Setup and use"
-msgstr "Configura e usa"
+#: ../src/gtk-ui/sync-ui.c:2791
+msgid "Failed to login. Could there be a problem with your username or password?"
+msgstr "Impossibile effettuare il login. Ci potrebbe essere un problema con il tuo nome utente o la password?"
 
-#: ../src/gtk-ui/sync-ui.c:1766
-msgid "Failed to get list of manually setup services from SyncEvolution"
-msgstr "Recupero elenco dei servizi configurati manualmente da SyncEvolution non riuscito"
-
-#: ../src/gtk-ui/sync-ui.c:1807
-msgid "Failed to get list of supported services from SyncEvolution"
-msgstr "Recupero elenco dei servizi supportati da SyncEvolution non riuscito"
-
-#. TODO: this is a hack... SyncEnd should be a signal of it's own,
-#. not just hacked on top of the syncevolution error codes
-#: ../src/gtk-ui/sync-ui.c:1968
-msgid "Service configuration not found"
-msgstr "Configurazione del servizio non trovata"
-
-#: ../src/gtk-ui/sync-ui.c:1974
-msgid "Not authorized"
-msgstr "Non autorizzato"
-
-#: ../src/gtk-ui/sync-ui.c:1976
+#: ../src/gtk-ui/sync-ui.c:2794
 msgid "Forbidden"
 msgstr "Proibito"
 
-#: ../src/gtk-ui/sync-ui.c:1978
-msgid "Not found"
-msgstr "Non trovato"
+#: ../src/gtk-ui/sync-ui.c:2799
+msgid "The source could not be found. Could there be a problem with the server settings?"
+msgstr "Impossibile trovare la sorgente. Ci potrebbe essere un problema con le impostazioni del server?"
 
-#: ../src/gtk-ui/sync-ui.c:1980
+#: ../src/gtk-ui/sync-ui.c:2802
 msgid "Fatal database error"
 msgstr "Errore grave nel database"
 
-#: ../src/gtk-ui/sync-ui.c:1982
+#. This can happen when EDS is borked, restart it may help...
+#: ../src/gtk-ui/sync-ui.c:2805
+msgid "There is a problem with the local database. Syncing again or rebooting may help."
+msgstr "C'è un problema con il database locale. Sincronizzare di nuovo o riavviare."
+
+#: ../src/gtk-ui/sync-ui.c:2808
 msgid "Database error"
 msgstr "Errore nel database"
 
-#: ../src/gtk-ui/sync-ui.c:1984
+#: ../src/gtk-ui/sync-ui.c:2810
 msgid "No space left"
 msgstr "Spazio esaurito"
 
-#. TODO identify problem item somehow ?
-#: ../src/gtk-ui/sync-ui.c:1987
+#: ../src/gtk-ui/sync-ui.c:2812
 msgid "Failed to process SyncML"
 msgstr "Elaborazione SyncML non riuscita"
 
-#: ../src/gtk-ui/sync-ui.c:1989
+#: ../src/gtk-ui/sync-ui.c:2814
 msgid "Server authorization failed"
 msgstr "Autorizzazione server non riuscita"
 
-#: ../src/gtk-ui/sync-ui.c:1991
+#: ../src/gtk-ui/sync-ui.c:2816
 msgid "Failed to parse configuration file"
 msgstr "Analisi del file di configurazione non riuscita"
 
-#: ../src/gtk-ui/sync-ui.c:1993
+#: ../src/gtk-ui/sync-ui.c:2818
 msgid "Failed to read configuration file"
 msgstr "Lettura del file di configurazione non riuscita"
 
-#: ../src/gtk-ui/sync-ui.c:1995
+#: ../src/gtk-ui/sync-ui.c:2820
 msgid "No configuration found"
 msgstr "Non è stata trovata alcuna configurazione"
 
-#: ../src/gtk-ui/sync-ui.c:1997
+#: ../src/gtk-ui/sync-ui.c:2822
 msgid "No configuration file found"
 msgstr "Non è stato trovato alcun file di configurazione"
 
-#: ../src/gtk-ui/sync-ui.c:1999
+#: ../src/gtk-ui/sync-ui.c:2824
 msgid "Server sent bad content"
 msgstr "Il server ha inviato dei contenuti errati"
 
-#: ../src/gtk-ui/sync-ui.c:2001
-msgid "Transport failure (no connection?)"
-msgstr "Errore nel trasporto dei dati (connessione assente?)"
-
-#: ../src/gtk-ui/sync-ui.c:2003
-msgid "Connection timed out"
-msgstr "La connessione è terminata"
-
-#: ../src/gtk-ui/sync-ui.c:2005
+#: ../src/gtk-ui/sync-ui.c:2826
 msgid "Connection certificate has expired"
 msgstr "Il certificato della connessione è scaduto"
 
-#: ../src/gtk-ui/sync-ui.c:2007
+#: ../src/gtk-ui/sync-ui.c:2828
 msgid "Connection certificate is invalid"
 msgstr "Il certificato della connessione non è valido"
 
-#: ../src/gtk-ui/sync-ui.c:2010
-msgid "Connection failed"
-msgstr "La connessione non è stata stabilita"
+#: ../src/gtk-ui/sync-ui.c:2836
+msgid "We were unable to connect to the server. The problem could be temporary or there could be something wrong with the server settings."
+msgstr "Non siamo stati in grado di connetterci al server. Il problema potrebbe essere temporaneo o ci potrebbe essere qualcosa di sbagliato con le impostazioni del server."
 
-#: ../src/gtk-ui/sync-ui.c:2012
+#: ../src/gtk-ui/sync-ui.c:2840
 msgid "URL is bad"
 msgstr "L'URL non è corretto"
 
-#: ../src/gtk-ui/sync-ui.c:2014
+#: ../src/gtk-ui/sync-ui.c:2842
 msgid "Server not found"
 msgstr "Il server non è stato trovato"
 
-#: ../src/gtk-ui/sync-ui.c:2016
+#: ../src/gtk-ui/sync-ui.c:2844
 #, c-format
 msgid "Error %d"
 msgstr "Errore %d"
 
-#: ../src/gtk-ui/sync-ui.c:2026
-msgid "Sync D-Bus service exited unexpectedly"
-msgstr "Il servizio di sincronizzazione di D-Bus è terminato inaspettatamente"
-
-#: ../src/gtk-ui/sync-ui.c:2029
-#: ../src/gtk-ui/sync-ui.c:2080
-msgid "Sync Failed"
-msgstr "Sincronizzazione non riuscita"
-
-#: ../src/gtk-ui/sync-ui.c:2072
-msgid "Sync complete"
-msgstr "Sincronizzazione completata"
-
-#: ../src/gtk-ui/sync-ui.c:2077
-msgid "Sync canceled"
-msgstr "Sincronizzazione annullata"
-
-#. NOTE extra1 can be error here
-#: ../src/gtk-ui/sync-ui.c:2095
-msgid "Ending sync"
-msgstr "Termine sincronizzazione"
-
-#. TRANSLATORS: placeholder is a source name (e.g. 'Calendar') in a progress text
-#: ../src/gtk-ui/sync-ui.c:2119
-#, c-format
-msgid "Preparing '%s'"
-msgstr "Preparazione di «%s»"
-
-#. TRANSLATORS: placeholder is a source name in a progress text
-#: ../src/gtk-ui/sync-ui.c:2131
-#, c-format
-msgid "Sending '%s'"
-msgstr "Invio di «%s»"
-
-#. TRANSLATORS: placeholder is a source name in a progress text
-#: ../src/gtk-ui/sync-ui.c:2143
-#, c-format
-msgid "Receiving '%s'"
-msgstr "Ricezione di «%s»"
-
-#: ../src/gtk-ui/ui.glade.h:1
-msgid "<b>Data</b>"
-msgstr "<b>Dati</b>"
-
+#. title for the buttons on the right side of main view
 #: ../src/gtk-ui/ui.glade.h:2
-msgid "<b>No sync service in use</b>"
-msgstr "<b>Nessun servizio di sincronizzazione in uso</b>"
-
-#: ../src/gtk-ui/ui.glade.h:3
-msgid "<b>Sync failure</b>"
-msgstr "<b>Sincronizzazione non riuscita</b>"
+#, fuzzy
+msgid "<b>Actions</b>"
+msgstr "<b>Dati</b>"
 
+#. text between the two "start from scratch" buttons in emergency view
 #: ../src/gtk-ui/ui.glade.h:4
-msgid "<b>Type of Sync</b>"
-msgstr "<b>Tipo di sincronizzazione</b>"
+#, fuzzy
+msgid "<b>or</b>"
+msgstr "<b>Dati</b>"
 
 #: ../src/gtk-ui/ui.glade.h:5
-msgid "<big>Manual setup</big>"
-msgstr "<big>Configurazione manuale</big>"
+#, fuzzy
+msgid "<big>Direct sync</big>"
+msgstr "<big>Servizi supportati</big>"
 
 #: ../src/gtk-ui/ui.glade.h:6
-msgid "<big>Supported services</big>"
+#, fuzzy
+msgid "<big>Network sync</big>"
+msgstr "<b>Tipo di sincronizzazione</b>"
+
+#. a title in emergency view
+#: ../src/gtk-ui/ui.glade.h:8
+msgid "<big>Restore from backup</big>"
+msgstr "<big>Rirpistina dal backup</big>"
+
+#. a title in emergency view
+#: ../src/gtk-ui/ui.glade.h:10
+#, fuzzy
+msgid "<big>Slow sync</big>"
+msgstr "<big>Servizi supportati</big>"
+
+#. a title in emergency view
+#: ../src/gtk-ui/ui.glade.h:12
+#, fuzzy
+msgid "<big>Start from scratch</big>"
 msgstr "<big>Servizi supportati</big>"
 
+#: ../src/gtk-ui/ui.glade.h:13
+msgid ""
+"A slow sync compares items from both sides and tries to merge them. \n"
+"This may fail in some cases, leading to duplicates or lost information."
+msgstr "Questo potrebbe non riuscire in alcuni casi, portando a duplicati o a informazioni perse."
+
 # (ndt) pulsante
-#: ../src/gtk-ui/ui.glade.h:7
+#: ../src/gtk-ui/ui.glade.h:15
+msgid "Add new device"
+msgstr "Aggiungi un nuovo dispositivo"
+
+# (ndt) pulsante
+#: ../src/gtk-ui/ui.glade.h:16
 msgid "Add new service"
 msgstr "Aggiungi servizio"
 
-# (ndt) pulsante, dovrebbe tornare alla finestra principale
-#: ../src/gtk-ui/ui.glade.h:8
-msgid "Back to sync"
-msgstr "Torna indietro"
+#. explanation of "Restore backup" function
+#: ../src/gtk-ui/ui.glade.h:18
+msgid "Backups are made before every time we Sync. Choose a backup to restore. Any changes you have made since then will be lost."
+msgstr ""
+
+#: ../src/gtk-ui/ui.glade.h:19
+msgid "Calendar"
+msgstr "Calendario"
 
 # (ndt) pulsante
-#: ../src/gtk-ui/ui.glade.h:9
+#. Button in main view, right side. Keep to below 20 chars per line, feel free to use two lines
+#: ../src/gtk-ui/ui.glade.h:21
+#, fuzzy
 msgid ""
-"Change sync\n"
-"service"
+"Change or edit\n"
+"sync service"
 msgstr ""
 "Modifica servizio\n"
 "di sincronizzazione"
 
-#: ../src/gtk-ui/ui.glade.h:11
-msgid "Delete all local data and replace it with remote data"
-msgstr "Eliminare i dati locali e sostituirli con quelli remoti"
-
-#: ../src/gtk-ui/ui.glade.h:12
-msgid "Delete all remote data and replace it with local data"
+#: ../src/gtk-ui/ui.glade.h:23
+#, fuzzy
+msgid ""
+"Delete all data on Zyb \n"
+"and replace with your\n"
+"local information"
 msgstr "Eliminare i dati remoti e sostituirli con quelli locali"
 
-# (ndt) pulsante
-#: ../src/gtk-ui/ui.glade.h:13
-msgid "Delete this service"
-msgstr "Elimina servizio"
+#: ../src/gtk-ui/ui.glade.h:26
+#, fuzzy
+msgid ""
+"Delete all your local\n"
+"information and replace\n"
+"with data from Zyb"
+msgstr "Eliminare i dati locali e sostituirli con quelli remoti"
 
-# (ndt) pulsante, sto sul corto
-#: ../src/gtk-ui/ui.glade.h:14
-msgid "Edit service settings"
-msgstr "Modifica impostazioni"
+#. button in main view, right side. Keep length to 20 characters or so, use two lines if needed
+#: ../src/gtk-ui/ui.glade.h:30
+msgid ""
+"Fix a sync\n"
+"emergency"
+msgstr ""
 
 # (ndt) segnalare SyncML?
-#: ../src/gtk-ui/ui.glade.h:15
+#: ../src/gtk-ui/ui.glade.h:32
 msgid ""
 "If you don't see your service above but know that your sync provider uses SyncML\n"
 "you can setup a service manually."
@@ -471,54 +541,15 @@ msgstr ""
 "Se non vengono visualizzati i propri servizi, ma si è certi che il proprio provider\n"
 "usa SyncML, è possibile impostare un servizio manualmente."
 
-#: ../src/gtk-ui/ui.glade.h:18
-msgid "Merge local and remote data (recommended)"
-msgstr "Unire i dati locali e remoti (raccomandato)"
-
-#: ../src/gtk-ui/ui.glade.h:19
-msgid "Password"
-msgstr "Password"
-
-# (ndt) pulsante
-#: ../src/gtk-ui/ui.glade.h:20
-msgid "Reset original server settings"
-msgstr "Ripristina valori originali"
-
-# (ndt) pulsante
-#: ../src/gtk-ui/ui.glade.h:21
-msgid "Save and use this service"
-msgstr "Salva e usa il servizio"
-
-#: ../src/gtk-ui/ui.glade.h:22
-msgid "Select sync service"
-msgstr "Seleziona servizio di sincronizzazione"
-
-#: ../src/gtk-ui/ui.glade.h:23
-msgid "Server settings"
-msgstr "Impostazioni server"
-
-#: ../src/gtk-ui/ui.glade.h:24
-msgid "Service name"
-msgstr "Nome servizio"
+#: ../src/gtk-ui/ui.glade.h:34
+msgid "Settings"
+msgstr "Impostazioni"
 
-#: ../src/gtk-ui/ui.glade.h:25
-msgid ""
-"Sorry, you need an internet\n"
-"connection to sync."
+#: ../src/gtk-ui/ui.glade.h:37
+msgid "Sync Emergency"
 msgstr ""
-"È necessaria una connessione a\n"
-"Internet per la sincronizzazione."
-
-# (ndt) pulsante
-#: ../src/gtk-ui/ui.glade.h:27
-msgid "Stop using this service"
-msgstr "Non usare più"
-
-#: ../src/gtk-ui/ui.glade.h:30
-msgid "Synchronization is not available (D-Bus service does not answer), sorry."
-msgstr "La sincronizzazione non è disponibile (il servizio D-Bus non è attivo)."
 
-#: ../src/gtk-ui/ui.glade.h:31
+#: ../src/gtk-ui/ui.glade.h:39
 msgid ""
 "To sync you'll need a network connection and an account with a sync service.\n"
 "We support the following services: "
@@ -526,17 +557,13 @@ msgstr ""
 "Per effettuare la sincronizzazione sono necessari una connessione e un account con un servizio di sincronizzazione.\n"
 "I servizi supportati sono: "
 
-#: ../src/gtk-ui/ui.glade.h:33
-msgid "Username"
-msgstr "Nome utente"
+#: ../src/gtk-ui/ui.glade.h:41
+msgid "Use Bluetooth to Sync your data from one device to another."
+msgstr ""
 
-#: ../src/gtk-ui/ui.glade.h:34
-msgid ""
-"You haven't selected a sync service yet. Sync services let you \n"
-"synchronize your data between your netbook and a web service."
+#: ../src/gtk-ui/ui.glade.h:42
+msgid "You will need to add Bluetooth devices before they can be synced."
 msgstr ""
-"Non è ancora stato selezionato un servizio di sincronizzazione. Questi servizi\n"
-"consentono di sincronizzare i dati tra il proprio netbook e un servizio web."
 
 #: ../src/gtk-ui/sync.desktop.in.h:2
 #: ../src/gtk-ui/sync-gtk.desktop.in.h:2
@@ -547,6 +574,259 @@ msgstr "Aggiornato"
 msgid "Sync (GTK)"
 msgstr "Sync (GTK)"
 
-#~ msgid "Bring your data with you"
-#~ msgstr "Portare i dati con sé"
+#: ../src/gtk-ui/sync-config-widget.c:71
+msgid "ScheduleWorld enables you to keep your contacts, events, tasks, and notes in sync."
+msgstr "ScheduleWorld consente di tenere sincronizzati contatti, eventi, attività e note."
+
+#: ../src/gtk-ui/sync-config-widget.c:74
+msgid "Google Sync can backup and synchronize your contacts with your Gmail contacts."
+msgstr "Google Sync è in grado di eseguire backup di sicurezza e di sincronizzare la propria rubrica con i contatti di Gmail."
+
+#. TRANSLATORS: Please include the word "demo" (or the equivalent in
+#. your language): Funambol is going to be a 90 day demo service
+#. in the future
+#: ../src/gtk-ui/sync-config-widget.c:80
+msgid "Backup your contacts and calendar. Sync with a single click, anytime, anywhere (DEMO)."
+msgstr "Eseguire una copia di sicurezza dei propri contatti e del proprio calendario. Sincronizzare con un solo clic, sempre e ovunque (dimostrativo)."
+
+#: ../src/gtk-ui/sync-config-widget.c:83
+msgid "Mobical Backup and Restore service allows you to securely backup your personal mobile data for free."
+msgstr ""
+
+#: ../src/gtk-ui/sync-config-widget.c:86
+msgid "ZYB is a simple way for people to store and share mobile information online."
+msgstr ""
+
+#: ../src/gtk-ui/sync-config-widget.c:89
+msgid "Memotoo lets you access your personal data from any computer connected to the Internet."
+msgstr ""
+
+#: ../src/gtk-ui/sync-config-widget.c:255
+#, c-format
+msgid "Do you want to replace %s with %s? This will not remove any synced information on either end but you will no longer be able to sync with %s."
+msgstr ""
+
+#. TRANSLATORS: decline/accept buttons in warning dialog.
+#. Placeholder is service name
+#: ../src/gtk-ui/sync-config-widget.c:267
+#, c-format
+msgid "Yes, use %s"
+msgstr "Sì, utilizza %s"
+
+#: ../src/gtk-ui/sync-config-widget.c:268
+#, c-format
+msgid "No, use %s"
+msgstr "No, utilizza %s"
+
+#: ../src/gtk-ui/sync-config-widget.c:341
+msgid "Service must have a name and server URL"
+msgstr "Il servizio deve avere un nome e un indirizzo del server"
+
+#: ../src/gtk-ui/sync-config-widget.c:381
+#, c-format
+msgid "Do you want to reset the settings for %s? This will not remove any synced information on either end."
+msgstr ""
+
+#. TRANSLATORS: buttons in reset-service warning dialog
+#: ../src/gtk-ui/sync-config-widget.c:385
+msgid "Yes, reset"
+msgstr "Sì, ripristina"
+
+#: ../src/gtk-ui/sync-config-widget.c:386
+#: ../src/gtk-ui/sync-config-widget.c:397
+msgid "No, keep settings"
+msgstr "No, mantieni le impostazioni"
+
+#: ../src/gtk-ui/sync-config-widget.c:391
+#, c-format
+msgid "Do you want to delete the settings for %s? This will not remove any synced information on either end but it will remove this service configuration."
+msgstr ""
+
+# (ndt) pulsante
+#. TRANSLATORS: buttons in delete-service warning dialog
+#: ../src/gtk-ui/sync-config-widget.c:396
+msgid "Yes, delete"
+msgstr "Sì, elimina"
+
+#: ../src/gtk-ui/sync-config-widget.c:425
+msgid "Reset service"
+msgstr "Servizio di ripristino"
+
+# (ndt) pulsante
+#: ../src/gtk-ui/sync-config-widget.c:428
+msgid "Delete service"
+msgstr "Elimina servizio"
+
+# (ndt) pulsante
+#: ../src/gtk-ui/sync-config-widget.c:438
+msgid "Save and use"
+msgstr "Salva e usa"
+
+# (ndt) pulsante
+#: ../src/gtk-ui/sync-config-widget.c:441
+msgid ""
+"Save and replace\n"
+"current service"
+msgstr ""
+"Salva e sostituisci\n"
+" il servizio"
+
+#. TRANSLATORS: label for an entry in service configuration form.
+#. * Placeholder is a source  name.
+#. * Example: "Appointments URI"
+#: ../src/gtk-ui/sync-config-widget.c:672
+#, c-format
+msgid "%s URI"
+msgstr "URI di %s"
+
+#. TRANSLATORS: toggles in service configuration form, placeholder is service
+#. * or device name
+#: ../src/gtk-ui/sync-config-widget.c:850
+#, c-format
+msgid "Send changes to %s"
+msgstr "Invia modifiche a %s"
+
+#: ../src/gtk-ui/sync-config-widget.c:854
+#, c-format
+msgid "Receive changes from %s"
+msgstr ""
+
+#: ../src/gtk-ui/sync-config-widget.c:869
+msgid "<b>Sync</b>"
+msgstr "<b>Sincronizzazione</b>"
+
+#. TRANSLATORS: label of a entry in service configuration
+#: ../src/gtk-ui/sync-config-widget.c:885
+msgid "Server address"
+msgstr "Indirizzo server"
+
+#: ../src/gtk-ui/sync-config-widget.c:1067
+#, c-format
+msgid "%s - Bluetooth device"
+msgstr ""
+
+#. TRANSLATORS: service title for services that are not based on a
+#. * template in service list, the placeholder is the name of the service
+#: ../src/gtk-ui/sync-config-widget.c:1073
+#, c-format
+msgid "%s - manually setup"
+msgstr ""
+
+# (ndt) è un collegamento
+#. TRANSLATORS: link button in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1748
+msgid "Launch website"
+msgstr "Visita il sito web"
+
+# (ndt) pulsante
+#. TRANSLATORS: button in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1757
+msgid "Setup now"
+msgstr "Configura adesso"
+
+#. TRANSLATORS: labels of entries in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1820
+msgid "Username"
+msgstr "Nome utente"
+
+#: ../src/gtk-ui/sync-config-widget.c:1835
+msgid "Password"
+msgstr "Password"
+
+#. TRANSLATORS: warning in service configuration form for people
+#. who have modified the configuration via other means.
+#: ../src/gtk-ui/sync-config-widget.c:1858
+msgid "Current service configuration is more complex than what can be shown here. Changes to sync mode or synced data types will overwrite that configuration."
+msgstr ""
+
+#. TRANSLATORS: this is the epander label for server settings
+#. in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1877
+msgid "Hide server settings"
+msgstr "Nascondi impostazioni server"
+
+#. TRANSLATORS: this is the epander label for server settings
+#. in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1897
+msgid "Show server settings"
+msgstr "Mostra impostazioni server"
+
+# (ndt) pulsante
+#. TRANSLATORS: button in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1910
+msgid "Stop using service"
+msgstr "Non usare il servizio"
+
+#: ../src/gnome-bluetooth/syncevolution.c:110
+msgid "Sync in the Sync application"
+msgstr ""
+
+#~ msgid "Addressbook"
+#~ msgstr "Rubrica"
+#~ msgid "Todo"
+#~ msgstr "Attività"
+#~ msgid "Memo"
+#~ msgstr "Memo"
+#~ msgid "Failed to save current service in GConf configuration system"
+#~ msgstr ""
+#~ "Salvataggio del servizio attuale nel sistema di configurazione GConf non "
+#~ "riuscito"
+#~ msgid "Failed to save service configuration to SyncEvolution"
+#~ msgstr ""
+#~ "Salvataggio della configurazione del servizio in SyncEvolution non "
+#~ "riuscito"
+#~ msgid "Failed to get service configuration from SyncEvolution"
+#~ msgstr ""
+#~ "Recupero della configurazione del servizio da SyncEvolution non riuscito"
+#~ msgid "Failed to remove service configuration from SyncEvolution"
+#~ msgstr ""
+#~ "Rimozione della configurazione del servizio da SyncEvolution non riuscita"
+#~ msgid "Failed to cancel: sync was no longer in progress"
+#~ msgstr "Annullamento non riuscito: la sincronizzazione non era più in corso"
+#~ msgid "Failed to cancel sync"
+#~ msgstr "Annullamento sincronizzazione non riuscito"
+#~ msgid "Canceling sync"
+#~ msgstr "Annullamento sincronizzazione"
+#~ msgid "No sources are enabled, not syncing"
+#~ msgstr "Nessuna sorgente è abilitata, niente da sincronizzare"
+#~ msgid "A sync is already in progress"
+#~ msgstr "È già in corso una sincronizzazione"
+#~ msgid "Failed to start sync"
+#~ msgstr "Avvio della sincronizzazione non riuscito"
+#~ msgid "%s (not supported by this service)"
+#~ msgstr "%s (non supportato da questo servizio)"
+#~ msgid "Failed to get server configuration from SyncEvolution"
+#~ msgstr ""
+#~ "Recupero della configurazione del server da SyncEvolution non riuscito"
+#~ msgid "Server URL"
+#~ msgstr "URL del server"
+#~ msgid "Failed to get list of manually setup services from SyncEvolution"
+#~ msgstr ""
+#~ "Recupero elenco dei servizi configurati manualmente da SyncEvolution non "
+#~ "riuscito"
+#~ msgid "Service configuration not found"
+#~ msgstr "Configurazione del servizio non trovata"
+#~ msgid "Not authorized"
+#~ msgstr "Non autorizzato"
+#~ msgid "Not found"
+#~ msgstr "Non trovato"
+#~ msgid "Transport failure (no connection?)"
+#~ msgstr "Errore nel trasporto dei dati (connessione assente?)"
+#~ msgid "Connection timed out"
+#~ msgstr "La connessione è terminata"
+#~ msgid "Connection failed"
+#~ msgstr "La connessione non è stata stabilita"
+#~ msgid "Sync D-Bus service exited unexpectedly"
+#~ msgstr ""
+#~ "Il servizio di sincronizzazione di D-Bus è terminato inaspettatamente"
+#~ msgid "Sync canceled"
+#~ msgstr "Sincronizzazione annullata"
+#~ msgid "Ending sync"
+#~ msgstr "Termine sincronizzazione"
+#~ msgid "<b>No sync service in use</b>"
+#~ msgstr "<b>Nessun servizio di sincronizzazione in uso</b>"
+#~ msgid "<big>Manual setup</big>"
+#~ msgstr "<big>Configurazione manuale</big>"
+#~ msgid "Merge local and remote data (recommended)"
+#~ msgstr "Unire i dati locali e remoti (raccomandato)"
 
index 1644aeb..158a0e7 100644 (file)
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: http://moblin.org/projects/syncevolution\n"
-"POT-Creation-Date: 2009-12-21 18:45+0000\n"
+"POT-Creation-Date: 2010-02-12 00:01+0000\n"
 "PO-Revision-Date: \n"
 "Last-Translator: Cheng-Chia Tseng <pswo10680@gmail.com>\n"
 "Language-Team: \n"
@@ -15,344 +15,505 @@ msgstr ""
 
 #. TRANSLATORS: this is the application name that may be used by e.g.
 #. the windowmanager
-#: ../src/gtk-ui/main.c:31
-#: ../src/gtk-ui/ui.glade.h:20
+#: ../src/gtk-ui/main.c:40
+#: ../src/gtk-ui/ui.glade.h:36
 #: ../src/gtk-ui/sync.desktop.in.h:1
+#: ../src/gnome-bluetooth/syncevolution.c:112
 msgid "Sync"
 msgstr "同步"
 
-#. TRANSLATORS: The name was changed from 'Addressbook' to
-#. 'Contacts' to match naming in rest of moblin. Please make sure the
-#. name you use matches the name in the panel and Contacts application.
-#: ../src/gtk-ui/sync-ui.c:192
+#: ../src/gtk-ui/sync-ui.c:234
 msgid "Contacts"
 msgstr "連絡人"
 
-#: ../src/gtk-ui/sync-ui.c:194
-msgid "Calendar"
-msgstr "行事曆"
+#: ../src/gtk-ui/sync-ui.c:236
+msgid "Appointments"
+msgstr "約會"
 
-#: ../src/gtk-ui/sync-ui.c:196
-msgid "Todo"
-msgstr "要做的事"
+#: ../src/gtk-ui/sync-ui.c:238
+#: ../src/gtk-ui/ui.glade.h:38
+msgid "Tasks"
+msgstr "工作"
 
-#: ../src/gtk-ui/sync-ui.c:198
-msgid "Memo"
-msgstr "備忘錄"
+#: ../src/gtk-ui/sync-ui.c:240
+msgid "Notes"
+msgstr "註記"
 
-#. TODO show in UI: failed to abort sync (while syncing)
-#: ../src/gtk-ui/sync-ui.c:273
-msgid "Failed to abort sync"
-msgstr "放棄同步時失敗"
+#. TRANSLATORS: This is a "combination source" for syncing with devices
+#. * that combine appointments and tasks. the name should match the ones
+#. * used for calendar and todo above
+#: ../src/gtk-ui/sync-ui.c:245
+msgid "Appointments & Tasks"
+msgstr "約會與工作"
 
-#. TODO show in UI: sync failed (failed to even start)
-#: ../src/gtk-ui/sync-ui.c:287
-msgid "Failed to start sync"
-msgstr "開始同步動作失敗"
-
-#: ../src/gtk-ui/sync-ui.c:293
+#: ../src/gtk-ui/sync-ui.c:317
 msgid "Starting sync"
 msgstr "正在開始同步"
 
-#: ../src/gtk-ui/sync-ui.c:309
-msgid "Trying to cancel sync"
-msgstr "正在嘗試取消同步"
+#. TRANSLATORS: slow sync confirmation dialog message. Placeholder
+#. * is service/device name
+#: ../src/gtk-ui/sync-ui.c:355
+#, c-format
+msgid "Do you want to slow sync with %s?"
+msgstr "你想要與 %s 進行慢速同步嗎?"
+
+#: ../src/gtk-ui/sync-ui.c:359
+msgid "Yes, do slow sync"
+msgstr "是,執行慢速同步"
 
-#: ../src/gtk-ui/sync-ui.c:316
+#: ../src/gtk-ui/sync-ui.c:359
+msgid "No, cancel sync"
+msgstr "不,取消同步"
+
+#. TRANSLATORS: confirmation dialog for refresh-from-server. Placeholder
+#. * is service/device name
+#: ../src/gtk-ui/sync-ui.c:392
 #, c-format
 msgid "Do you want to delete all local data and replace it with data from %s? This is not usually advised."
 msgstr "你要刪除所有本機資料,並使用 %s 上的資料取代它嗎?我們通常不建議這麼做。"
 
-#: ../src/gtk-ui/sync-ui.c:321
+#: ../src/gtk-ui/sync-ui.c:397
+#: ../src/gtk-ui/sync-ui.c:428
+msgid "Yes, delete and replace"
+msgstr "是,刪除並取代"
+
+#: ../src/gtk-ui/sync-ui.c:397
+#: ../src/gtk-ui/sync-ui.c:428
+#: ../src/gtk-ui/sync-ui.c:1217
+msgid "No"
+msgstr "否"
+
+#. TRANSLATORS: confirmation dialog for refresh-from-client. Placeholder
+#. * is service/device name
+#: ../src/gtk-ui/sync-ui.c:423
 #, c-format
 msgid "Do you want to delete all data in %s and replace it with your local data? This is not usually advised."
-msgstr "你要刪除所有在 %s 上的資料,並且使用本資料取代它嗎?我們通常不建議這麼做。"
+msgstr "你要刪除所有在 %s 上的資料,並且使用本資料取代它嗎?我們通常不建議這麼做。"
 
-#: ../src/gtk-ui/sync-ui.c:338
-msgid "No, cancel sync"
-msgstr "不,取消同步"
+#: ../src/gtk-ui/sync-ui.c:455
+msgid "Trying to cancel sync"
+msgstr "正在嘗試取消同步"
 
-#: ../src/gtk-ui/sync-ui.c:340
-msgid "Yes, delete and replace"
-msgstr "æ\98¯ï¼\8cå\88ªé\99¤ä¸¦å\8f\96代"
+#: ../src/gtk-ui/sync-ui.c:481
+msgid "No service selected"
+msgstr "æ\9cªé\81¸å\8f\96æ\9c\8då\8b\99"
 
-#: ../src/gtk-ui/sync-ui.c:377
-msgid "Last synced just seconds ago"
-msgstr "最後一次同步是在幾秒鐘以前"
+#. TRANSLATORS: This is the title on main view. Placeholder is
+#. * the service name. Example: "Google - synced just now"
+#: ../src/gtk-ui/sync-ui.c:489
+#, c-format
+msgid "%s - synced just now"
+msgstr "%s - 剛才同步過"
 
-#: ../src/gtk-ui/sync-ui.c:380
-msgid "Last synced a minute ago"
-msgstr "最後一次同步是在一分鐘以前"
+#: ../src/gtk-ui/sync-ui.c:493
+#, c-format
+msgid "%s - synced a minute ago"
+msgstr "%s - 一分鐘前同步過"
 
-#: ../src/gtk-ui/sync-ui.c:383
+#: ../src/gtk-ui/sync-ui.c:497
 #, c-format
-msgid "Last synced %ld minutes ago"
-msgstr "最後一次同步是在 %ld 分鐘以前"
+msgid "%s - synced %ld minutes ago"
+msgstr "%s - %ld 分鐘前同步過"
 
-#: ../src/gtk-ui/sync-ui.c:386
-msgid "Last synced an hour ago"
-msgstr "最後一次同步是在一小時以前"
+#: ../src/gtk-ui/sync-ui.c:502
+#, c-format
+msgid "%s - synced an hour ago"
+msgstr "%s - 一小時前同步過"
 
-#: ../src/gtk-ui/sync-ui.c:389
+#: ../src/gtk-ui/sync-ui.c:506
 #, c-format
-msgid "Last synced %ld hours ago"
-msgstr "最後一次同步是在 %ld 小時以前"
+msgid "%s - synced %ld hours ago"
+msgstr "%s - %ld 小時前同步過"
 
-#: ../src/gtk-ui/sync-ui.c:392
-msgid "Last synced a day ago"
-msgstr "最後一次同步是在一天以前"
+#: ../src/gtk-ui/sync-ui.c:511
+#, c-format
+msgid "%s - synced a day ago"
+msgstr "%s - 一天前同步過"
 
-#: ../src/gtk-ui/sync-ui.c:395
+#: ../src/gtk-ui/sync-ui.c:515
 #, c-format
-msgid "Last synced %ld days ago"
-msgstr "最後一次同步是在 %ld 天以前"
+msgid "%s - synced %ld days ago"
+msgstr "%s - %ld 天前同步過"
+
+#. TRANSLATORS: Action buttons in error/info bars in main view.
+#: ../src/gtk-ui/sync-ui.c:563
+#: ../src/gtk-ui/ui.glade.h:35
+msgid "Slow sync"
+msgstr "慢速同步"
+
+#: ../src/gtk-ui/sync-ui.c:564
+msgid "Other options..."
+msgstr "其他選項..."
+
+#: ../src/gtk-ui/sync-ui.c:567
+msgid "Select sync service"
+msgstr "選取同步服務"
 
-#: ../src/gtk-ui/sync-ui.c:480
+#: ../src/gtk-ui/sync-ui.c:570
+msgid "Edit service settings"
+msgstr "編輯服務設定"
+
+#: ../src/gtk-ui/sync-ui.c:618
+msgid "You haven't selected a sync service yet. Sync services let you synchronize your data between your netbook and a web service"
+msgstr "你尚未選取同步服務。同步服務讓你在你的網路筆電與網路服務間同步你的資料"
+
+#: ../src/gtk-ui/sync-ui.c:662
 msgid "Sync again"
 msgstr "再次同步"
 
-#: ../src/gtk-ui/sync-ui.c:482
-#: ../src/gtk-ui/ui.glade.h:21
+#: ../src/gtk-ui/sync-ui.c:664
 msgid "Sync now"
 msgstr "現在同步"
 
-#: ../src/gtk-ui/sync-ui.c:493
+#: ../src/gtk-ui/sync-ui.c:674
 msgid "Syncing"
 msgstr "正在同步中"
 
-#: ../src/gtk-ui/sync-ui.c:499
+#. TRANSLATORS: This is for the button in main view, right side.
+#. Keep line length below ~20 characters, use two lines if needed
+#: ../src/gtk-ui/sync-ui.c:683
 msgid "Cancel sync"
 msgstr "取消同步"
 
-#. TRANSLATORS: placeholder is a source name, shown with checkboxes in main window
-#: ../src/gtk-ui/sync-ui.c:849
+#. TRANSLATORS: button in the Moblin window title bar when main view is
+#. * not visible
+#: ../src/gtk-ui/sync-ui.c:763
+msgid "Back to sync"
+msgstr "返回同步"
+
+#. This is the expander label in emergency view. It summarizes the
+#. * currently selected data sources. First placeholder is service/device
+#. * name, second a comma separeted list of sources.
+#. * E.g. "Affected data: Google Contacts, Appointments"
+#: ../src/gtk-ui/sync-ui.c:1134
+#, c-format
+msgid "Affected data: %s %s"
+msgstr "受影響的資料:%s %s"
+
+#: ../src/gtk-ui/sync-ui.c:1139
+#, c-format
+msgid "Affected data: none"
+msgstr "受影響的資料:無"
+
+#. TRANSLATORS: confirmation for restoring a backup. placeholder is the
+#. * backup time string defined below
+#: ../src/gtk-ui/sync-ui.c:1214
+#, c-format
+msgid "Do you want to restore the backup from %s? All changes you have made since then will be lost."
+msgstr "你想要還原來自 %s 的備份嗎?所有你從那時起的變更都會喪失。"
+
+#: ../src/gtk-ui/sync-ui.c:1217
+msgid "Yes, restore"
+msgstr "是,還原"
+
+#: ../src/gtk-ui/sync-ui.c:1249
+#, c-format
+msgid "%x %X"
+msgstr "%x %X"
+
+#. TRANSLATORS: label for a backup in emergency view. Placeholder is
+#. * service or device name
+#: ../src/gtk-ui/sync-ui.c:1268
 #, c-format
-msgid "%s (not supported by this service)"
-msgstr "%s (不被此服務支援)"
+msgid "Backed up before syncing with %s"
+msgstr "在與 %s 同步前已備份"
 
-#: ../src/gtk-ui/sync-ui.c:1046
-msgid "Failed to get list of configured services from SyncEvolution"
-msgstr "從 SyncEvolution 取得已設定的服務清單時失敗"
+#: ../src/gtk-ui/sync-ui.c:1285
+msgid "Restore"
+msgstr "還原"
 
-#: ../src/gtk-ui/sync-ui.c:1117
+#. TRANSLATORS: this is an explanation in Emergency view.
+#. * Placeholder is a service/device name
+#: ../src/gtk-ui/sync-ui.c:1392
+#, c-format
+msgid "A normal sync with %s is not possible at this time. You can do a slow two-way sync, start from scratch or restore from backup."
+msgstr "與 %s 的一般同步目前無法使用。你可以嘗試慢速雙方同步、從零開始或是從備份中還原。"
+
+#: ../src/gtk-ui/sync-ui.c:1400
+#, c-format
+msgid "If something has gone horribly wrong, you can try a slow sync, start from scratch or restore from backup."
+msgstr "如果有東西發生可怕錯誤,你可以嘗試慢速同步、從零開始或是從備份還原。"
+
+#. TRANSLATORS: These are a buttons in Emergency view. Placeholder is a
+#. * service/device name. Please don't use too long lines, but feel free to
+#. * use several lines.
+#: ../src/gtk-ui/sync-ui.c:1409
+#, c-format
+msgid ""
+"Delete all your local\n"
+"data and replace with\n"
+"data from %s"
+msgstr ""
+"刪除所有本機\n"
+"資料並使用來自\n"
+"%s 的資料取代"
+
+#: ../src/gtk-ui/sync-ui.c:1415
+#, c-format
+msgid ""
+"Delete all data on\n"
+"%s and replace\n"
+"with your local data"
+msgstr ""
+"刪除所有 %s 上的\n"
+"資料並使用你的本機\n"
+"資料取代"
+
+#: ../src/gtk-ui/sync-ui.c:1854
 msgid "Failed to get list of supported services from SyncEvolution"
 msgstr "從 SyncEvolution 取得支援的服務清單時失敗"
 
-#: ../src/gtk-ui/sync-ui.c:1221
+#: ../src/gtk-ui/sync-ui.c:1958
+#: ../src/gtk-ui/sync-ui.c:2854
+msgid "Sync failed"
+msgstr "同步失敗"
+
+#: ../src/gtk-ui/sync-ui.c:1962
 msgid "Sync complete"
 msgstr "同步完成"
 
-#: ../src/gtk-ui/sync-ui.c:1310
+#: ../src/gtk-ui/sync-ui.c:2053
 #, c-format
 msgid "Preparing '%s'"
 msgstr "正在準備 '%s'"
 
-#: ../src/gtk-ui/sync-ui.c:1313
+#: ../src/gtk-ui/sync-ui.c:2056
 #, c-format
 msgid "Receiving '%s'"
 msgstr "正在接收 '%s'"
 
-#: ../src/gtk-ui/sync-ui.c:1316
+#: ../src/gtk-ui/sync-ui.c:2059
 #, c-format
 msgid "Sending '%s'"
-msgstr "正在發送 '%s'"
+msgstr "正在傳送「%s」"
+
+#: ../src/gtk-ui/sync-ui.c:2180
+#, c-format
+msgid "There was one remote rejection."
+msgid_plural "There were %ld remote rejections."
+msgstr[0] "有過一次遠端拒絕。"
+msgstr[1] "有過 %ld 次遠端拒絕。"
+
+#: ../src/gtk-ui/sync-ui.c:2185
+#, c-format
+msgid "There was one local rejection."
+msgid_plural "There were %ld local rejections."
+msgstr[0] "有過一次本機拒絕。"
+msgstr[1] "有過 %ld 次本機拒絕。"
+
+#: ../src/gtk-ui/sync-ui.c:2190
+#, c-format
+msgid "There were %ld local rejections and %ld remote rejections."
+msgstr "有過 %ld  次本機拒絕和 %ld  次遠端拒絕。"
 
-#: ../src/gtk-ui/sync-ui.c:1705
+#: ../src/gtk-ui/sync-ui.c:2195
+#, c-format
+msgid "Last time: No changes."
+msgstr "上次:沒有變更。"
+
+#: ../src/gtk-ui/sync-ui.c:2197
+#, c-format
+msgid "Last time: Sent one change."
+msgid_plural "Last time: Sent %ld changes."
+msgstr[0] "上次:傳送了一個變更。"
+msgstr[1] "上次:傳送了 %ld 個變更。"
+
+#. This is about changes made to the local data. Not all of these
+#. changes were requested by the remote server, so "applied"
+#. is a better word than "received" (bug #5185).
+#: ../src/gtk-ui/sync-ui.c:2205
+#, c-format
+msgid "Last time: Applied one change."
+msgid_plural "Last time: Applied %ld changes."
+msgstr[0] "上次:套用了一個變更。"
+msgstr[1] "上次:套用了 %ld 個變更。"
+
+#: ../src/gtk-ui/sync-ui.c:2210
+#, c-format
+msgid "Last time: Applied %ld changes and sent %ld changes."
+msgstr "上次:套用了 %ld 個變更並傳送了 %ld 個變更。"
+
+#: ../src/gtk-ui/sync-ui.c:2711
 msgid "Waiting for current operation to finish..."
 msgstr "正在等候目前的操作完成..."
 
-#: ../src/gtk-ui/sync-ui.c:1750
-msgid "Not authorized"
-msgstr "未授權"
+#: ../src/gtk-ui/sync-ui.c:2784
+msgid "A normal sync is not possible at this time. The server suggests a slow sync, but this might not always be what you want if both ends already have data."
+msgstr "一般同步目前無法使用。這台伺服器建議使用慢速同步,但這可能不總是你想要的,因為雙方可能都已經有資料。"
 
-#: ../src/gtk-ui/sync-ui.c:1752
+#: ../src/gtk-ui/sync-ui.c:2791
+msgid "Failed to login. Could there be a problem with your username or password?"
+msgstr "登入失敗。你的使用者名稱或密碼是否有問題?"
+
+#: ../src/gtk-ui/sync-ui.c:2794
 msgid "Forbidden"
 msgstr "禁止"
 
-#: ../src/gtk-ui/sync-ui.c:1754
-msgid "Not found"
-msgstr "未找到"
+#: ../src/gtk-ui/sync-ui.c:2799
+msgid "The source could not be found. Could there be a problem with the server settings?"
+msgstr "無法找到來源。伺服器設定是否有問題?"
 
-#: ../src/gtk-ui/sync-ui.c:1756
+#: ../src/gtk-ui/sync-ui.c:2802
 msgid "Fatal database error"
 msgstr "致命數據錯誤"
 
-#: ../src/gtk-ui/sync-ui.c:1758
+#. This can happen when EDS is borked, restart it may help...
+#: ../src/gtk-ui/sync-ui.c:2805
+msgid "There is a problem with the local database. Syncing again or rebooting may help."
+msgstr "本機資料庫有問題。再次同步或是重新開機可能會有所幫助。"
+
+#: ../src/gtk-ui/sync-ui.c:2808
 msgid "Database error"
 msgstr "數據錯誤"
 
-#: ../src/gtk-ui/sync-ui.c:1760
+#: ../src/gtk-ui/sync-ui.c:2810
 msgid "No space left"
 msgstr "沒有空間"
 
-#. TODO identify problem item somehow ?
-#: ../src/gtk-ui/sync-ui.c:1763
+#: ../src/gtk-ui/sync-ui.c:2812
 msgid "Failed to process SyncML"
 msgstr "進行 SyncML 動作失敗"
 
-#: ../src/gtk-ui/sync-ui.c:1765
+#: ../src/gtk-ui/sync-ui.c:2814
 msgid "Server authorization failed"
 msgstr "伺服器授權失敗"
 
-#: ../src/gtk-ui/sync-ui.c:1767
+#: ../src/gtk-ui/sync-ui.c:2816
 msgid "Failed to parse configuration file"
 msgstr "解析設置檔案動作失敗"
 
-#: ../src/gtk-ui/sync-ui.c:1769
+#: ../src/gtk-ui/sync-ui.c:2818
 msgid "Failed to read configuration file"
 msgstr "讀取設置檔案動作失敗"
 
-#: ../src/gtk-ui/sync-ui.c:1771
+#: ../src/gtk-ui/sync-ui.c:2820
 msgid "No configuration found"
 msgstr "未找到設置"
 
-#: ../src/gtk-ui/sync-ui.c:1773
+#: ../src/gtk-ui/sync-ui.c:2822
 msgid "No configuration file found"
 msgstr "未找到設置檔案"
 
-#: ../src/gtk-ui/sync-ui.c:1775
+#: ../src/gtk-ui/sync-ui.c:2824
 msgid "Server sent bad content"
-msgstr "伺服器發送出壞的內容"
-
-#: ../src/gtk-ui/sync-ui.c:1777
-msgid "Transport failure (no connection?)"
-msgstr "傳輸失敗(沒有連線?)"
+msgstr "伺服器傳送出壞的內容"
 
-#: ../src/gtk-ui/sync-ui.c:1779
-msgid "Connection timed out"
-msgstr "連線時間已過"
-
-#: ../src/gtk-ui/sync-ui.c:1781
+#: ../src/gtk-ui/sync-ui.c:2826
 msgid "Connection certificate has expired"
 msgstr "連線憑證己過期"
 
-#: ../src/gtk-ui/sync-ui.c:1783
+#: ../src/gtk-ui/sync-ui.c:2828
 msgid "Connection certificate is invalid"
 msgstr "連線憑證無效"
 
-#: ../src/gtk-ui/sync-ui.c:1786
-msgid "Connection failed"
-msgstr "連線失敗"
+#: ../src/gtk-ui/sync-ui.c:2836
+msgid "We were unable to connect to the server. The problem could be temporary or there could be something wrong with the server settings."
+msgstr "我們無法連接到伺服器。問題可能是暫時的,或是伺服器設定可能有問題。"
 
-#: ../src/gtk-ui/sync-ui.c:1788
+#: ../src/gtk-ui/sync-ui.c:2840
 msgid "URL is bad"
 msgstr "URL 不對"
 
-#: ../src/gtk-ui/sync-ui.c:1790
+#: ../src/gtk-ui/sync-ui.c:2842
 msgid "Server not found"
 msgstr "找不到伺服器"
 
-#: ../src/gtk-ui/sync-ui.c:1792
+#: ../src/gtk-ui/sync-ui.c:2844
 #, c-format
 msgid "Error %d"
 msgstr "錯誤 %d"
 
-#. TODO show in UI: server disappeared
-#: ../src/gtk-ui/sync-ui.c:1803
-msgid "Syncevolution.Server D-Bus service exited unexpectedly"
-msgstr "Syncevlolution.Server 的 D-Bus 服務在沒有預料的情況下離開了"
-
-#: ../src/gtk-ui/sync-ui.c:1806
-msgid "Sync Failed"
-msgstr "同步失敗"
-
-#: ../src/gtk-ui/sync-ui-config.c:95
-#, c-format
-msgid "There was one remote rejection."
-msgid_plural "There were %d remote rejections."
-msgstr[0] "有一個遠端拒絕。"
-msgstr[1] "有 %d 個遠端拒絕。"
-
-#: ../src/gtk-ui/sync-ui-config.c:100
-#, c-format
-msgid "There was one local rejection."
-msgid_plural "There were %d local rejections."
-msgstr[0] "有一個本地拒絕。"
-msgstr[1] "有 %d 個本地拒絕。"
-
-#: ../src/gtk-ui/sync-ui-config.c:105
-#, c-format
-msgid "There were %d local rejections and %d remote rejections."
-msgstr "曾經有 %d 次本地拒絕和 %d 次遠端拒絕。"
-
-#: ../src/gtk-ui/sync-ui-config.c:110
-#, c-format
-msgid "Last time: No changes."
-msgstr "上次:沒有變更。"
-
-#: ../src/gtk-ui/sync-ui-config.c:112
-#, c-format
-msgid "Last time: Sent one change."
-msgid_plural "Last time: Sent %d changes."
-msgstr[0] "上一次:發送一個變更。"
-msgstr[1] "上一次:發送 %d 個變更。"
+#. title for the buttons on the right side of main view
+#: ../src/gtk-ui/ui.glade.h:2
+msgid "<b>Actions</b>"
+msgstr "<b>動作</b>"
 
-#. This is about changes made to the local data. Not all of these
-#. changes were requested by the remote server, so "applied"
-#. is a better word than "received" (bug #5185).
-#: ../src/gtk-ui/sync-ui-config.c:120
-#, c-format
-msgid "Last time: Applied one change."
-msgid_plural "Last time: Applied %d changes."
-msgstr[0] "上一次:套用了一個變更。"
-msgstr[1] "上一次:套用了 %d 個變更。"
+#. text between the two "start from scratch" buttons in emergency view
+#: ../src/gtk-ui/ui.glade.h:4
+msgid "<b>or</b>"
+msgstr "<b>或</b>"
 
-#: ../src/gtk-ui/sync-ui-config.c:125
-#, c-format
-msgid "Last time: Applied %d changes and sent %d changes."
-msgstr "上次:套用了 %d 個變更並傳送了 %d 個變更。"
+#: ../src/gtk-ui/ui.glade.h:5
+msgid "<big>Direct sync</big>"
+msgstr "<big>直接同步</big>"
 
-#: ../src/gtk-ui/ui.glade.h:1
-msgid "<b>Data</b>"
-msgstr "<b>資料</b>"
+#: ../src/gtk-ui/ui.glade.h:6
+msgid "<big>Network sync</big>"
+msgstr "<big>網路同步</big>"
 
-#: ../src/gtk-ui/ui.glade.h:2
-msgid "<b>No sync service in use</b>"
-msgstr "<b>沒有使用中的同步服務</b>"
+#. a title in emergency view
+#: ../src/gtk-ui/ui.glade.h:8
+msgid "<big>Restore from backup</big>"
+msgstr "<big>從備份還原</big>"
 
-#: ../src/gtk-ui/ui.glade.h:3
-msgid "<b>Sync failure</b>"
-msgstr "<b>同步失敗</b>"
+#. a title in emergency view
+#: ../src/gtk-ui/ui.glade.h:10
+msgid "<big>Slow sync</big>"
+msgstr "<big>慢速同步</big>"
 
-#: ../src/gtk-ui/ui.glade.h:4
-msgid "<b>Type of Sync</b>"
-msgstr "<b>同步的類型</b>"
+#. a title in emergency view
+#: ../src/gtk-ui/ui.glade.h:12
+msgid "<big>Start from scratch</big>"
+msgstr "<big>從零開始</big>"
 
-#: ../src/gtk-ui/ui.glade.h:5
-msgid "<big>Manual setup</big>"
-msgstr "<big>手動設置</big>"
+#: ../src/gtk-ui/ui.glade.h:13
+msgid ""
+"A slow sync compares items from both sides and tries to merge them. \n"
+"This may fail in some cases, leading to duplicates or lost information."
+msgstr ""
+"慢速同步會比較兩邊的項目,並嘗試合併它們。\n"
+"這在某些情況下可能會失敗,並導致副本產生或是遺失資訊。"
 
-#: ../src/gtk-ui/ui.glade.h:6
-msgid "<big>Supported services</big>"
-msgstr "<big>支援的服務</big>"
+#: ../src/gtk-ui/ui.glade.h:15
+msgid "Add new device"
+msgstr "加入新裝置"
 
-#: ../src/gtk-ui/ui.glade.h:7
+#: ../src/gtk-ui/ui.glade.h:16
 msgid "Add new service"
 msgstr "新增服務"
 
-#: ../src/gtk-ui/ui.glade.h:8
-msgid "Back to sync"
-msgstr "返回同步"
+#. explanation of "Restore backup" function
+#: ../src/gtk-ui/ui.glade.h:18
+msgid "Backups are made before every time we Sync. Choose a backup to restore. Any changes you have made since then will be lost."
+msgstr "在我們每次同步前都會進行備份。選擇要還原的備份。所有你從那時起的變更都會喪失。"
+
+#: ../src/gtk-ui/ui.glade.h:19
+msgid "Calendar"
+msgstr "行事曆"
 
-#: ../src/gtk-ui/ui.glade.h:9
+#. Button in main view, right side. Keep to below 20 chars per line, feel free to use two lines
+#: ../src/gtk-ui/ui.glade.h:21
 msgid ""
-"Change sync\n"
-"service"
-msgstr "變更同步服務"
+"Change or edit\n"
+"sync service"
+msgstr ""
+"變更或編輯\n"
+"同步服務"
 
-#: ../src/gtk-ui/ui.glade.h:11
-msgid "Delete all local data and replace it with remote data"
-msgstr "刪除所有本地資料,並使用遠端資料取代"
+#: ../src/gtk-ui/ui.glade.h:23
+msgid ""
+"Delete all data on Zyb \n"
+"and replace with your\n"
+"local information"
+msgstr "刪除所有遠端資料,並使用本機資料取代"
 
-#: ../src/gtk-ui/ui.glade.h:12
-msgid "Delete all remote data and replace it with local data"
-msgstr "刪除所有遠端資料,並使用本地資料取代"
+#: ../src/gtk-ui/ui.glade.h:26
+msgid ""
+"Delete all your local\n"
+"information and replace\n"
+"with data from Zyb"
+msgstr "刪除所有本機資料,並使用遠端資料取代"
 
-#: ../src/gtk-ui/ui.glade.h:13
-msgid "Edit service settings"
-msgstr "編輯服務設定"
+#. button in main view, right side. Keep length to 20 characters or so, use two lines if needed
+#: ../src/gtk-ui/ui.glade.h:30
+msgid ""
+"Fix a sync\n"
+"emergency"
+msgstr ""
+"修正同步\n"
+"緊急狀況"
 
-#: ../src/gtk-ui/ui.glade.h:14
+#: ../src/gtk-ui/ui.glade.h:32
 msgid ""
 "If you don't see your service above but know that your sync provider uses SyncML\n"
 "you can setup a service manually."
@@ -360,43 +521,29 @@ msgstr ""
 "如果你在上方沒有看到你的服務,但是你知道你的同步功能供應廠商使用 SyncML 的話,\n"
 "你可以手動設定服務。"
 
-#: ../src/gtk-ui/ui.glade.h:16
-msgid "Merge local and remote data (recommended)"
-msgstr "合併本地和遠端資料(建議)"
-
-#: ../src/gtk-ui/ui.glade.h:17
-msgid "Select sync service"
-msgstr "選取同步服務"
-
-#: ../src/gtk-ui/ui.glade.h:18
-msgid ""
-"Sorry, you need an internet\n"
-"connection to sync."
-msgstr ""
-"抱歉,你需要有網際網路連線\n"
-"才能同步。"
+#: ../src/gtk-ui/ui.glade.h:34
+msgid "Settings"
+msgstr "設定"
 
-#: ../src/gtk-ui/ui.glade.h:22
-msgid "Sync settings"
-msgstr "同步設定"
+#: ../src/gtk-ui/ui.glade.h:37
+msgid "Sync Emergency"
+msgstr "同步緊急狀況"
 
-#: ../src/gtk-ui/ui.glade.h:23
-msgid "Synchronization is not available (D-Bus service does not answer), sorry."
-msgstr "非常抱歉,因為 D-Bus 服務沒有回應,所以無法提供同步化。"
-
-#: ../src/gtk-ui/ui.glade.h:24
+#: ../src/gtk-ui/ui.glade.h:39
 msgid ""
 "To sync you'll need a network connection and an account with a sync service.\n"
 "We support the following services: "
 msgstr ""
-"若要同步,你需要有網路連線以及使用同步服務的帳。\n"
+"若要同步,你需要有網路連線以及使用同步服務的帳。\n"
 "我們支援下列服務:"
 
-#: ../src/gtk-ui/ui.glade.h:26
-msgid ""
-"You haven't selected a sync service yet. Sync services let you \n"
-"synchronize your data between your netbook and a web service."
-msgstr "你尚未選取同步服務。同步服務讓你在你的網路筆電與網路服務之間同步你的資料。"
+#: ../src/gtk-ui/ui.glade.h:41
+msgid "Use Bluetooth to Sync your data from one device to another."
+msgstr "使用藍牙從某裝置同步你的資料到另一個裝置去。"
+
+#: ../src/gtk-ui/ui.glade.h:42
+msgid "You will need to add Bluetooth devices before they can be synced."
+msgstr "在它們能被同步前,你需要先加入藍牙裝置。"
 
 #: ../src/gtk-ui/sync.desktop.in.h:2
 #: ../src/gtk-ui/sync-gtk.desktop.in.h:2
@@ -407,38 +554,92 @@ msgstr "已是最新"
 msgid "Sync (GTK)"
 msgstr "同步 (GTK)"
 
-#: ../src/gtk-ui/sync-config-widget.c:48
+#: ../src/gtk-ui/sync-config-widget.c:71
 msgid "ScheduleWorld enables you to keep your contacts, events, tasks, and notes in sync."
 msgstr "ScheduleWorld 可以讓你的連絡人、行事曆事項、工作、以及註記保持同步。"
 
-#: ../src/gtk-ui/sync-config-widget.c:51
-msgid "Google Sync can backup and synchronize your Address Book with your Gmail contacts."
-msgstr "Google Sync 可以讓你手中的通訊錄和 Gmail 連絡人進行備份以及同步。"
+#: ../src/gtk-ui/sync-config-widget.c:74
+msgid "Google Sync can backup and synchronize your contacts with your Gmail contacts."
+msgstr "Google Sync 可以將你的聯絡人和 Gmail 連絡人進行備份以及同步。"
 
 #. TRANSLATORS: Please include the word "demo" (or the equivalent in
 #. your language): Funambol is going to be a 90 day demo service
 #. in the future
-#: ../src/gtk-ui/sync-config-widget.c:57
-msgid "Backup your contacts and calendar. Sync with a singleclick, anytime, anywhere (DEMO)."
+#: ../src/gtk-ui/sync-config-widget.c:80
+msgid "Backup your contacts and calendar. Sync with a single click, anytime, anywhere (DEMO)."
 msgstr "備份你的連絡人與行事曆。一個按鍵就同步,任何時間、任何地點(樣本)。"
 
-#: ../src/gtk-ui/sync-config-widget.c:227
+#: ../src/gtk-ui/sync-config-widget.c:83
+msgid "Mobical Backup and Restore service allows you to securely backup your personal mobile data for free."
+msgstr "Mobical 備份與還原服務讓你可以安全地備份個人行動資料,而且免費。"
+
+#: ../src/gtk-ui/sync-config-widget.c:86
+msgid "ZYB is a simple way for people to store and share mobile information online."
+msgstr "ZYB 是人們儲存與線上分享行動資訊的一種簡單方法。"
+
+#: ../src/gtk-ui/sync-config-widget.c:89
+msgid "Memotoo lets you access your personal data from any computer connected to the Internet."
+msgstr "Memotoo 能讓你從任何連接到網路的電腦存取你的個人資料。"
+
+#: ../src/gtk-ui/sync-config-widget.c:255
+#, c-format
+msgid "Do you want to replace %s with %s? This will not remove any synced information on either end but you will no longer be able to sync with %s."
+msgstr "你想要把 %s 用 %s 取代掉嗎?這不會移除兩方任何同步過的資訊,但是你將無法再與 %s 進行同步。"
+
+#. TRANSLATORS: decline/accept buttons in warning dialog.
+#. Placeholder is service name
+#: ../src/gtk-ui/sync-config-widget.c:267
+#, c-format
+msgid "Yes, use %s"
+msgstr "是,使用 %s"
+
+#: ../src/gtk-ui/sync-config-widget.c:268
+#, c-format
+msgid "No, use %s"
+msgstr "是,使用 %s"
+
+#: ../src/gtk-ui/sync-config-widget.c:341
 msgid "Service must have a name and server URL"
 msgstr "服務項目必須要有一個名稱和伺服器網址"
 
-#: ../src/gtk-ui/sync-config-widget.c:271
+#: ../src/gtk-ui/sync-config-widget.c:381
+#, c-format
+msgid "Do you want to reset the settings for %s? This will not remove any synced information on either end."
+msgstr "你想要為 %s 重設設定嗎?這不會移除任何兩方同步過的資訊。"
+
+#. TRANSLATORS: buttons in reset-service warning dialog
+#: ../src/gtk-ui/sync-config-widget.c:385
+msgid "Yes, reset"
+msgstr "是,重設"
+
+#: ../src/gtk-ui/sync-config-widget.c:386
+#: ../src/gtk-ui/sync-config-widget.c:397
+msgid "No, keep settings"
+msgstr "不,維持設定"
+
+#: ../src/gtk-ui/sync-config-widget.c:391
+#, c-format
+msgid "Do you want to delete the settings for %s? This will not remove any synced information on either end but it will remove this service configuration."
+msgstr "你想要為 %s 刪除設定值嗎?這不會移除任何兩方同步過的資訊,但是它會移除本服務設定檔。"
+
+#. TRANSLATORS: buttons in delete-service warning dialog
+#: ../src/gtk-ui/sync-config-widget.c:396
+msgid "Yes, delete"
+msgstr "是,刪除"
+
+#: ../src/gtk-ui/sync-config-widget.c:425
 msgid "Reset service"
 msgstr "重設服務"
 
-#: ../src/gtk-ui/sync-config-widget.c:274
+#: ../src/gtk-ui/sync-config-widget.c:428
 msgid "Delete service"
 msgstr "刪除服務"
 
-#: ../src/gtk-ui/sync-config-widget.c:284
+#: ../src/gtk-ui/sync-config-widget.c:438
 msgid "Save and use"
 msgstr "儲存並使用"
 
-#: ../src/gtk-ui/sync-config-widget.c:287
+#: ../src/gtk-ui/sync-config-widget.c:441
 msgid ""
 "Save and replace\n"
 "current service"
@@ -446,58 +647,134 @@ msgstr ""
 "儲存並取代\n"
 "目前的服務"
 
-#. TRANSLATORS: label for an entry in service configuration.
-#. * Placeholder is a source  name in settings window.
-#. * Example: "Addressbook URI"
-#: ../src/gtk-ui/sync-config-widget.c:355
+#. TRANSLATORS: label for an entry in service configuration form.
+#. * Placeholder is a source  name.
+#. * Example: "Appointments URI"
+#: ../src/gtk-ui/sync-config-widget.c:672
 #, c-format
 msgid "%s URI"
 msgstr "%s URI"
 
-#. TRANSLATORS: label of a entry in service configuration
-#: ../src/gtk-ui/sync-config-widget.c:419
-msgid "Server URL"
-msgstr "伺服器 URL"
+#. TRANSLATORS: toggles in service configuration form, placeholder is service
+#. * or device name
+#: ../src/gtk-ui/sync-config-widget.c:850
+#, c-format
+msgid "Send changes to %s"
+msgstr "傳送變更到 %s"
 
-#. TRANSLATORS: this is the epander label for server settings
-#: ../src/gtk-ui/sync-config-widget.c:459
-msgid "Hide server settings"
-msgstr "隱藏伺服器設定"
+#: ../src/gtk-ui/sync-config-widget.c:854
+#, c-format
+msgid "Receive changes from %s"
+msgstr "接收來自 %s 的變更"
 
-#: ../src/gtk-ui/sync-config-widget.c:461
-msgid "Show server settings"
-msgstr "顯示伺服器設定"
+#: ../src/gtk-ui/sync-config-widget.c:869
+msgid "<b>Sync</b>"
+msgstr "<b>同步</b>"
+
+#. TRANSLATORS: label of a entry in service configuration
+#: ../src/gtk-ui/sync-config-widget.c:885
+msgid "Server address"
+msgstr "伺服器位址"
+
+#: ../src/gtk-ui/sync-config-widget.c:1067
+#, c-format
+msgid "%s - Bluetooth device"
+msgstr "%s - 藍牙裝置"
 
 #. TRANSLATORS: service title for services that are not based on a
 #. * template in service list, the placeholder is the name of the service
-#: ../src/gtk-ui/sync-config-widget.c:512
+#: ../src/gtk-ui/sync-config-widget.c:1073
 #, c-format
 msgid "%s - manually setup"
 msgstr "%s - 手動設置"
 
-#. TRANSLATORS: linkbutton label
-#: ../src/gtk-ui/sync-config-widget.c:1189
+#. TRANSLATORS: link button in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1748
 msgid "Launch website"
 msgstr "開啟網站"
 
-#. TRANSLATORS: button label
-#: ../src/gtk-ui/sync-config-widget.c:1198
+#. TRANSLATORS: button in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1757
 msgid "Setup now"
 msgstr "立刻設置"
 
-#. TRANSLATORS: labels of entries
-#: ../src/gtk-ui/sync-config-widget.c:1236
+#. TRANSLATORS: labels of entries in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1820
 msgid "Username"
 msgstr "使用者名稱"
 
-#: ../src/gtk-ui/sync-config-widget.c:1251
+#: ../src/gtk-ui/sync-config-widget.c:1835
 msgid "Password"
 msgstr "密碼"
 
-#: ../src/gtk-ui/sync-config-widget.c:1295
+#. TRANSLATORS: warning in service configuration form for people
+#. who have modified the configuration via other means.
+#: ../src/gtk-ui/sync-config-widget.c:1858
+msgid "Current service configuration is more complex than what can be shown here. Changes to sync mode or synced data types will overwrite that configuration."
+msgstr "目前的服務設定檔比這裡能夠顯示的還要複雜許多。變更為同步模式或是同步過的資料類型將會覆蓋寫入那份設定檔。"
+
+#. TRANSLATORS: this is the epander label for server settings
+#. in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1877
+msgid "Hide server settings"
+msgstr "隱藏伺服器設定"
+
+#. TRANSLATORS: this is the epander label for server settings
+#. in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1897
+msgid "Show server settings"
+msgstr "顯示伺服器設定"
+
+#. TRANSLATORS: button in service configuration form
+#: ../src/gtk-ui/sync-config-widget.c:1910
 msgid "Stop using service"
 msgstr "停止使用服務"
 
+#: ../src/gnome-bluetooth/syncevolution.c:110
+msgid "Sync in the Sync application"
+msgstr "在「同步」應用程式內同步"
+
+#~ msgid "Todo"
+#~ msgstr "要做的事"
+#~ msgid "Memo"
+#~ msgstr "備忘錄"
+#~ msgid "Failed to abort sync"
+#~ msgstr "放棄同步時失敗"
+#~ msgid "Failed to start sync"
+#~ msgstr "開始同步動作失敗"
+#~ msgid "%s (not supported by this service)"
+#~ msgstr "%s (不被此服務支援)"
+#~ msgid "Failed to get list of configured services from SyncEvolution"
+#~ msgstr "從 SyncEvolution 取得已設定的服務清單時失敗"
+#~ msgid "Not authorized"
+#~ msgstr "未授權"
+#~ msgid "Not found"
+#~ msgstr "未找到"
+#~ msgid "Transport failure (no connection?)"
+#~ msgstr "傳輸失敗(沒有連線?)"
+#~ msgid "Connection timed out"
+#~ msgstr "連線時間已過"
+#~ msgid "Connection failed"
+#~ msgstr "連線失敗"
+#~ msgid "Syncevolution.Server D-Bus service exited unexpectedly"
+#~ msgstr "Syncevlolution.Server 的 D-Bus 服務在沒有預料的情況下離開了"
+#~ msgid "<b>No sync service in use</b>"
+#~ msgstr "<b>沒有使用中的同步服務</b>"
+#~ msgid "<big>Manual setup</big>"
+#~ msgstr "<big>手動設置</big>"
+#~ msgid "Merge local and remote data (recommended)"
+#~ msgstr "合併本地和遠端資料(建議)"
+#~ msgid ""
+#~ "Sorry, you need an internet\n"
+#~ "connection to sync."
+#~ msgstr ""
+#~ "抱歉,你需要有網際網路連線\n"
+#~ "才能同步。"
+#~ msgid ""
+#~ "Synchronization is not available (D-Bus service does not answer), sorry."
+#~ msgstr "非常抱歉,因為 D-Bus 服務沒有回應,所以無法提供同步化。"
+#~ msgid "Server URL"
+#~ msgstr "伺服器 URL"
 #~ msgid "Addressbook"
 #~ msgstr "地址簿"
 #~ msgid "Failed to save current service in GConf configuration system"
@@ -530,8 +807,6 @@ msgstr "停止使用服務"
 #~ msgstr "正在結束同步"
 #~ msgid "Reset original server settings"
 #~ msgstr "重置為原始的伺服器設定"
-#~ msgid "Service name"
-#~ msgstr "服務名稱"
 #~ msgid "Bring your data with you"
 #~ msgstr "請帶攜你的數據"
 
index 9859083..7018a7c 100644 (file)
@@ -20,7 +20,10 @@ GUI_DIR = gtk-ui
 endif
 
 SUBDIRS = $(SYNTHESIS_SUBDIR) $(DBUS_DIRS) syncevo $(BACKENDS) $(GUI_DIR)
-DIST_SUBDIRS = gdbus dbus syncevo $(BACKENDS) gtk-ui
+if ENABLE_GNOME_BLUETOOTH_PANEL
+SUBDIRS += gnome-bluetooth
+endif
+DIST_SUBDIRS = gdbus dbus syncevo $(BACKENDS) gtk-ui gnome-bluetooth
 BUILT_SOURCES =
 
 AM_CPPFLAGS = $(SUBDIRS:%=-I$(srcdir)/%) -I$(srcdir)/../test -I$(top_srcdir) $(BACKEND_CPPFLAGS)
@@ -39,7 +42,7 @@ else
 SYNCEVOLUTION_LDADD += $(SYNCSOURCES)
 SYNCEVOLUTION_DEP += $(SYNCSOURCES)
 endif
-EXTRA_DIST = shlibs.local Makefile-gen.am syncclient_sample_config.xml $(service_in_files)
+EXTRA_DIST = shlibs.local Makefile-gen.am $(service_in_files)
 
 TEMPLATE_FILES = @TEMPLATE_FILES@
 templatedir = $(datadir)/syncevolution/
index d6b3e64..cf26b55 100644 (file)
@@ -20,7 +20,10 @@ GUI_DIR = gtk-ui
 endif
 
 SUBDIRS = $(SYNTHESIS_SUBDIR) $(DBUS_DIRS) syncevo $(BACKENDS) $(GUI_DIR)
-DIST_SUBDIRS = gdbus dbus syncevo $(BACKENDS) gtk-ui
+if ENABLE_GNOME_BLUETOOTH_PANEL
+SUBDIRS += gnome-bluetooth
+endif
+DIST_SUBDIRS = gdbus dbus syncevo $(BACKENDS) gtk-ui gnome-bluetooth
 BUILT_SOURCES =
 
 AM_CPPFLAGS = $(SUBDIRS:%=-I$(srcdir)/%) -I$(srcdir)/../test -I$(top_srcdir) $(BACKEND_CPPFLAGS)
@@ -39,7 +42,7 @@ else
 SYNCEVOLUTION_LDADD += $(SYNCSOURCES)
 SYNCEVOLUTION_DEP += $(SYNCSOURCES)
 endif
-EXTRA_DIST = shlibs.local Makefile-gen.am syncclient_sample_config.xml $(service_in_files)
+EXTRA_DIST = shlibs.local Makefile-gen.am $(service_in_files)
 
 TEMPLATE_FILES = templates/README templates/servers/Funambol/config.ini templates/servers/Funambol/template.ini templates/servers/Funambol/sources/calendar/config.ini templates/servers/Funambol/sources/todo/config.ini templates/servers/Funambol/sources/addressbook/config.ini templates/servers/Funambol/sources/memo/config.ini templates/servers/ScheduleWorld/config.ini templates/servers/ScheduleWorld/template.ini templates/servers/ScheduleWorld/sources/calendar/config.ini templates/servers/ScheduleWorld/sources/todo/config.ini templates/servers/ScheduleWorld/sources/addressbook/config.ini templates/servers/ScheduleWorld/sources/memo/config.ini templates/clients/phone/nokia/S40/7210c/config.ini templates/clients/phone/nokia/S40/7210c/template.ini templates/clients/phone/nokia/S40/7210c/sources/calendar/config.ini templates/clients/phone/nokia/S40/7210c/sources/calendar+todo/config.ini templates/clients/phone/nokia/S40/7210c/sources/todo/config.ini templates/clients/phone/nokia/S40/7210c/sources/addressbook/config.ini templates/clients/phone/nokia/S40/7210c/sources/memo/config.ini templates/clients/default/template.ini templates/clients/SyncEvolution/config.ini templates/clients/SyncEvolution/template.ini templates/clients/SyncEvolution/sources/calendar/config.ini templates/clients/SyncEvolution/sources/todo/config.ini templates/clients/SyncEvolution/sources/addressbook/config.ini templates/clients/SyncEvolution/sources/memo/config.ini
 templatedir = $(datadir)/syncevolution/
index 16d2dfb..13afd29 100644 (file)
@@ -34,25 +34,26 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
+@ENABLE_GNOME_BLUETOOTH_PANEL_TRUE@am__append_1 = gnome-bluetooth
 EXTRA_PROGRAMS = $(am__EXEEXT_1) abort-redirect$(EXEEXT)
 TESTS = $(am__EXEEXT_2)
 bin_PROGRAMS = syncevolution$(EXEEXT) $(am__EXEEXT_2)
 @COND_DBUS_TRUE@libexec_PROGRAMS = syncevo-dbus-server$(EXEEXT)
-@ENABLE_MODULES_FALSE@am__append_1 = $(SYNCSOURCES)
 @ENABLE_MODULES_FALSE@am__append_2 = $(SYNCSOURCES)
-@ENABLE_MODULES_TRUE@am__append_3 = $(BACKEND_REGISTRIES)
-@ENABLE_MODULES_FALSE@am__append_4 = $(BACKEND_REGISTRIES)
+@ENABLE_MODULES_FALSE@am__append_3 = $(SYNCSOURCES)
+@ENABLE_MODULES_TRUE@am__append_4 = $(BACKEND_REGISTRIES)
+@ENABLE_MODULES_FALSE@am__append_5 = $(BACKEND_REGISTRIES)
 
 # distribute test system?
 # yes: install client-test in bindir, test files in datadir
-@ENABLE_TESTING_TRUE@am__append_5 = client-test
 @ENABLE_TESTING_TRUE@am__append_6 = client-test
+@ENABLE_TESTING_TRUE@am__append_7 = client-test
 # The "all" dependency causes a rebuild even if the actual input files
 # haven't changed. If client-test is part of the regular targets built
 # by "all", then it must not depend on all!
-@ENABLE_TESTING_FALSE@am__append_7 = client-test
-@ENABLE_TESTING_FALSE@am__append_8 = $(CLIENT_LIB_TEST_FILES)
-@ENABLE_TESTING_FALSE@am__append_9 = all
+@ENABLE_TESTING_FALSE@am__append_8 = client-test
+@ENABLE_TESTING_FALSE@am__append_9 = $(CLIENT_LIB_TEST_FILES)
+@ENABLE_TESTING_FALSE@am__append_10 = all
 subdir = src
 DIST_COMMON = $(am__nobase_dist_doc_DATA_DIST) \
        $(nobase_dist_template_DATA) $(srcdir)/Makefile.am \
@@ -303,11 +304,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -337,6 +343,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -356,6 +364,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
@@ -378,7 +388,7 @@ SQLITE_LIBS = @SQLITE_LIBS@
 STABLE_VERSION = @STABLE_VERSION@
 STRIP = @STRIP@
 SYNCEVOLUTION_CXXFLAGS = @SYNCEVOLUTION_CXXFLAGS@
-SYNCEVOLUTION_LDADD = @SYNCEVOLUTION_LDADD@ $(am__append_1)
+SYNCEVOLUTION_LDADD = @SYNCEVOLUTION_LDADD@ $(am__append_2)
 SYNCEVOLUTION_LOCALEDIR = @SYNCEVOLUTION_LOCALEDIR@
 SYNCSOURCES = @SYNCSOURCES@
 SYNTHESIS = @SYNTHESIS@
@@ -461,14 +471,15 @@ service_in_files = org.syncevolution.service.in
 @COND_DBUS_FALSE@@COND_GUI_TRUE@DBUS_DIRS = dbus/interfaces
 @COND_DBUS_TRUE@DBUS_DIRS = gdbus dbus
 @COND_GUI_TRUE@GUI_DIR = gtk-ui
-SUBDIRS = $(SYNTHESIS_SUBDIR) $(DBUS_DIRS) syncevo $(BACKENDS) $(GUI_DIR)
-DIST_SUBDIRS = gdbus dbus syncevo $(BACKENDS) gtk-ui
+SUBDIRS = $(SYNTHESIS_SUBDIR) $(DBUS_DIRS) syncevo $(BACKENDS) \
+       $(GUI_DIR) $(am__append_1)
+DIST_SUBDIRS = gdbus dbus syncevo $(BACKENDS) gtk-ui gnome-bluetooth
 BUILT_SOURCES = 
 AM_CPPFLAGS = $(SUBDIRS:%=-I$(srcdir)/%) -I$(srcdir)/../test -I$(top_srcdir) $(BACKEND_CPPFLAGS)
 bin_SCRIPTS = synccompare
-SYNCEVOLUTION_DEP = $(am__append_2)
-EXTRA_DIST = shlibs.local Makefile-gen.am syncclient_sample_config.xml \
-       $(service_in_files) $(am__append_3)
+SYNCEVOLUTION_DEP = $(am__append_3)
+EXTRA_DIST = shlibs.local Makefile-gen.am $(service_in_files) \
+       $(am__append_4)
 TEMPLATE_FILES = templates/README \
        templates/servers/Funambol/config.ini \
        templates/servers/Funambol/template.ini \
@@ -502,7 +513,7 @@ DISTCLEANFILES = synccompare
 MAINTAINERCLEANFILES = Makefile.in
 CLEANFILES = libstdc++.a client-test $(CLIENT_LIB_TEST_FILES) \
        org.syncevolution.service abort-redirect.log
-CORE_SOURCES = $(am__append_4)
+CORE_SOURCES = $(am__append_5)
 
 # The files which register backends have to be compiled into
 # "client-test" and "syncevolution" in order to pull in the
@@ -555,7 +566,7 @@ client_test_SOURCES = \
        ../test/client-test-main.cpp \
        $(CORE_SOURCES)
 
-nodist_client_test_SOURCES = ../test/test.cpp $(am__append_8)
+nodist_client_test_SOURCES = ../test/test.cpp $(am__append_9)
 
 # list of test file base files
 #
@@ -599,7 +610,7 @@ client_test_LDADD = $(CORE_LDADD) $(SYNTHESIS_ENGINE)
 # runs the binary with out-dated auxiliary files.
 client_test_DEPENDENCIES = $(EXTRA_LTLIBRARIES) $(CORE_DEP) \
        $(CLIENT_LIB_TEST_FILES) testcase2patch synccompare templates \
-       $(am__append_9)
+       $(am__append_10)
 @ENABLE_TESTING_TRUE@nobase_dist_doc_DATA = $(CLIENT_LIB_TEST_FILES)
 abort_redirect_SOURCES = ../test/abort-redirect.cpp
 abort_redirect_CPPFLAGS = -DHAVE_CONFIG_H $(AM_CPPFLAGS)
index 360e866..eab241f 100644 (file)
@@ -148,11 +148,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -182,6 +187,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -201,6 +208,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index dc42ae9..8c7a8b8 100644 (file)
@@ -209,6 +209,15 @@ void EvolutionCalendarSource::open()
                            (void *)"Evolution Data Server has died unexpectedly, database no longer available.");
 }
 
+bool EvolutionCalendarSource::isEmpty()
+{
+    // TODO: add more efficient implementation which does not
+    // depend on actually pulling all items from EDS
+    RevisionMap_t revisions;
+    listAllItems(revisions);
+    return revisions.empty();
+}
+
 void EvolutionCalendarSource::listAllItems(RevisionMap_t &revisions)
 {
     GError *gerror = NULL;
@@ -555,6 +564,27 @@ void EvolutionCalendarSource::removeItem(const string &luid)
         }
     }
     m_allLUIDs.erase(luid);
+
+    if (!id.m_rid.empty()) {
+        // Removing the child may have modified the parent.
+        // We must record the new LAST-MODIFIED string,
+        // otherwise it might be reported as modified during
+        // the next sync (timing dependent: if the parent
+        // was updated before removing the child *and* the
+        // update and remove fall into the same second,
+        // then the modTime does not change again during the
+        // removal).
+        try {
+            ItemID parent(id.m_uid, "");
+            string modTime = getItemModTime(parent);
+            string parentLUID = parent.getLUID();
+            updateRevision(getTrackingNode(), parentLUID, parentLUID, modTime);
+        } catch (...) {
+            // There's no guarantee that the parent still exists.
+            // Instead of checking that, ignore errors (a bit hacky,
+            // but better than breaking the removal).
+        }
+    }
 }
 
 icalcomponent *EvolutionCalendarSource::retrieveItem(const ItemID &id)
index d7da566..f86ef9f 100644 (file)
@@ -60,6 +60,7 @@ class EvolutionCalendarSource : public EvolutionSyncSource,
     //
     virtual Databases getDatabases();
     virtual void open();
+    virtual bool isEmpty();
     virtual void close(); 
     virtual const char *getMimeType() const { return "text/calendar"; }
     virtual const char *getMimeVersion() const { return "2.0"; }
index eccca34..42c0296 100644 (file)
@@ -38,8 +38,10 @@ static SyncSource *createSource(const SyncSourceParams &params)
 
     isMe = sourceType.m_backend == "Evolution Task List";
     if (isMe || sourceType.m_backend == "todo") {
-        if (sourceType.m_format == "" || sourceType.m_format == "text/calendar" 
-                || sourceType.m_format == "text/x-vcalendar") { 
+        if (sourceType.m_format == "" ||
+            sourceType.m_format == "text/calendar" ||
+            sourceType.m_format == "text/x-calendar" ||
+            sourceType.m_format == "text/x-vcalendar") {
             return
 #ifdef ENABLE_ECAL
                 enabled ? new EvolutionCalendarSource(E_CAL_SOURCE_TYPE_TODO, params) :
@@ -69,8 +71,10 @@ static SyncSource *createSource(const SyncSourceParams &params)
 
     isMe = sourceType.m_backend == "Evolution Calendar";
     if (isMe || sourceType.m_backend == "calendar") {
-        if (sourceType.m_format == "" || sourceType.m_format == "text/calendar" ||
-            sourceType.m_format == "text/x-vcalendar" /* this is for backwards compatibility with broken configs */ ) {
+        if (sourceType.m_format == "" ||
+            sourceType.m_format == "text/calendar" ||
+            sourceType.m_format == "text/x-calendar" ||
+            sourceType.m_format == "text/x-vcalendar") {
             return
 #ifdef ENABLE_ECAL
                 enabled ? new EvolutionCalendarSource(E_CAL_SOURCE_TYPE_EVENT, params) :
@@ -93,14 +97,14 @@ static RegisterSyncSource registerMe("Evolution Calendar/Task List/Memos",
                                      createSource,
                                      "Evolution Calendar = calendar = events = evolution-events\n"
                                      "   iCalendar 2.0 (default) = text/calendar\n"
-                                     "   vCalendar 1.0 = text/x-calendar\n"
+                                     "   vCalendar 1.0 = text/x-vcalendar\n"
                                      "Evolution Task List = Evolution Tasks = todo = tasks = evolution-tasks\n"
                                      "   iCalendar 2.0 (default) = text/calendar\n"
-                                     "   vCalendar 1.0 = text/x-calendar\n"
+                                     "   vCalendar 1.0 = text/x-vcalendar\n"
                                      "Evolution Memos = memo = memos = evolution-memos\n"
                                      "   plain text in UTF-8 (default) = text/plain\n"
                                      "   iCalendar 2.0 = text/calendar\n"
-                                     "   vCalendar 1.0 = text/x-calendar\n"
+                                     "   vCalendar 1.0 = text/x-vcalendar\n"
                                      "   The later format is not tested because none of the\n"
                                      "   supported SyncML servers accepts it.\n",
                                      Values() +
index 20e413e..4b59d47 100644 (file)
@@ -84,7 +84,6 @@ EvolutionSyncSource::Databases EvolutionContactSource::getDatabases()
 
     Databases secondary;
     Databases result;
-    bool first = true;
     for (GSList *g = e_source_list_peek_groups (sources); g; g = g->next) {
         ESourceGroup *group = E_SOURCE_GROUP (g->data);
         for (GSList *s = e_source_group_peek_sources (group); s; s = s->next) {
@@ -96,7 +95,7 @@ EvolutionSyncSource::Databases EvolutionContactSource::getDatabases()
             }
             Database entry(e_source_peek_name(source),
                            uristr,
-                           first);
+                           false);
             if (boost::starts_with(uristr, "couchdb://")) {
                 // Append CouchDB address books at the end of the list,
                 // otherwise preserving the order of address books.
@@ -115,7 +114,6 @@ EvolutionSyncSource::Databases EvolutionContactSource::getDatabases()
             } else {
                 result.push_back(entry);
             }
-            first = false;
         }
     }
     result.insert(result.end(), secondary.begin(), secondary.end());
@@ -139,6 +137,9 @@ EvolutionSyncSource::Databases EvolutionContactSource::getDatabases()
             const char *uri = e_book_get_uri (book);
             result.push_back(Database(name, uri, true));
         }
+    } else {
+        //  the first DB found is the default
+        result[0].m_isDefault = true;
     }
     
     return result;
@@ -218,6 +219,14 @@ void EvolutionContactSource::open()
                            (void *)"Evolution Data Server has died unexpectedly, contacts no longer available.");
 }
 
+bool EvolutionContactSource::isEmpty()
+{
+    // TODO: add more efficient implementation which does not
+    // depend on actually pulling all items from EDS
+    RevisionMap_t revisions;
+    listAllItems(revisions);
+    return revisions.empty();
+}
 
 void EvolutionContactSource::listAllItems(RevisionMap_t &revisions)
 {
index c83abf4..3278fc1 100644 (file)
@@ -51,6 +51,7 @@ class EvolutionContactSource : public EvolutionSyncSource,
     //
     virtual Databases getDatabases();
     virtual void open();
+    virtual bool isEmpty();
     virtual void close();
     virtual const char *getMimeType() const;
     virtual const char *getMimeVersion() const;
index d2aa20b..5d3a2d6 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include "EvolutionSyncSource.h"
+#include <syncevo/SmartPtr.h>
 
 #include <syncevo/declarations.h>
 SE_BEGIN_CXX
@@ -27,16 +28,30 @@ SE_BEGIN_CXX
 
 ESource *EvolutionSyncSource::findSource( ESourceList *list, const string &id )
 {
+    string finalID;
+    if (!id.empty()) {
+        finalID = id;
+    } else {
+        // Nothing selected specifically, use the one marked as default
+        // by the backend. If none is marked, use the first one
+        // (not expected to happen, though).
+        BOOST_FOREACH(const Database &db, getDatabases()) {
+            if (db.m_isDefault) {
+                finalID = db.m_uri;
+                break;
+            }
+        }
+    }
+
     for (GSList *g = e_source_list_peek_groups (list); g; g = g->next) {
         ESourceGroup *group = E_SOURCE_GROUP (g->data);
         GSList *s;
         for (s = e_source_group_peek_sources (group); s; s = s->next) {
             ESource *source = E_SOURCE (s->data);
-            char *uri = e_source_get_uri(source);
-            bool found = id.empty() ||
-                !id.compare(e_source_peek_name(source)) ||
-                (uri && !id.compare(uri));
-            g_free(uri);
+            GString uri(e_source_get_uri(source));
+            bool found = finalID.empty() ||
+                !finalID.compare(e_source_peek_name(source)) ||
+                (uri && !finalID.compare(uri));
             if (found) {
                 return source;
             }
index 4ac4ed8..ae04511 100644 (file)
@@ -172,11 +172,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -206,6 +211,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -225,6 +232,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index cb3a39b..604393d 100644 (file)
@@ -81,6 +81,11 @@ else
         ECAL_LIBS=
 fi
 
+PKG_CHECK_MODULES(LIBICAL_AVAILABLE,
+                  libical >= 0.43,
+                  AC_DEFINE(HAVE_LIBICAL_R, 1, [have recent enough libical with _r variants]),
+                  pass)
+
 if test "$enable_evo" = "yes"; then
         need_glib="yes"
         if test "$EDSFOUND" = "yes"; then
index 394b9ac..51385e4 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include "libical/icalstrdup.h"
+#include <syncevo/eds_abi_wrapper.h>
 
 #if !defined(LIBICAL_MEMFIXES) || defined(EVOLUTION_COMPATIBILITY)
 
@@ -35,6 +36,9 @@
 char *ical_strdup(const char *x)
 {
 #ifdef LIBICAL_RUNTIME_CHECK
+    // One situation when we must not dup strings is when
+    // running with a libecal with the modified string
+    // handling semantic. Check that here.
     static enum {
         PATCH_UNCHECKED,
         PATCH_FOUND,
@@ -52,6 +56,20 @@ char *ical_strdup(const char *x)
     }
 #endif
 
+#ifdef EVOLUTION_COMPATIBILITY
+    // Another situation is when we have a libical with the
+    // _r variants of the relevant calls. We can call that directly,
+    // which has the advantage that we get the saner implementation.
+    // There have been crashes on Debian testing with libical 0.43-2
+    // inside icalmemory_add_tmp_buffer/icaltime_as_ical_string.
+    //
+    // We assume here that if one _r variant was found, all of
+    // them are found. See also the wrappers in eds_abi_wrapper.h.
+    if (EDSAbiWrapperSingleton.icalcomponent_as_ical_string_r) {
+        return (char *)x;
+    }
+#endif
+
     return x ? strdup(x) : NULL;
 }
 
index a235850..ea2e6f5 100644 (file)
@@ -25,6 +25,8 @@
 #endif
 #include <libical/ical.h>
 
+#include <syncevo/eds_abi_wrapper.h>
+
 #ifdef __cplusplus
 extern "C" {
 #pragma }
index 07765f0..3279145 100644 (file)
 #include <unistd.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <dirent.h>
 
 #include <syncevo/util.h>
 
 #include <sstream>
 #include <fstream>
 
+#include <syncevo/SyncContext.h>
 #include <syncevo/declarations.h>
 SE_BEGIN_CXX
 
@@ -101,6 +103,40 @@ void FileSyncSource::open()
     m_basedir = basedir;
 }
 
+bool FileSyncSource::isEmpty()
+{
+    DIR *dir = NULL;
+    bool empty = true;
+
+    try {
+        dir = opendir(m_basedir.c_str());
+        if (!dir) {
+            SyncContext::throwError(m_basedir, errno);
+        }
+        errno = 0;
+        struct dirent *entry = readdir(dir);
+        while (entry) {
+            if (strcmp(entry->d_name, ".") &&
+                strcmp(entry->d_name, "..")) {
+                empty = false;
+                break;
+            }
+            entry = readdir(dir);
+        }
+        if (errno) {
+            SyncContext::throwError(m_basedir, errno);
+        }
+    } catch(...) {
+        if (dir) {
+            closedir(dir);
+        }
+        throw;
+    }
+
+    closedir(dir);
+    return empty;
+}
+
 void FileSyncSource::close()
 {
     m_basedir.clear();
index c674490..7bf9f78 100644 (file)
@@ -64,6 +64,7 @@ class FileSyncSource : public TrackingSyncSource, private boost::noncopyable
  protected:
     /* implementation of SyncSource interface */
     virtual void open();
+    virtual bool isEmpty();
     virtual void close();
     virtual Databases getDatabases();
     virtual const char *getMimeType() const;
index b687a2e..0881629 100644 (file)
@@ -145,11 +145,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -179,6 +184,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -198,6 +205,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index de56e92..10a5756 100644 (file)
@@ -103,6 +103,14 @@ void MaemoCalendarSource::open()
     conv->setSyncing(true); // not sure what this does, but may as well tell the truth
 }
 
+bool MaemoCalendarSource::isEmpty()
+{
+    // TODO: provide a real implementation. Always returning false
+    // here disables the "allow slow sync when no local data" heuristic
+    // for preventSlowSync=1.
+    return false;
+}
+
 void MaemoCalendarSource::close()
 {
     delete conv;
index af500a2..573215e 100644 (file)
@@ -51,6 +51,7 @@ class MaemoCalendarSource : public TrackingSyncSource, private boost::noncopyabl
  protected:
     /* implementation of SyncSource interface */
     virtual void open();
+    virtual bool isEmpty();
     virtual void close();
     virtual Databases getDatabases();
     virtual const char *getMimeType() const;
index ab31e0c..d407307 100644 (file)
@@ -146,11 +146,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -180,6 +185,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -199,6 +206,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index 8a9cd7a..6cf4f9b 100644 (file)
@@ -146,11 +146,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -180,6 +185,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -199,6 +206,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index 6853116..c8399b0 100644 (file)
@@ -214,6 +214,17 @@ SQLiteContactSource::Databases SQLiteContactSource::getDatabases()
     return result;
 }
 
+bool SQLiteContactSource::isEmpty()
+{
+    // there are probably more efficient ways to do this, but this is just
+    // a proof-of-concept anyway
+    sqliteptr all(m_sqlite.prepareSQL("SELECT ROWID FROM ABPerson;"));
+    while (m_sqlite.checkSQL(sqlite3_step(all)) == SQLITE_ROW) {
+        return false;
+    }
+    return true;
+}
+
 void SQLiteContactSource::listAllItems(RevisionMap_t &revisions)
 {
     sqliteptr all(m_sqlite.prepareSQL("SELECT ROWID, CreationDate, ModificationDate FROM ABPerson;"));
index 759b31a..622ad4d 100644 (file)
@@ -70,6 +70,7 @@ class SQLiteContactSource : public SyncSource,
             SyncSourceRevisions::init(NULL, NULL, 1, m_operations);
             SyncSourceChanges::init(m_operations);
 
+            m_operations.m_isEmpty = boost::bind(&SQLiteContactSource::isEmpty, this);
             m_operations.m_readItemAsKey = boost::bind(&SQLiteContactSource::readItemAsKey, this, _1, _2);
             m_operations.m_insertItemAsKey = boost::bind(&SQLiteContactSource::insertItemAsKey, this, _1, (sysync::cItemID)NULL, _2);
             m_operations.m_updateItemAsKey = boost::bind(&SQLiteContactSource::insertItemAsKey, this, _1, _2, _3);
@@ -105,6 +106,9 @@ class SQLiteContactSource : public SyncSource,
     /** encapsulates access to database */
     boost::shared_ptr<ConfigNode> m_trackingNode;
     SQLiteUtil m_sqlite;
+
+    /** implements the m_isEmpty operation */
+    bool isEmpty();
 };
 
 #endif // ENABLE_SQLITE
index 6df48ad..6956704 100644 (file)
@@ -145,11 +145,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -179,6 +184,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -198,6 +205,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index c414cab..8d56c31 100644 (file)
@@ -64,6 +64,14 @@ void XMLRPCSyncSource::open()
 {
 }
 
+bool XMLRPCSyncSource::isEmpty()
+{
+    // TODO: provide a real implementation. Always returning false
+    // here disables the "allow slow sync when no local data" heuristic
+    // for preventSlowSync=1.
+    return false;
+}
+
 void XMLRPCSyncSource::close()
 {
 }
index b84c34d..de9a43b 100644 (file)
@@ -41,6 +41,7 @@ class XMLRPCSyncSource : public TrackingSyncSource
  protected:
     /* implementation of SyncSource interface */
     virtual void open();
+    virtual bool isEmpty();
     virtual void close();
     virtual Databases getDatabases();
     virtual const char *getMimeType() const;
index 53187cb..705f66f 100644 (file)
@@ -68,7 +68,7 @@ static RegisterSyncSource registerMe("XMLRPC interface for data exchange",
                                      "      xmlrpc:text/plain:1.0\n"
                                      "      xmlrpc:text/x-vcard:2.1\n"
                                      "      xmlrpc:text/vcard:3.0\n"
-                                     "      xmlrpc:text/x-calendar:1.0\n"
+                                     "      xmlrpc:text/x-vcalendar:1.0\n"
                                      "      xmlrpc:text/calendar:2.0\n",
                                      Values() +
                                      (Aliases("XMLRPC interface") + "xmlrpc"));
index 37a2233..48e0d67 100644 (file)
@@ -174,11 +174,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -208,6 +213,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -227,6 +234,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index b85e75b..0cafde9 100644 (file)
@@ -102,11 +102,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -136,6 +141,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -155,6 +162,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index 1fc1368..24d4e1b 100644 (file)
@@ -2,6 +2,6 @@ VOID:STRING,STRING,INT,INT,INT,INT
 VOID:STRING,STRING
 VOID:STRING,BOOLEAN
 VOID:STRING,STRING,STRING
-VOID:STRING,STRING,STRING,STRING,STRING
+VOID:STRING,STRING,STRING,STRING,STRING,BOXED
 VOID:INT,BOXED
 VOID:UINT,UINT,BOXED
index f034e42..0fee97e 100644 (file)
     <method name ="GetConfigs">
       <doc:doc><doc:description>
         Get an array of all configured servers (or templates)
+
+        In getting templates mode, the dbus server checks the paired devices
+        from bluez and returns the matched templates. Templates for each device
+        are re-named with 'Bluetooth_&lt;mac address&gt;_&lt;number&gt;', where number
+        enumerates the templates created for the device.
+
+        Get these templates latter when calling GetConfig with additional new
+        properties: 
+            description - the description for the template
+            score - the calculated score based on the device name and template
+            deviceName - the device name that the template is for
+            fingerPrint - the fingerPrint of the template, used for dbus
+                          clients to re-match with user input device info
+        Also a property value is changed: 'syncURL' is replaced with the device 
+        mac address.
       </doc:description></doc:doc>
       <arg type="b" name="template" direction="in">
         <doc:doc><doc:summary>
       </arg>
     </signal>
 
+    <signal name="TemplatesChanged">
+      <doc:doc>
+        <doc:description>
+          Template added or removed, for example because a Bluetooth
+          peer was paired resp. removed. To find out more, request
+          list of templates anew.
+      </doc:description>
+      </doc:doc>
+    </signal>
+
     <signal name="Presence">
       <doc:doc>
         <doc:description>
index a664896..684e04e 100644 (file)
@@ -35,6 +35,8 @@ syncevo_config_get_value (SyncevoConfig *config,
     g_return_val_if_fail (config, FALSE);
     g_return_val_if_fail (value, FALSE);
 
+    *value = NULL;
+
     if (!source || strlen (source) == 0) {
         name = g_strdup ("");
     } else {
@@ -45,10 +47,9 @@ syncevo_config_get_value (SyncevoConfig *config,
     g_free (name);
 
     if (source_config) {
-        *value = (char*)g_hash_table_lookup (source_config, key);
-        return TRUE;
+        return g_hash_table_lookup_extended (source_config, key,
+                                             NULL, (gpointer*)value);
     }
-    
     return FALSE;
 }
 
@@ -117,7 +118,9 @@ void
 syncevo_config_free (SyncevoConfig *config)
 {
     /* NOTE: Hashtables gcreated by dbus-glib should free their contents */
-    g_hash_table_destroy (config);
+    if (config) {
+        g_hash_table_destroy (config);
+    }
 }
 
 const char*
@@ -210,16 +213,19 @@ syncevo_session_status_from_string (const char *status_str)
         status = SYNCEVO_STATUS_UNKNOWN;
     }
 
+    if (status_str && strstr (status_str, ";waiting")) {
+        status |= SYNCEVO_STATUS_WAITING;
+    }
+
     return status;
 }
 
 SyncevoSyncMode
 syncevo_sync_mode_from_string (const char *mode_str)
 {
-    if (!mode_str) {
-        return SYNCEVO_SYNC_UNKNOWN;
-    } else if (g_str_has_prefix (mode_str, "none") ||
-               g_str_has_prefix (mode_str, "disabled")) {
+    if (!mode_str ||
+        g_str_has_prefix (mode_str, "none") ||
+        g_str_has_prefix (mode_str, "disabled")) {
         return SYNCEVO_SYNC_NONE;
     } else if (g_str_has_prefix (mode_str, "two-way")) {
         return SYNCEVO_SYNC_TWO_WAY;
@@ -238,26 +244,26 @@ syncevo_sync_mode_from_string (const char *mode_str)
     }
 }
 
-static SyncevoSourceStatus
+static SyncevoSessionStatus
 syncevo_source_status_from_string (const char *status_str)
 {
-    SyncevoSourceStatus status;
+    SyncevoSessionStatus status;
 
     if (!status_str) {
-        status = SYNCEVO_SOURCE_UNKNOWN;
+        status = SYNCEVO_STATUS_UNKNOWN;
     } else if (g_str_has_prefix (status_str, "idle")) {
-        status = SYNCEVO_SOURCE_IDLE;
+        status = SYNCEVO_STATUS_IDLE;
     } else if (g_str_has_prefix (status_str, "running")) {
-        status = SYNCEVO_SOURCE_RUNNING;
+        status = SYNCEVO_STATUS_RUNNING;
     } else if (g_str_has_prefix (status_str, "done")) {
-        status = SYNCEVO_SOURCE_DONE;
+        status = SYNCEVO_STATUS_DONE;
     } else {
-        status = SYNCEVO_SOURCE_UNKNOWN;
+        status = SYNCEVO_STATUS_UNKNOWN;
     }
 
     /* check modifiers */
     if (status_str && strstr (status_str, ";waiting")) {
-        status |= SYNCEVO_SOURCE_WAITING;
+        status |= SYNCEVO_STATUS_WAITING;
     }
 
     return status;
@@ -280,7 +286,7 @@ syncevo_source_statuses_foreach (SyncevoSourceStatuses *source_statuses,
         const char *mode_str;
         const char *status_str;
         SyncevoSyncMode mode;
-        SyncevoSourceStatus status;
+        SyncevoSessionStatus status;
         guint error_code;
 
         mode_str = g_value_get_string (g_value_array_get_nth (source_status, 0));
@@ -312,23 +318,21 @@ syncevo_source_statuses_free (SyncevoSourceStatuses *source_statuses)
     g_hash_table_destroy (source_statuses);
 }
 
-/* The return value contents are only valid as long as the
- * SyncevoSourceProgresses is. */
-SyncevoSourceProgress*
-syncevo_source_progresses_get_current (SyncevoSourceProgresses *source_progresses)
+void
+syncevo_source_progresses_foreach (SyncevoSourceProgresses *source_progresses,
+                                   SourceProgressFunc func,
+                                   gpointer userdata)
 {
     const char *phase_str, *name;
     GHashTableIter iter;
     GValueArray *progress_array;
-    GValue *val;
-    SyncevoSourceProgress *progress = NULL;
 
-    g_return_val_if_fail (source_progresses, FALSE);
+    g_return_if_fail (source_progresses);
 
     g_hash_table_iter_init (&iter, source_progresses);
     while (g_hash_table_iter_next (&iter, (gpointer)&name, (gpointer)&progress_array)) {
         SyncevoSourcePhase phase;
-        phase_str = g_value_get_string (g_value_array_get_nth (progress_array, 0));
+        phase_str = g_value_get_string (g_value_array_get_nth (progress_array, 0));    
 
         if (!phase_str) {
             phase = SYNCEVO_PHASE_NONE;
@@ -342,14 +346,7 @@ syncevo_source_progresses_get_current (SyncevoSourceProgresses *source_progresse
             phase = SYNCEVO_PHASE_NONE;
         }
 
-        if (phase == SYNCEVO_PHASE_NONE) {
-            continue;
-        }
-
-        progress = g_slice_new (SyncevoSourceProgress);
-        progress->name = g_strdup (name);
-        progress->phase = phase;
-
+/*
         val = g_value_array_get_nth (progress_array, 1);
         progress->prepare_current = g_value_get_int (val);
         val = g_value_array_get_nth (progress_array, 2);
@@ -362,18 +359,12 @@ syncevo_source_progresses_get_current (SyncevoSourceProgresses *source_progresse
         progress->receive_current = g_value_get_int (val);
         val = g_value_array_get_nth (progress_array, 6);
         progress->receive_total = g_value_get_int (val);
+*/
 
-        break;
+        func (name, phase, userdata);
     }
-    return progress;
 }
 
-void
-syncevo_source_progress_free (SyncevoSourceProgress *progress)
-{
-    g_free (progress->name);
-    g_slice_free (SyncevoSourceProgress, progress);
-}
 static void
 free_source_progress_item (char *source,
                            GValueArray *progress_array)
index d138f4c..c303214 100644 (file)
@@ -40,28 +40,20 @@ typedef enum {
   SYNCEVO_SYNC_ONE_WAY_FROM_SERVER,
 } SyncevoSyncMode;
 
+/* SyncevoSessionStatus is a bitfield, although most value are exclusive */
 typedef enum {
-  SYNCEVO_STATUS_UNKNOWN,
-  SYNCEVO_STATUS_QUEUEING,
-  SYNCEVO_STATUS_IDLE,
-  SYNCEVO_STATUS_RUNNING,
-  SYNCEVO_STATUS_ABORTING,
-  SYNCEVO_STATUS_SUSPENDING,
-  SYNCEVO_STATUS_DONE,
-} SyncevoSessionStatus;
-
-
-/* SyncevoSourceStatus is a bitfield, but only one of four first values 
- * will be present */
-typedef enum {
-  SYNCEVO_SOURCE_UNKNOWN = 0,
-  SYNCEVO_SOURCE_IDLE = 1 << 0,
-  SYNCEVO_SOURCE_RUNNING = 1 << 1,
-  SYNCEVO_SOURCE_DONE = 1 << 2,
+  SYNCEVO_STATUS_UNKNOWN = 0,
+  SYNCEVO_STATUS_QUEUEING = 1 << 0,
+  SYNCEVO_STATUS_IDLE = 1 << 1,
+  SYNCEVO_STATUS_RUNNING = 1 << 2,
+  SYNCEVO_STATUS_ABORTING = 1 << 3,
+  SYNCEVO_STATUS_SUSPENDING = 1 << 4,
+  SYNCEVO_STATUS_DONE = 1 << 5,
 
   /* the ones below are modifiers */
-  SYNCEVO_SOURCE_WAITING = 1 << 3,
-} SyncevoSourceStatus;
+  SYNCEVO_STATUS_WAITING =  1 << 6
+} SyncevoSessionStatus;
+
 
 typedef enum {
   SYNCEVO_PHASE_NONE,
@@ -70,17 +62,6 @@ typedef enum {
   SYNCEVO_PHASE_RECEIVING,
 } SyncevoSourcePhase;
 
-typedef struct {
-  char *name;
-  SyncevoSourcePhase phase;
-  int prepare_current;
-  int prepare_total;
-  int send_current;
-  int send_total;
-  int receive_current;
-  int receive_total;
-} SyncevoSourceProgress;  
-
 #define SYNCEVO_TYPE_SOURCE_STATUS (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID))
 #define SYNCEVO_TYPE_SOURCE_STATUSES (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, SYNCEVO_TYPE_SOURCE_STATUS))
 #define SYNCEVO_TYPE_SOURCE_PROGRESS (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID))
@@ -126,7 +107,7 @@ SyncevoSessionStatus syncevo_session_status_from_string (const char *status_str)
 
 typedef void (*SourceStatusFunc) (char *name,
                                   SyncevoSyncMode mode,
-                                  SyncevoSourceStatus status,
+                                  SyncevoSessionStatus status,
                                   guint error_code,
                                   gpointer user_data);
 void
@@ -136,14 +117,14 @@ syncevo_source_statuses_foreach (SyncevoSourceStatuses *source_statuses,
 
 void syncevo_source_statuses_free (SyncevoSourceStatuses *source_statuses);
 
-
-SyncevoSourceProgress* syncevo_source_progresses_get_current (SyncevoSourceProgresses *source_progresses);
-
+typedef void (*SourceProgressFunc) (const char *name,
+                                    SyncevoSourcePhase phase,
+                                    gpointer user_data);
+void syncevo_source_progresses_foreach (SyncevoSourceProgresses *source_progresses,
+                                        SourceProgressFunc func,
+                                        gpointer userdata);
 void syncevo_source_progresses_free (SyncevoSourceProgresses *source_progresses);
 
-void syncevo_source_progress_free (SyncevoSourceProgress *progress);
-
-
 GHashTable* syncevo_reports_index (SyncevoReports *reports,
                                    guint index);
 guint syncevo_reports_get_length (SyncevoReports *reports);
index 0542780..3c4afd1 100644 (file)
@@ -34,6 +34,7 @@ enum {
     SESSION_CHANGED,
     PRESENCE_CHANGED,
     INFO_REQUEST,
+    TEMPLATES_CHANGED,
     SHUTDOWN,
     LAST_SIGNAL
 };
@@ -97,6 +98,13 @@ generic_error (ServerAsyncData *data)
 }
 
 static void
+templates_changed_cb (DBusGProxy *proxy,
+                      SyncevoServer *server)
+{
+    g_signal_emit (server, signals[TEMPLATES_CHANGED], 0);
+}
+
+static void
 session_changed_cb (DBusGProxy *proxy,
                     char *session_path,
                     gboolean started,
@@ -124,10 +132,11 @@ info_request_cb (DBusGProxy *proxy,
                  char *state,
                  char *handler_path,
                  char *type,
+                 GHashTable *parameters,
                  SyncevoServer *server)
 {
     g_signal_emit (server, signals[INFO_REQUEST], 0, 
-                   id, session_path, state, handler_path, type);
+                   id, session_path, state, handler_path, type, parameters);
 }
 
 static void
@@ -175,6 +184,9 @@ dispose (GObject *object)
         dbus_g_proxy_disconnect_signal (priv->proxy, "InfoRequest",
                                         G_CALLBACK (info_request_cb),
                                         object);
+        dbus_g_proxy_disconnect_signal (priv->proxy, "TemplatesChanged",
+                                        G_CALLBACK (templates_changed_cb),
+                                        object);
         dbus_g_proxy_disconnect_signal (priv->proxy, "destroy",
                                         G_CALLBACK (proxy_destroy_cb),
                                         object);
@@ -259,11 +271,15 @@ syncevo_server_get_new_proxy (SyncevoServer *server)
                              G_TYPE_STRING,
                              DBUS_TYPE_G_OBJECT_PATH,
                              G_TYPE_STRING,
-                             DBUS_TYPE_G_OBJECT_PATH,
                              G_TYPE_STRING,
+                             G_TYPE_STRING,
+                             DBUS_TYPE_G_STRING_STRING_HASHTABLE,
                              G_TYPE_INVALID);
     dbus_g_proxy_connect_signal (priv->proxy, "InfoRequest",
                                  G_CALLBACK (info_request_cb), server, NULL);
+    dbus_g_proxy_add_signal (priv->proxy, "TemplatesChanged", G_TYPE_INVALID);
+    dbus_g_proxy_connect_signal (priv->proxy, "TemplatesChanged",
+                                 G_CALLBACK (templates_changed_cb), server, NULL);
     g_signal_connect (priv->proxy, "destroy",
                       G_CALLBACK (proxy_destroy_cb), server);
 
@@ -296,13 +312,19 @@ syncevo_server_init (SyncevoServer *server)
                                        G_TYPE_STRING,
                                        G_TYPE_INVALID);
     /* InfoRequest */
-    dbus_g_object_register_marshaller (syncevo_marshal_VOID__STRING_STRING_STRING_STRING_STRING,
+    dbus_g_object_register_marshaller (syncevo_marshal_VOID__STRING_STRING_STRING_STRING_STRING_BOXED,
                                        G_TYPE_NONE,
                                        G_TYPE_STRING,
                                        DBUS_TYPE_G_OBJECT_PATH,
                                        G_TYPE_STRING,
-                                       DBUS_TYPE_G_OBJECT_PATH,
                                        G_TYPE_STRING,
+                                       G_TYPE_STRING,
+                                       DBUS_TYPE_G_STRING_STRING_HASHTABLE,
+                                       G_TYPE_INVALID);
+
+    /* TemplatesChanged */
+    dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__VOID,
+                                       G_TYPE_NONE,
                                        G_TYPE_INVALID);
 
     syncevo_server_get_new_proxy (server);
@@ -341,9 +363,18 @@ syncevo_server_class_init (SyncevoServerClass *klass)
                           G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
                           G_STRUCT_OFFSET (SyncevoServerClass, info_request),
                           NULL, NULL,
-                          syncevo_marshal_VOID__STRING_STRING_STRING_STRING_STRING,
+                          syncevo_marshal_VOID__STRING_STRING_STRING_STRING_STRING_BOXED,
                           G_TYPE_NONE,
-                          5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+                          6, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, DBUS_TYPE_G_STRING_STRING_HASHTABLE);
+    signals[TEMPLATES_CHANGED] =
+            g_signal_new ("templates-changed",
+                          G_TYPE_FROM_CLASS (klass),
+                          G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
+                          G_STRUCT_OFFSET (SyncevoServerClass, templates_changed),
+                          NULL, NULL,
+                          g_cclosure_marshal_VOID__VOID,
+                          G_TYPE_NONE,
+                          0);
     signals[SHUTDOWN] =
             g_signal_new ("shutdown",
                           G_TYPE_FROM_CLASS (klass),
index 3cf8fd0..520e0f1 100644 (file)
@@ -62,7 +62,10 @@ typedef struct _SyncevoServerClass {
                           char *session_path,
                           char *state,
                           char *handler_path,
-                          char *type);
+                          char *type,
+                          GHashTable *parameters);
+
+    void (*templates_changed) (SyncevoServer *syncevo);
 
     void (*shutdown) (SyncevoServer *syncevo);
 } SyncevoServerClass;
index 2ef0303..47cfb8b 100644 (file)
@@ -670,6 +670,37 @@ syncevo_session_check_source (SyncevoSession *session,
              data);
 }
 
+void
+syncevo_session_restore (SyncevoSession *session,
+                         const char *backup_dir,
+                         const gboolean before,
+                         const char **sources,
+                         SyncevoSessionGenericCb callback,
+                         gpointer userdata)
+{
+    SessionAsyncData *data;
+    SyncevoSessionPrivate *priv;
+
+    priv = GET_PRIVATE (session);
+
+    data = session_async_data_new (session, G_CALLBACK (callback), userdata);
+
+    if (!priv->proxy) {
+        if (callback) {
+            g_idle_add ((GSourceFunc)generic_error, data);
+        }
+        return;
+    }
+
+    org_syncevolution_Session_restore_async
+            (priv->proxy,
+             backup_dir,
+             before,
+             sources,
+             (org_syncevolution_Session_check_source_reply) generic_callback,
+             data);
+}
+
 const char*
 syncevo_session_get_path (SyncevoSession *session)
 {
index 1210186..909d631 100644 (file)
@@ -124,6 +124,13 @@ void syncevo_session_check_source (SyncevoSession *session,
                                    SyncevoSessionGenericCb callback,
                                    gpointer userdata);
 
+void syncevo_session_restore (SyncevoSession *session,
+                              const char *backup_dir,
+                              const gboolean before,
+                              const char **sources,
+                              SyncevoSessionGenericCb callback,
+                              gpointer userdata);
+
 const char *syncevo_session_get_path (SyncevoSession *session);
 
 SyncevoSession *syncevo_session_new (const char *path);
index 071f6b5..9c7e0f1 100644 (file)
@@ -156,11 +156,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -190,6 +195,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -209,6 +216,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index 4f594d8..94407c6 100644 (file)
 #include <boost/bind.hpp>
 #include <boost/intrusive_ptr.hpp>
 #include <boost/shared_ptr.hpp>
+#include <boost/variant.hpp>
+#include <boost/variant/get.hpp>
 
 namespace boost {
     void intrusive_ptr_add_ref(DBusConnection *con) { dbus_connection_ref(con); }
     void intrusive_ptr_release(DBusConnection *con) { dbus_connection_unref(con); }
     void intrusive_ptr_add_ref(DBusMessage *msg) { dbus_message_ref(msg); }
     void intrusive_ptr_release(DBusMessage *msg) { dbus_message_unref(msg); }
+    void intrusive_ptr_add_ref(DBusPendingCall *call) {dbus_pending_call_ref (call); }
+    void intrusive_ptr_release(DBusPendingCall *call) {dbus_pending_call_unref (call); }
 }
 
 class DBusConnectionPtr : public boost::intrusive_ptr<DBusConnection>
@@ -110,6 +114,21 @@ class DBusMessagePtr : public boost::intrusive_ptr<DBusMessage>
     }
 };
 
+class DBusPendingCallPtr : public boost::intrusive_ptr<DBusPendingCall>
+{
+public:
+    DBusPendingCallPtr(DBusPendingCall *call, bool add_ref = false) :
+        boost::intrusive_ptr<DBusPendingCall>(call, add_ref)
+    {}
+
+    DBusPendingCall *reference(void) throw()
+    {
+        DBusPendingCall *call = get();
+        dbus_pending_call_ref(call);
+        return call;
+    }
+};
+
 /**
  * wrapper around DBusError which initializes
  * the struct automatically, then can be used to
@@ -1242,6 +1261,93 @@ template<class V> struct dbus_traits< std::vector<V> > : public dbus_traits_base
 };
 
 /**
+ * A boost::variant <V> maps to a dbus variant, only care about values of
+ * type V but will not throw error if type is not matched, this is useful if
+ * application is interested on only a sub set of possible value types
+ * in variant.
+ */
+template <class V> struct dbus_traits <boost::variant <V> > : public dbus_traits_base
+{
+    static std::string getType() { return "v"; }
+    static std::string getSignature() { return getType(); }
+    static std::string getReply() { return ""; }
+    static const int dbus = DBUS_TYPE_VARIANT;
+
+    static void get(DBusConnection *conn, DBusMessage *msg,
+                    DBusMessageIter &iter, boost::variant <V> &value)
+    {
+        if (dbus_message_iter_get_arg_type(&iter) != dbus) {
+            throw std::runtime_error("invalid argument");
+            return;
+        }
+        DBusMessageIter sub;
+        dbus_message_iter_recurse(&iter, &sub);
+        if (dbus_message_iter_get_signature(&sub) != dbus_traits<V>::getSignature()){
+            //ignore unrecognized sub type in variant
+            return;
+        }
+        V val;
+        dbus_traits<V>::get (conn, msg, sub, val);
+        value = val;
+    }
+
+    static void append(DBusMessageIter &iter, const boost::variant <V>  &value) {
+    }
+
+    //append_retval not implemented
+    //static void append_retval(DBusMessageIter &iter, arg_type array)
+    //{
+    //}
+
+    typedef boost::variant<V> host_type;
+    typedef const boost::variant<V> &arg_type;
+};
+
+/**
+ * A boost::variant <V1, V2> maps to a dbus variant, only care about values of
+ * type V1, V2 but will not throw error if type is not matched, this is useful if
+ * application is interested on only a sub set of possible value types
+ * in variant.
+ */
+template <class V1, class V2> struct dbus_traits <boost::variant <V1, V2> > : public dbus_traits_base
+{
+    static std::string getType() { return "v"; }
+    static std::string getSignature() { return getType(); }
+    static std::string getReply() { return ""; }
+    static const int dbus = DBUS_TYPE_VARIANT;
+
+    static void get(DBusConnection *conn, DBusMessage *msg,
+                    DBusMessageIter &iter, boost::variant <V1, V2> &value)
+    {
+        if (dbus_message_iter_get_arg_type(&iter) != dbus) {
+            throw std::runtime_error("invalid argument");
+            return;
+        }
+        DBusMessageIter sub;
+        dbus_message_iter_recurse(&iter, &sub);
+        if (dbus_message_iter_get_signature(&sub) != dbus_traits<V1>::getSignature()
+                && dbus_message_iter_get_signature(&sub) != dbus_traits<V2>::getSignature()){
+            //ignore unrecognized sub type in variant
+            return;
+        } else if (dbus_message_iter_get_signature(&sub) == dbus_traits<V1>::getSignature()) {
+            V1 val;
+            dbus_traits<V1>::get (conn, msg, sub, val);
+            value = val;
+        } else {
+            V2 val;
+            dbus_traits<V2>::get (conn, msg, sub, val);
+            value = val;
+        }
+    }
+
+    static void append(DBusMessageIter &iter, const boost::variant <V1, V2>  &value) {
+    }
+
+    typedef boost::variant<V1, V2> host_type;
+    typedef const boost::variant<V1, V2> &arg_type;
+};
+
+/**
  * a single member m of type V in a struct K
  */
 template<class K, class V, V K::*m> struct dbus_member_single
@@ -3900,4 +4006,255 @@ struct MakeMethodEntry< boost::function<void ()> >
     }
 };
 
+/**
+ * interface to refer to a remote object
+ */
+class DBusRemoteObject : public DBusObject
+{
+public:
+    virtual const char *getDestination() const = 0;
+    virtual ~DBusRemoteObject() {}
+};
+/**
+ * interface expected by DBusClient
+ */
+class DBusCallObject : public DBusRemoteObject
+{
+public:
+    /* The method name for the calling dbus method */
+    virtual const char *getMethod() const =0;
+    virtual ~DBusCallObject() {}
+};
+
+/*
+ * A DBus Client Call object handling 0 argument and 1 return value.
+ */
+template <class R>
+class DBusClientCall0
+{
+    const std::string m_destination;
+    const std::string m_path;
+    const std::string m_interface;
+    const std::string m_method;
+    const DBusConnectionPtr m_conn;
+
+    /** called by libdbus on error or completion of call */
+    static void dbusCallback (DBusPendingCall *call, void *user_data)
+    {
+        DBusMessagePtr reply = dbus_pending_call_steal_reply (call);
+        const char* errname = dbus_message_get_error_name (reply.get());
+        std::string error;
+        typename dbus_traits<R>::host_type r;
+        if (!errname) {
+            DBusMessageIter iter;
+            dbus_message_iter_init(reply.get(), &iter);
+            //why need connection?
+            dbus_traits<R>::get(NULL, reply.get(), iter, r);
+        } else {
+            error = errname;
+        }
+        //unmarshal the return results and call user callback
+        (*static_cast <Callback_t *>(user_data))(r, error);
+    }
+
+    /**
+     * called by libdbus to free the user_data pointer set in 
+     * dbus_pending_call_set_notify()
+     */
+    static void callDataUnref(void *user_data) {
+        delete static_cast <Callback_t *>(user_data);
+    }
+
+public:
+    /**
+     * called when result of call is available (R) or an error occurred (non-empty string)
+     */
+    typedef boost::function<void (const R &, const std::string &)> Callback_t;
+
+    DBusClientCall0 (const DBusCallObject &object)
+        :m_destination (object.getDestination()),
+         m_path (object.getPath()),
+         m_interface (object.getInterface()),
+         m_method (object.getMethod()),
+         m_conn (object.getConnection())
+    {
+    }
+
+    DBusClientCall0 (const DBusRemoteObject &object, const std::string &method)
+        :m_destination (object.getDestination()),
+         m_path (object.getPath()),
+         m_interface (object.getInterface()),
+         m_method (method),
+         m_conn (object.getConnection())
+    {
+    }
+
+    void operator () (const Callback_t &callback)
+    {
+        DBusPendingCall *call;
+        DBusMessagePtr msg(dbus_message_new_method_call(
+                    m_destination.c_str(),
+                    m_path.c_str(),
+                    m_interface.c_str(),
+                    m_method.c_str()));
+        if (!msg) {
+            throw std::runtime_error("dbus_message_new_method_call() failed");
+        }
+
+        //parameter marshaling (none)
+        if (!dbus_connection_send_with_reply(m_conn.get(), msg.get(), &call, -1)) {
+            throw std::runtime_error("dbus_connection_send failed");
+        }
+
+        DBusPendingCallPtr mCall (call);
+        Callback_t *data = new Callback_t(callback);
+        dbus_pending_call_set_notify(mCall.get(),
+                                     dbusCallback,
+                                     data,
+                                     callDataUnref);
+    }
+};
+
+class SignalWatch
+{
+ protected:
+    const DBusRemoteObject &m_object;
+    std::string m_signal;
+
+    std::string makeSignalRule() {
+        std::string rule;
+        rule = "type='signal',path='";
+        rule += m_object.getPath();
+        rule += "',interface='";
+        rule += m_object.getInterface();
+        rule += "',member='";
+        rule += m_signal;
+        rule += "'";
+        return rule;
+    }
+
+    static gboolean isMatched(DBusMessage *msg, void *data) {
+        SignalWatch *watch = static_cast<SignalWatch*>(data);
+        return dbus_message_has_path(msg, watch->m_object.getPath()) &&
+                dbus_message_is_signal(msg, watch->m_object.getInterface(), watch->m_signal.c_str());
+    }
+
+ public:
+    SignalWatch(const DBusRemoteObject &object,
+                 const std::string &signal)
+        : m_object(object), m_signal(signal)
+    {
+    }
+
+    virtual ~SignalWatch() {}
+};
+
+template <typename A1>
+class SignalWatch1 : public SignalWatch
+{
+    typedef boost::function<void (const A1 &)> Callback_t;
+    guint m_tag;
+    Callback_t *m_callback;
+
+ public:
+    SignalWatch1(const DBusRemoteObject &object,
+                 const std::string &signal)
+        : SignalWatch(object, signal), m_tag(0), m_callback(0)
+    {
+    }
+
+    ~SignalWatch1()
+    {
+        if(m_tag) {
+            g_dbus_remove_watch(m_object.getConnection(), m_tag);
+        }
+        if(m_callback) {
+            delete m_callback;
+        }
+    }
+
+    static gboolean internalCallback(DBusConnection *conn, DBusMessage *msg, void *data)
+    {
+        if(isMatched(msg, data) == FALSE) {
+            return TRUE;
+        }
+        SignalWatch1<A1> *watch = static_cast<SignalWatch1<A1>* >(data);
+
+        typename dbus_traits<A1>::host_type a1;
+
+        DBusMessageIter iter;
+        dbus_message_iter_init(msg, &iter);
+        dbus_traits<A1>::get(conn, msg, iter, a1);
+        (*watch->m_callback)(a1);
+
+        return TRUE;
+    }
+
+    void operator () (const Callback_t &callback)
+    {
+        m_callback = new Callback_t(callback);
+        std::string rule = makeSignalRule();
+        m_tag = g_dbus_add_signal_watch(m_object.getConnection(),
+                                        rule.c_str(),
+                                        internalCallback,
+                                        this,
+                                        NULL);
+    }
+};
+
+template <typename A1, typename A2>
+class SignalWatch2 : public SignalWatch
+{
+    typedef boost::function<void (const A1 &, const A2 &)> Callback_t;
+
+    guint m_tag;
+    Callback_t *m_callback;
+ public:
+    SignalWatch2(const DBusRemoteObject &object,
+                 const std::string &signal)
+        : SignalWatch(object, signal), m_tag(0), m_callback(0)
+    {
+    }
+
+    ~SignalWatch2()
+    {
+        if(m_tag) {
+            g_dbus_remove_watch(m_object.getConnection(), m_tag);
+        }
+        if(m_callback) {
+            delete m_callback;
+        }
+    }
+
+    static gboolean internalCallback(DBusConnection *conn, DBusMessage *msg, void *data)
+    {
+        if(isMatched(msg, data) == FALSE) {
+            return TRUE;
+        }
+        SignalWatch2<A1, A2> *watch = static_cast<SignalWatch2<A1, A2> *>(data);
+
+        typename dbus_traits<A1>::host_type a1;
+        typename dbus_traits<A2>::host_type a2;
+
+        DBusMessageIter iter;
+        dbus_message_iter_init(msg, &iter);
+        dbus_traits<A1>::get(conn, msg, iter, a1);
+        dbus_traits<A2>::get(conn, msg, iter, a2);
+        (*watch->m_callback)(a1, a2);
+
+        return TRUE;
+    }
+
+    void operator () (const Callback_t &callback)
+    {
+        m_callback = new Callback_t(callback);
+        std::string rule = makeSignalRule();
+        m_tag = g_dbus_add_signal_watch(m_object.getConnection(),
+                                        rule.c_str(),
+                                        internalCallback,
+                                        this,
+                                        NULL);
+    }
+};
+
 #endif // INCL_GDBUS_CXX_BRIDGE
diff --git a/src/gnome-bluetooth/Makefile.am b/src/gnome-bluetooth/Makefile.am
new file mode 100644 (file)
index 0000000..7229d9c
--- /dev/null
@@ -0,0 +1,9 @@
+plugindir = $(GNOMEBLUETOOTH_DIR)/plugins/
+
+INCLUDES =  $(GNOMEBLUETOOTH_CFLAGS) \
+       -DLOCALEDIR=\"$(SYNCEVOLUTION_LOCALEDIR)\"      
+
+plugin_LTLIBRARIES = libgbtsyncevolution.la
+libgbtsyncevolution_la_SOURCES = syncevolution.c
+libgbtsyncevolution_la_LDFLAGS = -module -avoid-version
+
diff --git a/src/gnome-bluetooth/Makefile.in b/src/gnome-bluetooth/Makefile.in
new file mode 100644 (file)
index 0000000..e6ffa9b
--- /dev/null
@@ -0,0 +1,580 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/gnome-bluetooth
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4-repo/ax_boost_base.m4 \
+       $(top_srcdir)/m4/intltool.m4 $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(plugindir)"
+pluginLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(plugin_LTLIBRARIES)
+libgbtsyncevolution_la_LIBADD =
+am_libgbtsyncevolution_la_OBJECTS = syncevolution.lo
+libgbtsyncevolution_la_OBJECTS = $(am_libgbtsyncevolution_la_OBJECTS)
+libgbtsyncevolution_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+       $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+       $(libgbtsyncevolution_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+       --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+       --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+       $(LDFLAGS) -o $@
+SOURCES = $(libgbtsyncevolution_la_SOURCES)
+DIST_SOURCES = $(libgbtsyncevolution_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ADDRESSBOOK_CFLAGS = @ADDRESSBOOK_CFLAGS@
+ADDRESSBOOK_LIBS = @ADDRESSBOOK_LIBS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BACKEND_CPPFLAGS = @BACKEND_CPPFLAGS@
+BACKEND_DEFINES = @BACKEND_DEFINES@
+BLUEZ_CFLAGS = @BLUEZ_CFLAGS@
+BLUEZ_LIBS = @BLUEZ_LIBS@
+BOOST_CPPFLAGS = @BOOST_CPPFLAGS@
+BOOST_LDFLAGS = @BOOST_LDFLAGS@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CORE_LDADD_DEP = @CORE_LDADD_DEP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPUNIT_CXXFLAGS = @CPPUNIT_CXXFLAGS@
+CPPUNIT_LDFLAGS = @CPPUNIT_LDFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_BINDING_TOOL = @DBUS_BINDING_TOOL@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_CFLAGS = @DBUS_GLIB_CFLAGS@
+DBUS_GLIB_LIBS = @DBUS_GLIB_LIBS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SERVICES_DIR = @DBUS_SERVICES_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+EBOOK_CFLAGS = @EBOOK_CFLAGS@
+EBOOK_LIBS = @EBOOK_LIBS@
+ECAL_CFLAGS = @ECAL_CFLAGS@
+ECAL_LIBS = @ECAL_LIBS@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EPACKAGE_CFLAGS = @EPACKAGE_CFLAGS@
+EPACKAGE_LIBS = @EPACKAGE_LIBS@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+FILE_CFLAGS = @FILE_CFLAGS@
+FILE_LIBS = @FILE_LIBS@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
+GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
+GOBJECT_LIBS = @GOBJECT_LIBS@
+GREP = @GREP@
+GTHREAD_CFLAGS = @GTHREAD_CFLAGS@
+GTHREAD_LIBS = @GTHREAD_LIBS@
+GTK_2_18_CFLAGS = @GTK_2_18_CFLAGS@
+GTK_2_18_LIBS = @GTK_2_18_LIBS@
+GTK_BUILDER_CONV = @GTK_BUILDER_CONV@
+GUI_CFLAGS = @GUI_CFLAGS@
+GUI_DESKTOP_FILES = @GUI_DESKTOP_FILES@
+GUI_LIBS = @GUI_LIBS@
+GUI_PROGRAMS = @GUI_PROGRAMS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+KEYRING_2_20_CFLAGS = @KEYRING_2_20_CFLAGS@
+KEYRING_2_20_LIBS = @KEYRING_2_20_LIBS@
+KEYRING_CFLAGS = @KEYRING_CFLAGS@
+KEYRING_LIBS = @KEYRING_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
+LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
+LIBS = @LIBS@
+LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
+LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MCALB_CFLAGS = @MCALB_CFLAGS@
+MCALB_LIBS = @MCALB_LIBS@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MODIFY_SYNCCOMPARE = @MODIFY_SYNCCOMPARE@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SQLITE_CFLAGS = @SQLITE_CFLAGS@
+SQLITE_LIBS = @SQLITE_LIBS@
+STABLE_VERSION = @STABLE_VERSION@
+STRIP = @STRIP@
+SYNCEVOLUTION_CXXFLAGS = @SYNCEVOLUTION_CXXFLAGS@
+SYNCEVOLUTION_LDADD = @SYNCEVOLUTION_LDADD@
+SYNCEVOLUTION_LOCALEDIR = @SYNCEVOLUTION_LOCALEDIR@
+SYNCSOURCES = @SYNCSOURCES@
+SYNTHESIS = @SYNTHESIS@
+SYNTHESISSRC = @SYNTHESISSRC@
+SYNTHESIS_CFLAGS = @SYNTHESIS_CFLAGS@
+SYNTHESIS_DEP = @SYNTHESIS_DEP@
+SYNTHESIS_ENGINE = @SYNTHESIS_ENGINE@
+SYNTHESIS_LIB = @SYNTHESIS_LIB@
+SYNTHESIS_LIBS = @SYNTHESIS_LIBS@
+SYNTHESIS_SRC = @SYNTHESIS_SRC@
+SYNTHESIS_SUBDIR = @SYNTHESIS_SUBDIR@
+TRANSPORT_CFLAGS = @TRANSPORT_CFLAGS@
+TRANSPORT_LIBS = @TRANSPORT_LIBS@
+UNIQUE_CFLAGS = @UNIQUE_CFLAGS@
+UNIQUE_LIBS = @UNIQUE_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XMLRPC_CFLAGS = @XMLRPC_CFLAGS@
+XMLRPC_LIBS = @XMLRPC_LIBS@
+XSLT = @XSLT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+backenddir = @backenddir@
+backendsearchdir = @backendsearchdir@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+plugindir = $(GNOMEBLUETOOTH_DIR)/plugins/
+INCLUDES = $(GNOMEBLUETOOTH_CFLAGS) \
+       -DLOCALEDIR=\"$(SYNCEVOLUTION_LOCALEDIR)\"      
+
+plugin_LTLIBRARIES = libgbtsyncevolution.la
+libgbtsyncevolution_la_SOURCES = syncevolution.c
+libgbtsyncevolution_la_LDFLAGS = -module -avoid-version
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+               && exit 0; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu  src/gnome-bluetooth/Makefile'; \
+       cd $(top_srcdir) && \
+         $(AUTOMAKE) --gnu  src/gnome-bluetooth/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES)
+       @$(NORMAL_INSTALL)
+       test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)"
+       @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \
+         if test -f $$p; then \
+           f=$(am__strip_dir) \
+           echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(pluginLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(plugindir)/$$f'"; \
+           $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(pluginLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(plugindir)/$$f"; \
+         else :; fi; \
+       done
+
+uninstall-pluginLTLIBRARIES:
+       @$(NORMAL_UNINSTALL)
+       @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \
+         p=$(am__strip_dir) \
+         echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$p'"; \
+         $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$p"; \
+       done
+
+clean-pluginLTLIBRARIES:
+       -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES)
+       @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \
+         dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+         test "$$dir" != "$$p" || dir=.; \
+         echo "rm -f \"$${dir}/so_locations\""; \
+         rm -f "$${dir}/so_locations"; \
+       done
+libgbtsyncevolution.la: $(libgbtsyncevolution_la_OBJECTS) $(libgbtsyncevolution_la_DEPENDENCIES) 
+       $(libgbtsyncevolution_la_LINK) -rpath $(plugindir) $(libgbtsyncevolution_la_OBJECTS) $(libgbtsyncevolution_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syncevolution.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@   mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@   mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@   $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@   mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       mkid -fID $$unique
+tags: TAGS
+
+TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       tags=; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+           $$tags $$unique; \
+       fi
+ctags: CTAGS
+CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       tags=; \
+       list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+             END { if (nonempty) { for (i in files) print i; }; }'`; \
+       test -z "$(CTAGS_ARGS)$$tags$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$tags $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && cd $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       list='$(DISTFILES)'; \
+         dist_files=`for file in $$list; do echo $$file; done | \
+         sed -e "s|^$$srcdirstrip/||;t" \
+             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+       case $$dist_files in \
+         */*) $(MKDIR_P) `echo "$$dist_files" | \
+                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+                          sort -u` ;; \
+       esac; \
+       for file in $$dist_files; do \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         if test -d $$d/$$file; then \
+           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+           fi; \
+           cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+         else \
+           test -f $(distdir)/$$file \
+           || cp -p $$d/$$file $(distdir)/$$file \
+           || exit 1; \
+         fi; \
+       done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES)
+installdirs:
+       for dir in "$(DESTDIR)$(plugindir)"; do \
+         test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+       done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-pluginLTLIBRARIES \
+       mostlyclean-am
+
+distclean: distclean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pluginLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+       mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-pluginLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+       clean-libtool clean-pluginLTLIBRARIES ctags distclean \
+       distclean-compile distclean-generic distclean-libtool \
+       distclean-tags distdir dvi dvi-am html html-am info info-am \
+       install install-am install-data install-data-am install-dvi \
+       install-dvi-am install-exec install-exec-am install-html \
+       install-html-am install-info install-info-am install-man \
+       install-pdf install-pdf-am install-pluginLTLIBRARIES \
+       install-ps install-ps-am install-strip installcheck \
+       installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags uninstall uninstall-am uninstall-pluginLTLIBRARIES
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/gnome-bluetooth/syncevolution.c b/src/gnome-bluetooth/syncevolution.c
new file mode 100644 (file)
index 0000000..35f9998
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ *  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) any later version.
+ *
+ *  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 St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <bluetooth-plugin.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#define SYNCUI_BINARY "sync-ui"
+#define SYNCUI_ARG "--show-settings obex+bt://"
+
+/*Only detecting SyncML Client*/
+static gboolean
+has_config_widget (const char *bdaddr, const char **uuids)
+{
+       int i;
+       if (uuids == NULL) {
+               return FALSE;
+       }
+
+       for (i = 0; uuids[i] != NULL; i++) {
+               if (!g_strcmp0 (uuids[i], "SyncMLClient")) {
+                       //find whether "sync-ui" is available
+                       if (g_find_program_in_path (SYNCUI_BINARY) == NULL) {
+                               return FALSE;
+                       }
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+static void
+button_clicked (GtkButton *button, gpointer user_data)
+{
+       const char *bdaddr;
+       pid_t midman;
+       pid_t syncui;
+       char args[sizeof (SYNCUI_ARG) + sizeof ("FF:FF:FF:FF:FF:FF") - 1];
+
+       bdaddr = g_object_get_data (G_OBJECT (button), "bdaddr");
+       midman = fork ();
+       if (midman == 0) {
+               //in midman process
+               syncui = fork();
+               if (syncui == 0) {
+                       // in syncui process
+                       strcpy (args, SYNCUI_ARG);
+                       strncat (args, bdaddr, sizeof ("FF:FF:FF:FF:FF:FF"));
+                       if (execlp (SYNCUI_BINARY, args, NULL) == -1){
+                               g_warning ("SyncEvolution plugin failed to launch sync-ui!");
+                               exit (-1);
+                       }
+               } else if (syncui == -1) {
+                       // error in forking sub process
+                       g_warning ("SyncEvolution plugin failed to launch sync-ui!");
+                       exit (-1);
+               } else {
+                       //do nothing, just exit. This will cause sync-ui
+                       //process an orphan.
+                       exit (0);
+               }
+       } else if (midman == -1) {
+               //error in forking sub process
+               g_warning ("SyncEvolution plugin failed to launch sync-ui!");
+       } else {
+               //in bluetooth-panel process
+               if (waitpid (midman, NULL, 0) == -1){
+                       //error in waitpid
+                       g_warning ("SyncEvolution plugin failed to launch sync-ui!");
+               }
+       }
+}
+
+static GtkWidget *
+get_config_widgets (const char *bdaddr, const char **uuids)
+{
+       GtkWidget *hbox;
+       GtkWidget *button;
+       GtkWidget *label1;
+       GtkWidget *label2;
+       int label_max_width = 40;
+       int button_max_width = 10;
+
+       hbox = gtk_hbox_new (FALSE, 6);
+       gtk_widget_show (hbox);
+       label1 = gtk_label_new (_("Sync in the Sync application"));
+       gtk_label_set_max_width_chars (GTK_LABEL(label1), label_max_width);
+       label2 = gtk_label_new (_("Sync"));
+       gtk_label_set_max_width_chars (GTK_LABEL(label2), button_max_width);
+       button = gtk_button_new ();
+       gtk_container_add (GTK_CONTAINER(button), label2);
+       g_object_set_data_full (G_OBJECT (button), "bdaddr", g_strdup (bdaddr), g_free);
+       gtk_widget_show (label1);
+       gtk_widget_show_all (button);
+       gtk_box_pack_start (GTK_BOX(hbox), label1, FALSE, FALSE, 6);
+       gtk_box_pack_end (GTK_BOX(hbox), button, FALSE, FALSE, 6);
+       /* And set the signal */
+       g_signal_connect (G_OBJECT (button), "clicked",
+                         G_CALLBACK (button_clicked), NULL);
+
+       return hbox;
+}
+
+static void
+device_removed (const char *bdaddr)
+{
+       //nothing todo
+}
+
+static GbtPluginInfo plugin_info = {
+       "SyncEvolution",
+       has_config_widget,
+       get_config_widgets,
+       device_removed
+};
+
+GBT_INIT_PLUGIN(plugin_info)
+
index 96550cb..e791b8a 100644 (file)
@@ -24,6 +24,7 @@ themercdir = $(datadir)/syncevolution/
 themerc_DATA = \
        close.png close_hover.png settings.png settings_hover.png \
        sync-generic.png \
+       sync-spinner.gif \
        sync-ui.rc
 
 desktopdir = $(datadir)/applications
index 6a029c0..fe14411 100644 (file)
@@ -187,11 +187,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -221,6 +226,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -240,6 +247,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
@@ -351,6 +360,7 @@ themercdir = $(datadir)/syncevolution/
 themerc_DATA = \
        close.png close_hover.png settings.png settings_hover.png \
        sync-generic.png \
+       sync-spinner.gif \
        sync-ui.rc
 
 desktopdir = $(datadir)/applications
index e012f19..67c752a 100644 (file)
 #include "config.h"
 #include "sync-ui.h"
 
+static char *settings_id = NULL;
+
+static GOptionEntry entries[] =
+{
+  { "show-settings", 0, 0, G_OPTION_ARG_STRING, &settings_id, "Open sync settings for given sync url or configuration name", "url or config name" },
+  { NULL }
+};
+
+
 static void
 set_app_name_and_icon ()
 {
@@ -32,6 +41,26 @@ set_app_name_and_icon ()
     gtk_window_set_default_icon_name ("sync");
 }
 
+static void
+init (int argc, char *argv[])
+{
+    GError *error = NULL;
+    GOptionContext *context;
+
+    gtk_init (&argc, &argv);
+    bindtextdomain (GETTEXT_PACKAGE, SYNCEVOLUTION_LOCALEDIR);
+    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+    textdomain (GETTEXT_PACKAGE);
+
+    context = g_option_context_new ("- synchronise PIM data with Syncevolution");
+    g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+    g_option_context_add_group (context, gtk_get_option_group (TRUE));
+    if (!g_option_context_parse (context, &argc, &argv, &error)) {
+        g_warning ("option parsing failed: %s\n", error->message);
+    }
+}
+
+
 #ifdef ENABLE_UNIQUE
 #include <unique/unique.h>
 
@@ -39,6 +68,7 @@ enum
 {
     COMMAND_0, 
 
+    COMMAND_SHOW_CONFIGURATION
     /* no sync-ui specific commands */
 };
 
@@ -47,10 +77,12 @@ message_received_cb (UniqueApp         *app,
                      UniqueCommand      command,
                      UniqueMessageData *message,
                      guint              time_,
-                     GtkWindow         *main_win)
+                     app_data          *data)
 {
-    UniqueResponse res;
+    char *arg;
+    GtkWindow *main_win;
 
+    main_win = sync_ui_get_main_window (data);
     switch (command) {
     case UNIQUE_ACTIVATE:
         if (GTK_IS_WINDOW (main_win)) {
@@ -59,15 +91,21 @@ message_received_cb (UniqueApp         *app,
                                    unique_message_data_get_screen (message));
             gtk_window_present (GTK_WINDOW (main_win));
         }
-        res = UNIQUE_RESPONSE_OK;
         break;
-    /* handle any sync-ui specific commands here */
+    case COMMAND_SHOW_CONFIGURATION:
+        arg = unique_message_data_get_text (message);
+        if (GTK_IS_WINDOW (main_win) && arg) {
+            /* move the main window to the screen that sent us the command */
+            gtk_window_set_screen (GTK_WINDOW (main_win), 
+                                   unique_message_data_get_screen (message));
+            sync_ui_show_settings (data, arg);
+        }
+        break;
     default:
-        res = UNIQUE_RESPONSE_OK;
         break;
     }
 
-    return res;
+    return UNIQUE_RESPONSE_OK;
 }
 
 int
@@ -75,32 +113,40 @@ main (int argc, char *argv[])
 {
     UniqueApp *app;
 
-    gtk_init (&argc, &argv);
-    bindtextdomain (GETTEXT_PACKAGE, SYNCEVOLUTION_LOCALEDIR);
-    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-    textdomain (GETTEXT_PACKAGE);
+    init (argc, argv);
 
-    app = unique_app_new ("org.Moblin.Sync", NULL);
+    app = unique_app_new_with_commands ("org.Moblin.Sync", NULL,
+                                        "show-configuration", COMMAND_SHOW_CONFIGURATION,
+                                        NULL);
 
     if (unique_app_is_running (app)) {
+        UniqueMessageData *message = NULL;
+        UniqueCommand command = UNIQUE_ACTIVATE;
 
-        unique_app_send_message (app, UNIQUE_ACTIVATE, NULL);
-        /* could handle the response here... */
+        if (settings_id) {
+            command = COMMAND_SHOW_CONFIGURATION;
+            message = unique_message_data_new ();
+            unique_message_data_set_text (message, settings_id, -1);
+        }
+        unique_app_send_message (app, command, message);
+        unique_message_data_free (message);
     } else {
-        GtkWidget *main_win;
+        app_data *data;
 
         set_app_name_and_icon ();
 
-        main_win = sync_ui_create_main_window ();
-        if (main_win) {
+        data = sync_ui_create ();
+        if (data) {
             /* UniqueApp watches the main window so it can terminate 
              * the startup notification sequence for us */
-            unique_app_watch_window (app, GTK_WINDOW (main_win));
+            unique_app_watch_window (app, sync_ui_get_main_window (data));
 
             /* handle notifications from new app launches */     
             g_signal_connect (app, "message-received", 
-                              G_CALLBACK (message_received_cb), main_win);
-
+                              G_CALLBACK (message_received_cb), data);
+            if (settings_id) {
+                sync_ui_show_settings (data, settings_id);
+            }
             gtk_main ();
         }
     }
@@ -114,14 +160,17 @@ main (int argc, char *argv[])
 int
 main (int argc, char *argv[])
 {
-    gtk_init (&argc, &argv);
-    bindtextdomain (GETTEXT_PACKAGE, SYNCEVOLUTION_LOCALEDIR);
-    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-    textdomain (GETTEXT_PACKAGE);
+    app_data *data;
+
+    init (argc, argv);
 
     set_app_name_and_icon ();
+    data = sync_ui_create ();
+
+    if (settings_id) {
+        sync_ui_show_settings (data, settings_id);
+    }
 
-    sync_ui_create_main_window ();
     gtk_main ();
     return 0;
 }
index 94c48f2..3fda800 100644 (file)
@@ -43,6 +43,7 @@ mux_decorations_get_type (void)
 enum {
   PROP_0,
   PROP_DECORATIONS,
+  PROP_BACK_TITLE,
 };
 
 enum {
@@ -73,6 +74,10 @@ mux_window_get_property (GObject *object, guint property_id,
     case PROP_DECORATIONS:
         g_value_set_uint (value, win->decorations);
         break;
+    case PROP_BACK_TITLE:
+        g_value_set_string (value,
+                            gtk_button_get_label (GTK_BUTTON (win->back_btn)));
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -88,6 +93,13 @@ mux_window_set_property (GObject *object, guint property_id,
     case PROP_DECORATIONS:
         mux_window_set_decorations (win, g_value_get_uint (value));
         break;
+    case PROP_BACK_TITLE:
+        g_free (win->back_title);
+        win->back_title = g_strdup (g_value_get_string (value));
+        if (win->back_btn) {
+            gtk_button_set_label (GTK_BUTTON (win->back_btn), win->back_title);
+        }
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -302,6 +314,15 @@ mux_window_class_init (MuxWindowClass *klass)
                                      PROP_DECORATIONS,
                                      pspec);
 
+    pspec = g_param_spec_string ("back-title", 
+                                 NULL,
+                                 "title of the back button in the window decoration",
+                                 "", 
+                                 G_PARAM_READWRITE);
+    g_object_class_install_property (object_class,
+                                     PROP_BACK_TITLE,
+                                     pspec);
+
     mux_window_signals[SETTINGS_VISIBILITY_CHANGED] = 
             g_signal_new ("settings-visibility-changed",
                           G_OBJECT_CLASS_TYPE (object_class),
@@ -381,7 +402,6 @@ mux_window_build_title_bar (MuxWindow *window)
 {
     GtkWidget *box, *button_box, *btn, *align;
     GdkPixbuf *pixbuf, *pixbuf_hover;
-    gint index;
 
     if (window->title_bar) {
         gtk_widget_unparent (window->title_bar);
@@ -405,35 +425,14 @@ mux_window_build_title_bar (MuxWindow *window)
     gtk_container_add (GTK_CONTAINER (align), button_box);
     gtk_widget_show (button_box);
 
-    btn = gtk_button_new_with_label (gtk_window_get_title (GTK_WINDOW (window)));
-    gtk_box_pack_start (GTK_BOX (button_box), btn,
+    window->back_btn = gtk_button_new_with_label (window->back_title);
+    gtk_box_pack_start (GTK_BOX (button_box), window->back_btn,
                         FALSE, FALSE, 4);
-    g_signal_connect (btn, "clicked",
+    g_signal_connect (window->back_btn, "clicked",
                       G_CALLBACK (bread_crumb_clicked_cb), window);
-    gtk_widget_show (btn);
-
-    index = mux_window_get_current_page (window);
-    if (index > -1) {
-        char *title;
-        GtkWidget *page;
-
-        page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), index);
-        title = g_object_get_data (G_OBJECT (page), "title");
-        if (title) {
-            GtkWidget *img;
-            img = gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD,
-                                            GTK_ICON_SIZE_SMALL_TOOLBAR);
-            gtk_box_pack_start (GTK_BOX (button_box), img,
-                                FALSE, FALSE, 4);
-            gtk_widget_show (img);
-
-            btn = gtk_button_new_with_label (title);
-            gtk_box_pack_start (GTK_BOX (button_box), btn,
-                                FALSE, FALSE, 4);
-            gtk_widget_show (btn);
-        }
+    if (mux_window_get_current_page (window) != -1) {
+        gtk_widget_show (window->back_btn);
     }
-
     /*window->title_label = gtk_label_new (gtk_window_get_title (GTK_WINDOW (window)));
     gtk_box_pack_start (GTK_BOX (box), window->title_label,
                         FALSE, FALSE, 0);
@@ -526,9 +525,11 @@ mux_window_init (MuxWindow *self)
 }
 
 GtkWidget*
-mux_window_new (void)
+mux_window_new (const char *back_title)
 {
-    return g_object_new (MUX_TYPE_WINDOW, NULL);
+    return g_object_new (MUX_TYPE_WINDOW,
+                         "back-title", back_title,
+                         NULL);
 }
 
 void 
@@ -580,14 +581,12 @@ mux_window_get_settings_visible (MuxWindow *window)
 
 gint
 mux_window_append_page (MuxWindow *window,
-                        const char *title,
                         GtkWidget *page,
                         gboolean is_settings)
 {
     gint index;
 
     index = gtk_notebook_append_page (GTK_NOTEBOOK (window->notebook), page, NULL);
-    g_object_set_data_full (G_OBJECT (page), "title", g_strdup (title), g_free);
 
     if (is_settings) {
         window->settings_index = index;
@@ -604,6 +603,7 @@ void mux_window_set_current_page (MuxWindow *window, gint index)
         if (bin->child) {
             gtk_widget_show (bin->child);
         }
+        gtk_widget_hide (window->back_btn);
     } else {
         gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), index);
         if (bin->child) {
@@ -611,8 +611,9 @@ void mux_window_set_current_page (MuxWindow *window, gint index)
         }
         gtk_widget_show (window->notebook);
         gtk_widget_map (window->notebook);
-    }    
-    mux_window_build_title_bar (window);
+
+        gtk_widget_show (window->back_btn);
+    }
 }
 
 gint
index 51d7943..fed7f1d 100644 (file)
@@ -50,6 +50,7 @@ typedef struct {
     GtkWindow parent;
 
     GtkWidget *title_bar;
+    GtkWidget *back_btn;
     GtkWidget *settings;
     GtkWidget *settings_button;
     GtkWidget *notebook;
@@ -61,6 +62,8 @@ typedef struct {
     MuxDecorations decorations;
     GdkColor title_bar_color;
     guint title_bar_height;
+    char *back_title;
+
 } MuxWindow;
 
 typedef struct {
@@ -71,7 +74,7 @@ typedef struct {
 
 GType mux_window_get_type (void);
 
-GtkWidget* mux_window_new (void);
+GtkWidget* mux_window_new (const char *back_title);
 
 void mux_window_set_decorations (MuxWindow *window, MuxDecorations decorations);
 MuxDecorations mux_window_get_decorations (MuxWindow *window);
@@ -79,7 +82,7 @@ MuxDecorations mux_window_get_decorations (MuxWindow *window);
 void mux_window_set_settings_visible (MuxWindow *window, gboolean visible);
 gboolean mux_window_get_settings_visible (MuxWindow *window);
 
-gint mux_window_append_page (MuxWindow *window, const char *title, GtkWidget *page, gboolean is_settings);
+gint mux_window_append_page (MuxWindow *window, GtkWidget *page, gboolean is_settings);
 
 void mux_window_set_current_page (MuxWindow *window, gint index);
 gint mux_window_get_current_page (MuxWindow *window);
index 0d05eed..f910bdb 100644 (file)
@@ -27,6 +27,8 @@ typedef struct source_widgets {
     GtkWidget *check;
 
     GtkWidget *source_toggle_label;
+
+    guint count;
 } source_widgets;
 
 enum
@@ -35,6 +37,7 @@ enum
 
   PROP_SERVER,
   PROP_NAME,
+  PROP_CONFIG,
   PROP_CURRENT,
   PROP_HAS_TEMPLATE,
   PROP_CONFIGURED,
@@ -100,7 +103,6 @@ update_source_uri (char *name,
 
     widgets = (source_widgets*)g_hash_table_lookup (self->sources, name);
     if (!widgets) {
-        g_warning ("No widgets found for source %s", name);
         return;
     }
 
@@ -121,6 +123,8 @@ set_config_cb (SyncevoSession *session,
         return;
     }
 
+    self->configured = TRUE;
+
     g_object_unref (session);
     g_signal_emit (self, signals[SIGNAL_CHANGED], 0);
 }
@@ -131,14 +135,13 @@ sync_config_widget_save_config (SyncConfigWidget *self,
                                 gboolean delete)
 {
     if (delete) {
-        syncevo_config_free (self->config->config);
-        self->config->config = g_hash_table_new (g_str_hash, g_str_equal);
+        syncevo_config_free (self->config);
+        self->config = g_hash_table_new (g_str_hash, g_str_equal);
     }
-
     syncevo_session_set_config (session,
                                 FALSE,
                                 FALSE,
-                                self->config->config,
+                                self->config,
                                 (SyncevoSessionGenericCb)set_config_cb,
                                 self);
 }
@@ -213,22 +216,19 @@ static void
 stop_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
 {
     save_config_data *data;
-    SyncevoConfig *config;
 
     if (!self->config) {
         return;
     }
 
-    config = self->config->config;
-
-    syncevo_config_set_value (config, NULL, "defaultPeer", "");
+    syncevo_config_set_value (self->config, NULL, "defaultPeer", "");
     sync_config_widget_set_current (self, FALSE);
 
     data = g_slice_new (save_config_data);
     data->widget = self;
     data->delete = FALSE;
     syncevo_server_start_session (self->server,
-                                  self->config->name,
+                                  self->config_name,
                                   (SyncevoServerStartSessionCb)start_session_for_config_write_cb,
                                   data);
 }
@@ -236,10 +236,9 @@ stop_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
 static void
 use_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
 {
-    SyncevoConfig *config;
     save_config_data *data;
-    const char *username, *password, *sync_url;
-    char *real_url;
+    const char *username, *password, *sync_url, *pretty_name;
+    char *real_url, *device;
     gboolean send, receive;
     SyncevoSyncMode mode;
 
@@ -248,55 +247,51 @@ use_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
     }
 
     if (self->current_service_name && !self->current) {
-        GtkWidget *w, *top_level;
-        int ret;
-        char *msg, *yes, *no;
+        gboolean ret;
+        char *msg, *yes, *no, *new_name, *old_name;
 
         /*TRANSLATORS: warning dialog text for changing current service */
         msg = g_strdup_printf
-            (_("Do you want to replace %s with %s? This\n"
-               "will not remove any synced information on either\n"
-               "end but you will no longer be able to sync with\n"
-               "%s."),
+            (_("Do you want to replace %s with %s? This "
+               "will not remove any synced information on either "
+               "end but you will no longer be able to sync with %s."),
              self->current_service_name,
              gtk_entry_get_text (GTK_ENTRY (self->entry)),
              self->current_service_name);
+
+        new_name = g_strndup (gtk_entry_get_text (GTK_ENTRY (self->entry)), 40);
+        old_name = g_strndup (self->current_service_name, 40);
+
         /* TRANSLATORS: decline/accept buttons in warning dialog.
            Placeholder is service name */
-        yes = g_strdup_printf (_("Yes, use %s"),
-                               gtk_entry_get_text (GTK_ENTRY (self->entry)));
-        no = g_strdup_printf (_("No, use %s"), self->current_service_name);
-        top_level = gtk_widget_get_toplevel (GTK_WIDGET (self));
-        w = gtk_message_dialog_new (GTK_WINDOW (top_level),
-                                    GTK_DIALOG_MODAL,
-                                    GTK_MESSAGE_QUESTION,
-                                    GTK_BUTTONS_NONE,
-                                    msg);
-        gtk_dialog_add_buttons (GTK_DIALOG (w),
-                                no, GTK_RESPONSE_NO,
-                                yes, GTK_RESPONSE_YES,
-                                NULL);
-        ret = gtk_dialog_run (GTK_DIALOG (w));
-        gtk_widget_destroy (w);
+        yes = g_strdup_printf (_("Yes, use %s"), new_name);
+        no = g_strdup_printf (_("No, use %s"), old_name);
+
+        ret = show_confirmation (GTK_WIDGET (self), msg, yes, no);
+
         g_free (msg);
         g_free (yes);
         g_free (no);
+        g_free (new_name);
+        g_free (old_name);
 
-        if (ret != GTK_RESPONSE_YES) {
+        if (!ret) {
             return;
         }
     }
 
-    config = self->config->config;
-    self->config->name = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->entry)));
+    if (strlen (self->config_name) == 0) {
+        g_free (self->config_name);
+        self->config_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (self->entry)));
+    }
 
     if (self->mode_changed) {
         GHashTableIter iter;
         source_widgets *widgets;
         char *name;
 
-        g_object_get (self->send_check, "active", &send, NULL);
-        g_object_get (self->receive_check, "active", &receive, NULL);
+        send = toggle_get_active (self->send_check);
+        receive = toggle_get_active (self->receive_check);
 
         if (send && receive) {
             mode = SYNCEVO_SYNC_TWO_WAY;
@@ -313,18 +308,18 @@ use_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
             const char *mode_str;
             gboolean active;
 
-            g_object_get (widgets->check, "active", &active, NULL);
+            active = toggle_get_active (widgets->check);
             if (active) {
                 mode_str = syncevo_sync_mode_to_string (mode);
             } else {
                 mode_str = "none";
             }
-            syncevo_config_set_value (config, name, "sync", mode_str);
+            syncevo_config_set_value (self->config, name, "sync", mode_str);
         }
     }
 
     username = gtk_entry_get_text (GTK_ENTRY (self->username_entry));
-    syncevo_config_set_value (config, NULL, "username", username);
+    syncevo_config_set_value (self->config, NULL, "username", username);
 
     sync_url = gtk_entry_get_text (GTK_ENTRY (self->baseurl_entry));
     /* make a wild guess if no scheme in url */
@@ -333,32 +328,36 @@ use_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
     } else {
         real_url = g_strdup (sync_url);
     }
-    syncevo_config_set_value (config, NULL, "syncURL", real_url);
+    syncevo_config_set_value (self->config, NULL, "syncURL", real_url);
 
     password = gtk_entry_get_text (GTK_ENTRY (self->password_entry));
-    syncevo_config_set_value (config, NULL, "password", password);
-
-
-    if (!self->config->name || strlen (self->config->name) == 0 ||
-        !sync_url || strlen (sync_url) == 0) {
-        /* TODO show in UI: service settings missing name or url */
-        show_error_dialog (GTK_WIDGET (self), 
-                           _("Service must have a name and server URL"));
-        return;
+    syncevo_config_set_value (self->config, NULL, "password", password);
+
+    syncevo_config_get_value (self->config, NULL, "deviceName", &device);
+    if (!device || strlen (device) == 0) {
+        if (!self->config_name || strlen (self->config_name) == 0 ||
+            !sync_url || strlen (sync_url) == 0) {
+            show_error_dialog (GTK_WIDGET (self), 
+                               _("Service must have a name and server URL"));
+            return;
+        }
     }
 
-    syncevo_config_foreach_source (config,
+    syncevo_config_foreach_source (self->config,
                                    (ConfigFunc)update_source_uri,
                                    self);
 
-    syncevo_config_set_value (config, NULL, "defaultPeer", self->config->name);
+    pretty_name = gtk_entry_get_text (GTK_ENTRY (self->entry));
+    syncevo_config_set_value (self->config, NULL, "PeerName", pretty_name);
+    syncevo_config_get_value (self->config, NULL, "PeerName", &self->pretty_name);
+    syncevo_config_set_value (self->config, NULL, "defaultPeer", self->config_name);
     sync_config_widget_set_current (self, TRUE);
 
     data = g_slice_new (save_config_data);
     data->widget = self;
     data->delete = FALSE;
     syncevo_server_start_session (self->server,
-                                  self->config->name,
+                                  self->config_name,
                                   (SyncevoServerStartSessionCb)start_session_for_config_write_cb,
                                   data);
 
@@ -368,9 +367,7 @@ use_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
 static void
 reset_delete_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
 {
-    GtkWidget *w, *top_level;
-    int ret;
-    char *msg, *yes;
+    char *msg, *yes, *no;
     save_config_data *data;
 
     if (!self->config) {
@@ -381,43 +378,31 @@ reset_delete_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
         /*TRANSLATORS: warning dialog text for resetting pre-defined
           services */
         msg = g_strdup_printf
-            (_("Do you want to reset the settings for %s?\n"
+            (_("Do you want to reset the settings for %s? "
                "This will not remove any synced information on either end."),
-             self->config->name);
-        /*TRANSLATORS: accept button in warning dialog */
+             self->pretty_name);
+        /*TRANSLATORS: buttons in reset-service warning dialog */
         yes = _("Yes, reset");
+        no = _("No, keep settings");
     } else {
         /*TRANSLATORS: warning dialog text for deleting user-defined
           services */
         msg = g_strdup_printf
-            (_("Do you want to delete the settings for %s?\n"
-               "This will not remove any synced information on either\n"
+            (_("Do you want to delete the settings for %s? "
+               "This will not remove any synced information on either "
                "end but it will remove this service configuration."),
-             self->config->name);
-        /*TRANSLATORS: accept button in warning dialog */
+             self->pretty_name);
+        /*TRANSLATORS: buttons in delete-service warning dialog */
         yes = _("Yes, delete");
+        no = _("No, keep settings");
     }
 
-    top_level = gtk_widget_get_toplevel (GTK_WIDGET (self));
-    w = gtk_message_dialog_new (GTK_WINDOW (top_level),
-                                GTK_DIALOG_MODAL,
-                                GTK_MESSAGE_QUESTION,
-                                GTK_BUTTONS_NONE,
-                                msg);
-    /*TRANSLATORS: decline button in warning dialog */
-    gtk_dialog_add_buttons (GTK_DIALOG (w),
-                            _("No, keep settings"),
-                            GTK_RESPONSE_NO,
-                            yes,
-                            GTK_RESPONSE_YES,
-                            NULL);
-    ret = gtk_dialog_run (GTK_DIALOG (w));
-    gtk_widget_destroy (w);
-    g_free (msg);
-
-    if (ret != GTK_RESPONSE_YES) {
+    /*TRANSLATORS: decline button in "Reset/delete service" warning dialogs */
+    if (!show_confirmation (GTK_WIDGET (self), msg, yes, no)) {
+        g_free (msg);
         return;
     }
+    g_free (msg);
 
     if (self->current) {
         sync_config_widget_set_current (self, FALSE);
@@ -427,7 +412,7 @@ reset_delete_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
     data->widget = self;
     data->delete = TRUE;
     syncevo_server_start_session (self->server,
-                                  self->config->name,
+                                  self->config_name,
                                   (SyncevoServerStartSessionCb)start_session_for_config_write_cb,
                                   data);
 }
@@ -463,6 +448,23 @@ static void update_buttons (SyncConfigWidget *self)
     }
 }
 
+static void
+source_widgets_ref (source_widgets *widgets)
+{
+    if (widgets) {
+        widgets->count++;
+    }
+}
+
+static void
+source_widgets_unref (source_widgets *widgets)
+{
+    if (widgets) {
+        widgets->count--;
+        if (widgets->count == 0)
+            g_slice_free (source_widgets, widgets);
+    }
+}
 
 static void
 check_source_cb (SyncevoServer *server,
@@ -470,10 +472,10 @@ check_source_cb (SyncevoServer *server,
                  source_widgets *widgets)
 {
     gboolean show = TRUE;
+
     if (error) {
         if(error->code == DBUS_GERROR_REMOTE_EXCEPTION &&
            dbus_g_error_has_name (error, SYNCEVO_DBUS_ERROR_SOURCE_UNUSABLE)) {
-
             show = FALSE;
         } else if (error->code == DBUS_GERROR_REMOTE_EXCEPTION &&
                    dbus_g_error_has_name (error,
@@ -487,12 +489,15 @@ check_source_cb (SyncevoServer *server,
         g_error_free (error);
     }
 
-    if (show) {
+    if (show && widgets->count > 1) {
+        /* TODO: with the new two sources per row layout not showing things
+         * probably won't look good... */
         gtk_widget_show (widgets->source_toggle_label);
         gtk_widget_show (widgets->label);
         gtk_widget_show (widgets->entry);
         gtk_widget_show (widgets->check);
     }
+    source_widgets_unref (widgets);
 }
 
 static void
@@ -516,7 +521,7 @@ source_entry_notify_text_cb (GObject *gobject,
     old_editable = GTK_WIDGET_SENSITIVE (widgets->check);
     if (new_editable != old_editable) {
         gtk_widget_set_sensitive (widgets->check, new_editable);
-        g_object_set (widgets->check, "active", new_editable, NULL);
+        toggle_set_active (widgets->check, new_editable);
     }
 }
 
@@ -533,39 +538,106 @@ add_toggle_widget (SyncConfigWidget *self,
 
     col = col * 2;
     label = gtk_label_new (title);
+    gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+    gtk_widget_set_size_request (label, 260, -1);
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-    gtk_widget_show (label);
     gtk_table_attach (GTK_TABLE (self->mode_table), label,
                       col, col + 1, row, row + 1,
                       GTK_FILL, GTK_FILL, 0, 0);
     toggle = mx_gtk_light_switch_new ();
+    g_signal_connect_swapped (toggle, "hide",
+                              G_CALLBACK (gtk_widget_hide), label);
+    g_signal_connect_swapped (toggle, "show",
+                              G_CALLBACK (gtk_widget_show), label);
+    toggle_set_active (toggle, active);
+    g_signal_connect (toggle, "switch-flipped",
+                      G_CALLBACK (mode_widget_notify_active_cb), self);
 #else
     toggle = gtk_check_button_new_with_label (title);
+    gtk_widget_set_size_request (toggle, 260, -1);
+    toggle_set_active (toggle, active);
+    g_signal_connect (toggle, "notify::active",
+                      G_CALLBACK (mode_widget_notify_active_cb), self);
 #endif
-    g_object_set (toggle, "active", active, NULL);
-    gtk_widget_show (toggle);
     gtk_table_attach (GTK_TABLE (self->mode_table), toggle,
                       col + 1, col + 2, row, row + 1,
                       GTK_FILL, GTK_FILL, 32, 0);
-    g_signal_connect (toggle, "notify::active",
-                      G_CALLBACK (mode_widget_notify_active_cb), self);
 
     return toggle;
 }
 
+
+/* check if config includes a virtual source that covers the given 
+ * source */
+static gboolean
+virtual_source_exists (SyncevoConfig *config, const char *name)
+{
+    GHashTableIter iter;
+    const char *source_string;
+    GHashTable *source_config;
+
+    g_hash_table_iter_init (&iter, config);
+    while (g_hash_table_iter_next (&iter,
+                                   (gpointer)&source_string,
+                                   (gpointer)&source_config)) {
+        char **strs;
+
+        if (g_str_has_prefix (source_string, "source/")) {
+            const char *uri, *type;
+            type = g_hash_table_lookup (source_config, "type");
+            uri = g_hash_table_lookup (source_config, "uri");
+
+            if (!uri || !type || !g_str_has_prefix (type, "virtual:")) {
+                /* this source is not defined, or not virtual */
+                continue;
+            }
+
+            strs = g_strsplit (source_string + 7, "+", 0);
+            if (g_strv_length (strs) > 1) {
+                int i;
+
+                for (i = 0; strs[i]; i++) {
+                    if (g_strcmp0 (name, strs[i]) == 0) {
+                        g_strfreev (strs);
+                        return TRUE;
+                    }
+                }
+            }
+            g_strfreev (strs);
+        }
+    }
+
+    return FALSE;
+}
+
 static void
 init_source (char *name,
              GHashTable *source_configuration,
              SyncConfigWidget *self)
 {
     char *str, *pretty_name;
-    const char *uri;
+    const char *uri, *type;
     guint rows;
     guint row;
     static guint col = 0;
     source_widgets *widgets;
     SyncevoSyncMode mode;
 
+    type = g_hash_table_lookup (source_configuration, "type");
+    uri = g_hash_table_lookup (source_configuration, "uri");
+    if (!type || strlen (type) == 0) {
+        return;
+    }
+
+    if (g_str_has_prefix (type, "virtual:") && !uri) {
+        /* undefined virtual source */
+        return;
+    }
+
+    if (virtual_source_exists (self->config, name)) {
+        return;
+    }
+
     g_object_get (self->mode_table,
                   "n-rows", &rows,
                   NULL);
@@ -579,12 +651,12 @@ init_source (char *name,
     }
     self->no_source_toggles = FALSE;
 
-    widgets = g_slice_new (source_widgets);
+    widgets = g_slice_new0 (source_widgets);
+    widgets->count = 1;
     g_hash_table_insert (self->sources, name, widgets);
 
     widgets->source_toggle_label = self->source_toggle_label;
 
-    uri = g_hash_table_lookup (source_configuration, "uri");
     pretty_name = get_pretty_source_name (name);
     mode = syncevo_sync_mode_from_string
         (g_hash_table_lookup (source_configuration, "sync"));
@@ -625,14 +697,16 @@ init_source (char *name,
     gtk_widget_set_sensitive (widgets->check,
                               uri && strlen (uri) > 0);
 
+    /* TODO: template sources cannot be checked. Should set a temporary config
+     * to check sources */
     if (self->configured) {
+        source_widgets_ref (widgets);
         syncevo_server_check_source (self->server,
-                                     self->config->name,
+                                     self->config_name,
                                      name,
                                      (SyncevoServerGenericCb)check_source_cb,
                                      widgets);
     } else {
-        /* TODO: should do a temp config to test eve n template sources */
         gtk_widget_show (widgets->source_toggle_label);
         gtk_widget_show (widgets->label);
         gtk_widget_show (widgets->entry);
@@ -646,7 +720,12 @@ get_common_mode (char *name,
                  SyncevoSyncMode *common_mode)
 {
     SyncevoSyncMode mode;
-    char *mode_str;
+    char *mode_str, *type;
+
+    type = g_hash_table_lookup (source_configuration, "type");
+    if (!type || strlen (type) == 0) {
+        return;
+    }
 
     mode_str = g_hash_table_lookup (source_configuration, "sync");
     mode = syncevo_sync_mode_from_string (mode_str);
@@ -662,15 +741,26 @@ get_common_mode (char *name,
     }
 }
 
-static void
-source_widgets_free (source_widgets *widgets)
+void
+sync_config_widget_expand_id (SyncConfigWidget *self,
+                              const char *id)
 {
-    if (widgets) {
-        g_slice_free (source_widgets, widgets);
+    if (id && self->config) {
+        char *sync_url;
+
+        if (syncevo_config_get_value (self->config, NULL,
+                                      "syncURL", &sync_url) &&
+            strncmp (sync_url, id, strlen (id)) == 0) {
+
+            sync_config_widget_set_expanded (self, TRUE);
+        } else if (self->config_name &&
+                   g_strcasecmp (self->config_name, id) == 0) {
+
+            sync_config_widget_set_expanded (self, TRUE);
+        }
     }
 }
 
-
 static void
 sync_config_widget_update_expander (SyncConfigWidget *self)
 {
@@ -678,11 +768,10 @@ sync_config_widget_update_expander (SyncConfigWidget *self)
     char *password = "";
     char *sync_url = "";
     const char *descr;
-    char *str;
+    char *str, *device;
     GtkWidget *label, *align;
     SyncevoSyncMode mode = SYNCEVO_SYNC_NONE;
     gboolean send, receive;
-    SyncevoConfig *config = self->config->config;
 
     gtk_container_foreach (GTK_CONTAINER (self->server_settings_table),
                            (GtkCallback)remove_child,
@@ -695,10 +784,27 @@ sync_config_widget_update_expander (SyncConfigWidget *self)
     gtk_table_resize (GTK_TABLE (self->mode_table),
                       2, 1);
 
-    /* TODO: sources that are not supported locally will trigger the complex
-     * config warning for no real reason... should do get_common_mode only after
-     * check_source calls, and make sure unsupported sources do not get edited */
-    syncevo_config_foreach_source (config,
+    syncevo_config_get_value (self->config, NULL, "PeerIsClient", &device);
+    if (device && g_strcmp0 (device, "1") == 0) {
+        if (!self->device_template_selected) {
+            gtk_widget_hide (self->settings_box);
+            gtk_widget_show (self->device_selector_box);
+            /* temporary solution for device template selection:
+             * show list of templates only */
+        } else {
+            gtk_widget_show (self->settings_box);
+            gtk_widget_hide (self->device_selector_box);
+            gtk_widget_hide (self->userinfo_table);
+            gtk_widget_hide (self->fake_expander);
+        }
+    } else {
+        gtk_widget_show (self->settings_box);
+        gtk_widget_hide (self->device_selector_box);
+        gtk_widget_show (self->userinfo_table);
+        gtk_widget_show (self->fake_expander);
+    }
+
+    syncevo_config_foreach_source (self->config,
                                    (ConfigFunc)get_common_mode,
                                    &mode);
     switch (mode) {
@@ -715,24 +821,23 @@ sync_config_widget_update_expander (SyncConfigWidget *self)
         break;
     default:
         gtk_widget_show (self->complex_config_info_bar);
-        g_warning ("sync mode config is more complex than UI can handle");
         send = FALSE;
         receive = FALSE;
     }
     self->mode_changed = FALSE;
 
 
-    if (self->config->name) {
-        gtk_entry_set_text (GTK_ENTRY (self->entry), self->config->name);
+    if (self->pretty_name) {
+        gtk_entry_set_text (GTK_ENTRY (self->entry), self->pretty_name);
     }
-    if (!self->config->name || strlen (self->config->name) == 0) {
+    if (!self->config_name || strlen (self->config_name) == 0) {
         gtk_expander_set_expanded (GTK_EXPANDER (self->expander), TRUE);
     }
 
-    descr = get_service_description (self->config->name);
+    descr = get_service_description (self->config_name);
     if (descr) {
         gtk_label_set_text (GTK_LABEL (self->description_label),
-                            get_service_description (self->config->name));
+                            get_service_description (self->config_name));
         gtk_widget_show (self->description_label);
     } else {
         gtk_widget_hide (self->description_label);
@@ -740,13 +845,13 @@ sync_config_widget_update_expander (SyncConfigWidget *self)
 
     update_buttons (self);
 
-    /* TRANSLATORS: check button (or toggle) in service configuration form */
-    str = g_strdup_printf (_("Send changes to %s"), self->config->name);
+    /* TRANSLATORS: toggles in service configuration form, placeholder is service
+     * or device name */
+    str = g_strdup_printf (_("Send changes to %s"), self->pretty_name);
     self->send_check = add_toggle_widget (self, str, send, 0, 0);
     g_free (str);
 
-    /* TRANSLATORS: check button (or toggle) in service configuration form */
-    str = g_strdup_printf (_("Receive changes from %s"), self->config->name);
+    str = g_strdup_printf (_("Receive changes from %s"), self->pretty_name);
     self->receive_check = add_toggle_widget (self, str, receive, 0, 1);
     g_free (str);
 
@@ -765,9 +870,9 @@ sync_config_widget_update_expander (SyncConfigWidget *self)
     gtk_widget_show (self->source_toggle_label);
     gtk_container_add (GTK_CONTAINER (align), self->source_toggle_label);
 
-    syncevo_config_get_value (config, NULL, "username", &username);
-    syncevo_config_get_value (config, NULL, "password", &password);
-    syncevo_config_get_value (config, NULL, "syncURL", &sync_url);
+    syncevo_config_get_value (self->config, NULL, "username", &username);
+    syncevo_config_get_value (self->config, NULL, "password", &password);
+    syncevo_config_get_value (self->config, NULL, "syncURL", &sync_url);
 
     if (username) {
         gtk_entry_set_text (GTK_ENTRY (self->username_entry), username);
@@ -802,13 +907,51 @@ sync_config_widget_update_expander (SyncConfigWidget *self)
     self->sources = g_hash_table_new_full (g_str_hash,
                                            g_str_equal,
                                            NULL,
-                                           (GDestroyNotify)source_widgets_free);
+                                           (GDestroyNotify)source_widgets_unref);
     self->no_source_toggles = TRUE;
-    syncevo_config_foreach_source (config,
+    syncevo_config_foreach_source (self->config,
                                    (ConfigFunc)init_source,
                                    self);
 }
 
+/* only adds config to hashtable and combo */
+static void
+sync_config_widget_add_config (SyncConfigWidget *self,
+                               const char *name,
+                               SyncevoConfig *config)
+{
+    g_hash_table_insert (self->configs, g_strdup (name), config);
+    gtk_combo_box_prepend_text (GTK_COMBO_BOX (self->combo), name);
+}
+
+static void
+sync_config_widget_update_pretty_name (SyncConfigWidget *self)
+{
+    self->pretty_name = NULL;
+
+    if (self->config) {
+        syncevo_config_get_value (self->config, NULL,
+                                  "PeerName", &self->pretty_name);
+        if (!self->pretty_name) {
+            syncevo_config_get_value (self->config, NULL,
+                                      "deviceName", &self->pretty_name);
+        }
+    }
+
+    if (!self->pretty_name) {
+        self->pretty_name = self->config_name;
+    }
+}
+
+static void
+sync_config_widget_set_config (SyncConfigWidget *self,
+                               SyncevoConfig *config)
+{
+    self->config = config;
+    sync_config_widget_update_pretty_name (self);
+}
+
+
 static void
 setup_service_clicked (GtkButton *btn, SyncConfigWidget *self)
 {
@@ -816,6 +959,35 @@ setup_service_clicked (GtkButton *btn, SyncConfigWidget *self)
 }
 
 static void
+sync_config_widget_set_name (SyncConfigWidget *self,
+                             const char *name)
+{
+    g_free (self->config_name);
+    self->config_name = g_strdup (name);
+    sync_config_widget_update_pretty_name (self);
+}
+
+
+static void
+device_selection_btn_clicked_cb (GtkButton *btn, SyncConfigWidget *self)
+{
+    const char *name;
+    SyncevoConfig *config;
+
+    self->device_template_selected = TRUE;
+
+    name = gtk_combo_box_get_active_text (GTK_COMBO_BOX (self->combo));
+    config = g_hash_table_lookup (self->configs, name);
+
+    g_return_if_fail (config);
+
+    sync_config_widget_set_config (self, config);
+    sync_config_widget_set_name (self, name);
+
+    sync_config_widget_update_expander (self);
+}
+
+static void
 server_settings_notify_expand_cb (GtkExpander *expander,
                                   GParamSpec *pspec,
                                   SyncConfigWidget *self)
@@ -879,18 +1051,23 @@ load_icon (const char *uri, guint icon_size)
 static void
 sync_config_widget_update_label (SyncConfigWidget *self)
 {
-    if (self->config && self->config->name && self->config->config) {
-        char *url;
+    if (self->config && self->pretty_name) {
+        char *url, *sync_url;
         char *str;
 
-        syncevo_config_get_value (self->config->config, NULL, "WebURL", &url);
+        syncevo_config_get_value (self->config, NULL, "WebURL", &url);
+        syncevo_config_get_value (self->config, NULL, "syncURL", &sync_url);
 
         if (self->current) {
-            str = g_strdup_printf ("<b>%s</b>", self->config->name);
+            str = g_strdup_printf ("<b>%s</b>", self->pretty_name);
         } else {
-            str = g_strdup_printf ("%s", self->config->name);
+            str = g_strdup_printf ("%s", self->pretty_name);
         }
-        if (!self->has_template) {
+        if (g_str_has_prefix (sync_url, "obex-bt://")) {
+            char *tmp = g_strdup_printf (_("%s - Bluetooth device"), str);
+            g_free (str);
+            str = tmp;
+        } else if (!self->has_template) {
             /* TRANSLATORS: service title for services that are not based on a 
              * template in service list, the placeholder is the name of the service */
             char *tmp = g_strdup_printf (_("%s - manually setup"), str);
@@ -951,8 +1128,7 @@ session_changed_cb (SyncevoServer *server,
 
     if (started) {
         set_session (self, path);
-    } else if (self->running_session &&
-               strcmp (self->running_session, path) == 0 ) {
+    } else if (g_strcmp0 (self->running_session, path) == 0 ) {
         set_session (self, NULL);
     }
 }
@@ -1002,14 +1178,6 @@ sync_config_widget_set_server (SyncConfigWidget *self,
 }
 
 static void
-sync_config_widget_set_name (SyncConfigWidget *self,
-                             const char *name)
-{
-    self->config = g_slice_new0 (server_config);
-    self->config->name = g_strdup (name);
-}
-
-static void
 sync_config_widget_set_property (GObject      *object,
                                         guint         property_id,
                                         const GValue *value,
@@ -1024,6 +1192,9 @@ sync_config_widget_set_property (GObject      *object,
     case PROP_NAME:
         sync_config_widget_set_name (self, g_value_get_string (value));
         break;
+    case PROP_CONFIG:
+        sync_config_widget_set_config (self, g_value_get_pointer (value));
+        break;
     case PROP_CURRENT:
         sync_config_widget_set_current (self, g_value_get_boolean (value));
         break;
@@ -1056,10 +1227,9 @@ sync_config_widget_get_property (GObject    *object,
     case PROP_SERVER:
         g_value_set_pointer (value, self->server);
     case PROP_NAME:
-        if (self->config)
-            g_value_set_string (value, self->config->name);
-        else
-            g_value_set_string (value, NULL);
+        g_value_set_string (value, self->config_name);
+    case PROP_CONFIG:
+        g_value_set_pointer (value, self->config);
     case PROP_CURRENT:
         g_value_set_boolean (value, self->current);
     case PROP_HAS_TEMPLATE:
@@ -1069,7 +1239,7 @@ sync_config_widget_get_property (GObject    *object,
     case PROP_CURRENT_SERVICE_NAME:
         g_value_set_string (value, self->current_service_name);
     case PROP_EXPANDED:
-      g_value_set_boolean (value, self->expanded);
+        g_value_set_boolean (value, self->expanded);
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     }
@@ -1081,90 +1251,45 @@ sync_config_widget_dispose (GObject *object)
     SyncConfigWidget *self = SYNC_CONFIG_WIDGET (object);
 
     sync_config_widget_set_server (self, NULL);
-
-    G_OBJECT_CLASS (sync_config_widget_parent_class)->dispose (object);
-}
-
-static void
-init_default_config (server_config *config)
-{
-    g_free (config->name);
-    config->name = g_strdup ("");
-
-    syncevo_config_set_value (config->config, NULL, "username", "");
-    syncevo_config_set_value (config->config, NULL, "password", "");
-    syncevo_config_set_value (config->config, NULL, "syncURL", "");
-    syncevo_config_set_value (config->config, NULL, "WebURL", "");
-    syncevo_config_set_value (config->config, "memo", "uri", "");
-    syncevo_config_set_value (config->config, "todo", "uri", "");
-    syncevo_config_set_value (config->config, "addressbook", "uri", "");
-    syncevo_config_set_value (config->config, "calendar", "uri", "");
-
-}
-
-static void
-sync_config_widget_real_init (SyncConfigWidget *self,
-                              SyncevoConfig *config)
-{
-    char *url, *icon;
-    GdkPixbuf *buf;
-    server_config_init (self->config, config);
-    if (self->config->name &&
-        strcmp (self->config->name, "default") == 0) {
-
-        init_default_config (self->config);
-        self->has_template = FALSE;
-        gtk_widget_show (self->entry);
-        gtk_widget_hide (self->label);
-    } else {
-        /**/
-        gtk_widget_hide (self->entry);
-        gtk_widget_show (self->label);
+    if (self->config) {
+        syncevo_config_free (self->config);
     }
+    self->config = NULL;
 
-    syncevo_config_get_value (self->config->config, NULL, "WebURL", &url);
-    syncevo_config_get_value (self->config->config, NULL, "IconURI", &icon);
-
-    buf = load_icon (icon, SYNC_UI_LIST_ICON_SIZE);
-    gtk_image_set_from_pixbuf (GTK_IMAGE (self->image), buf);
-    g_object_unref (buf);
-
-    if (url && strlen (url) > 0) {
-        gtk_link_button_set_uri (GTK_LINK_BUTTON (self->link), url);
-        gtk_widget_show (self->link);
-    } else {
-        gtk_widget_hide (self->link);
+    if (self->configs) {
+        g_hash_table_destroy (self->configs);
+        self->configs = NULL;
+    }
+    g_free (self->config_name);
+    self->config_name = NULL;
+    g_free (self->current_service_name);
+    self->current_service_name = NULL;
+    g_free (self->running_session);
+    self->running_session = NULL;
+    if (self->sources) {
+        g_hash_table_destroy (self->sources);
+        self->sources = NULL;
     }
 
-    sync_config_widget_update_label (self);
-    sync_config_widget_update_expander (self);
 
-    if (self->showing) {
-        gtk_widget_show (GTK_WIDGET (self));
 
-        /* hack to get focus in the right place on "Setup new service" */
-        if (GTK_WIDGET_VISIBLE (self->entry)) {
-            gtk_widget_grab_focus (self->entry);
-        }
-    }
+    G_OBJECT_CLASS (sync_config_widget_parent_class)->dispose (object);
 }
 
 static void
-get_config_cb (SyncevoServer *syncevo,
-               SyncevoConfig *config,
-               GError *error,
-               SyncConfigWidget *self)
+init_default_config (SyncConfigWidget *self)
 {
-    if (error) {
-        g_warning ("Server.GetConfig failed: %s", error->message);
-        g_error_free (error);
-        g_object_thaw_notify (G_OBJECT (self));
+    sync_config_widget_set_name (self, "");
+    self->has_template = FALSE;
 
-        /* TODO: show in UI */
-        return;
-    }
-    sync_config_widget_real_init (self, config);
-    g_object_thaw_notify (G_OBJECT (self));
+    syncevo_config_set_value (self->config, NULL, "username", "");
+    syncevo_config_set_value (self->config, NULL, "password", "");
+    syncevo_config_set_value (self->config, NULL, "syncURL", "");
+    syncevo_config_set_value (self->config, NULL, "WebURL", "");
+    syncevo_config_set_value (self->config, "memo", "uri", "");
+    syncevo_config_set_value (self->config, "todo", "uri", "");
+    syncevo_config_set_value (self->config, "addressbook", "uri", "");
+    syncevo_config_set_value (self->config, "calendar", "uri", "");
 
 }
 
@@ -1223,27 +1348,6 @@ sync_config_widget_expose_event (GtkWidget      *widget,
                    rect.width,
                    rect.height);
 
-    if (self->expanded) {
-        gint shadow_x, shadow_y;
-
-        shadow_x = rect.x + widget->style->xthickness;
-        shadow_y = rect.y + 2 * widget->style->ythickness +
-                   self->label_box->allocation.height;
-
-        gtk_paint_box (widget->style,
-                       widget->window,
-                       widget->state,
-                       GTK_SHADOW_IN,
-                       &rect,
-                       widget,
-                       NULL,
-                       shadow_x,
-                       shadow_y,
-                       rect.width - (shadow_x - rect.x) - widget->style->xthickness,
-                       rect.height - (shadow_y - rect.y) - widget->style->ythickness);
-    }
-
-
     gtk_container_propagate_expose (GTK_CONTAINER (self),
                                     self->label_box, event);
     gtk_container_propagate_expose (GTK_CONTAINER (self),
@@ -1319,53 +1423,54 @@ sync_config_widget_constructor (GType                  gtype,
 {
     SyncConfigWidget *self;
     GObjectClass *parent_class;  
+    char *url, *icon;
+    GdkPixbuf *buf;
 
     parent_class = G_OBJECT_CLASS (sync_config_widget_parent_class);
     self = SYNC_CONFIG_WIDGET (parent_class->constructor (gtype,
                                                           n_properties,
                                                           properties));
 
-    if (!self->server) {
-        g_warning ("No SyncevoServer set for SyncConfigWidget");
+    if (!self->config || !self->server) {
+        g_warning ("No SyncevoServer or Syncevoconfig set for SyncConfigWidget");
+        return G_OBJECT (self);
     }
 
-    /* freeze notifys so we don't claim to have expanded until we have...
-       this could be achieved in more clean ways as well... */
-    g_object_freeze_notify (G_OBJECT (self));
-    syncevo_server_get_config (self->server,
-                               self->config->name,
-                               self->has_template && !self->configured,
-                               (SyncevoServerGetConfigCb)get_config_cb,
-                               self);
-    return G_OBJECT (self);
-}
+    sync_config_widget_add_config (self, self->config_name, self->config);
 
-static void
-sync_config_widget_show (GtkWidget *widget)
-{
-    char *ready;
-    SyncConfigWidget *self = SYNC_CONFIG_WIDGET (widget);
+    if (g_strcmp0 (self->config_name, "default") == 0) {
 
-    /* this is a bit dirty... might be better to show the widget
-     * in any case and handle removing non-ready templates otherwise */
-    self->showing = TRUE;
-    if (self->config && self->config->config) {
-        syncevo_config_get_value (self->config->config,
-                                  NULL, "ConsumerReady", &ready);
+        init_default_config (self);
+        gtk_widget_show (self->entry);
+        gtk_widget_hide (self->label);
+    } else {
+        gtk_widget_hide (self->entry);
+        gtk_widget_show (self->label);
+    }
 
-        if (self->configured || g_strcmp0 ("1", ready) == 0) {
-            GTK_WIDGET_CLASS (sync_config_widget_parent_class)->show (widget);
-        }
+    syncevo_config_get_value (self->config, NULL, "WebURL", &url);
+    syncevo_config_get_value (self->config, NULL, "IconURI", &icon);
+
+    buf = load_icon (icon, SYNC_UI_LIST_ICON_SIZE);
+    gtk_image_set_from_pixbuf (GTK_IMAGE (self->image), buf);
+    g_object_unref (buf);
+
+    if (url && strlen (url) > 0) {
+        gtk_link_button_set_uri (GTK_LINK_BUTTON (self->link), url);
+        gtk_widget_show (self->link);
+    } else {
+        gtk_widget_hide (self->link);
     }
-}
 
-static void
-sync_config_widget_hide (GtkWidget *widget)
-{
-    SyncConfigWidget *self = SYNC_CONFIG_WIDGET (widget);
+    sync_config_widget_update_label (self);
+    sync_config_widget_update_expander (self);
+
+    /* hack to get focus in the right place on "Setup new service" */
+    if (GTK_WIDGET_VISIBLE (self->entry)) {
+        gtk_widget_grab_focus (self->entry);
+    }
 
-    self->showing = FALSE;
-    GTK_WIDGET_CLASS (sync_config_widget_parent_class)->hide (widget);
+    return G_OBJECT (self);
 }
 
 static void
@@ -1398,18 +1503,6 @@ sync_config_widget_unmap (GtkWidget *widget)
 }
 
 static void
-sync_config_widget_realize (GtkWidget *widget)
-{
-    GTK_WIDGET_CLASS (sync_config_widget_parent_class)->realize (widget);
-}
-
-static void
-sync_config_widget_unrealize (GtkWidget *widget)
-{
-    GTK_WIDGET_CLASS (sync_config_widget_parent_class)->unrealize (widget);
-}
-
-static void
 sync_config_widget_add (GtkContainer *container,
                         GtkWidget    *widget)
 {
@@ -1463,15 +1556,11 @@ sync_config_widget_class_init (SyncConfigWidgetClass *klass)
     object_class->dispose = sync_config_widget_dispose;
     object_class->constructor = sync_config_widget_constructor;
 
-    w_class->show = sync_config_widget_show;
-    w_class->hide = sync_config_widget_hide;
     w_class->expose_event = sync_config_widget_expose_event;
     w_class->size_request = sync_config_widget_size_request;
     w_class->size_allocate = sync_config_widget_size_allocate;
     w_class->map = sync_config_widget_map;
     w_class->unmap = sync_config_widget_unmap;
-    w_class->realize = sync_config_widget_realize;
-    w_class->unrealize = sync_config_widget_unrealize;
 
     c_class->add = sync_config_widget_add;
     c_class->remove = sync_config_widget_remove;
@@ -1479,7 +1568,7 @@ sync_config_widget_class_init (SyncConfigWidgetClass *klass)
 
     pspec = g_param_spec_pointer ("server",
                                   "SyncevoServer",
-                                  "The SyncevoServer struct this widget represents",
+                                  "The SyncevoServer to use in Syncevolution DBus calls",
                                   G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
     g_object_class_install_property (object_class, PROP_SERVER, pspec);
 
@@ -1490,6 +1579,12 @@ sync_config_widget_class_init (SyncConfigWidgetClass *klass)
                                  G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
     g_object_class_install_property (object_class, PROP_NAME, pspec);
 
+    pspec = g_param_spec_pointer ("config",
+                                  "SyncevoConfig",
+                                  "The SyncevoConfig struct this widget represents. Takes ownership.",
+                                  G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+    g_object_class_install_property (object_class, PROP_CONFIG, pspec);
+
     pspec = g_param_spec_boolean ("current",
                                   "Current",
                                   "Whether the service is currently used",
@@ -1525,15 +1620,6 @@ sync_config_widget_class_init (SyncConfigWidgetClass *klass)
                                   G_PARAM_READWRITE);
     g_object_class_install_property (object_class, PROP_EXPANDED, pspec);
 
-    pspec = g_param_spec_int ("expander-size",
-                              "Expander Size",
-                              "Size of the expander indicator",
-                              0, G_MAXINT, INDICATOR_SIZE,
-                              G_PARAM_READABLE);
-
-  gtk_widget_class_install_style_property (w_class, pspec);
-
-
     signals[SIGNAL_CHANGED] = 
             g_signal_new ("changed",
                           G_TYPE_FROM_CLASS (klass),
@@ -1583,10 +1669,14 @@ label_button_release_cb (GtkWidget *widget,
 static void
 sync_config_widget_init (SyncConfigWidget *self)
 {
-    GtkWidget *tmp_box, *hbox, *cont, *vbox, *table, *label;
+    GtkWidget *tmp_box, *hbox, *cont, *vbox, *label, *btn;
 
     GTK_WIDGET_SET_FLAGS (GTK_WIDGET (self), GTK_NO_WINDOW);
 
+    /* should free the config? */
+    self->configs = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                           g_free, NULL);
+
     self->label_box = gtk_event_box_new ();
     gtk_widget_set_app_paintable (self->label_box, TRUE);
     gtk_widget_show (self->label_box);
@@ -1619,6 +1709,8 @@ sync_config_widget_init (SyncConfigWidget *self)
     gtk_box_pack_start (GTK_BOX (hbox), tmp_box, FALSE, FALSE, 8);
 
     self->label = gtk_label_new ("");
+    gtk_label_set_max_width_chars (GTK_LABEL (self->label), 60);
+    gtk_label_set_ellipsize (GTK_LABEL (self->label), PANGO_ELLIPSIZE_END);
     gtk_misc_set_alignment (GTK_MISC (self->label), 0.0, 0.5);
     gtk_widget_show (self->label);
     gtk_box_pack_start (GTK_BOX (tmp_box), self->label, FALSE, FALSE, 0);
@@ -1653,9 +1745,36 @@ sync_config_widget_init (SyncConfigWidget *self)
     gtk_widget_set_no_show_all (self->expando_box, TRUE);
     gtk_widget_set_parent (self->expando_box, GTK_WIDGET (self));
 
+    /* device_selector_box does device template selection */
+    self->device_selector_box = gtk_vbox_new (FALSE, 0);
+    gtk_box_pack_start (GTK_BOX (self->expando_box), self->device_selector_box,
+                        TRUE, TRUE, 16);
+
+    hbox = gtk_hbox_new (FALSE, 8);
+    gtk_widget_show (hbox);
+    gtk_box_pack_start (GTK_BOX (self->device_selector_box), hbox,
+                        FALSE, TRUE, 16);
+    self->combo = gtk_combo_box_new_text ();
+    gtk_widget_set_size_request (self->combo, 200, -1);
+    gtk_widget_show (self->combo);
+    gtk_box_pack_start (GTK_BOX (hbox), self->combo,
+                        FALSE, TRUE, 0);
+    btn = gtk_button_new_with_label ("Use these settings");
+    gtk_widget_show (btn);
+    gtk_box_pack_start (GTK_BOX (hbox), btn,
+                        FALSE, TRUE, 0);
+    g_signal_connect (btn, "clicked",
+                      G_CALLBACK (device_selection_btn_clicked_cb), self);
+
+    /* settings_box has normal expander contents */
+    self->settings_box = gtk_vbox_new (FALSE, 0);
+    gtk_widget_show (self->settings_box);
+    gtk_box_pack_start (GTK_BOX (self->expando_box), self->settings_box,
+                        TRUE, TRUE, 16);
+
     vbox = gtk_vbox_new (FALSE, 8);
     gtk_widget_show (vbox);
-    gtk_box_pack_start (GTK_BOX (self->expando_box), vbox, TRUE, TRUE, 16);
+    gtk_box_pack_start (GTK_BOX (self->settings_box), vbox, TRUE, TRUE, 0);
 
     tmp_box = gtk_vbox_new (FALSE, 0);
     gtk_widget_show (tmp_box);
@@ -1670,17 +1789,17 @@ sync_config_widget_init (SyncConfigWidget *self)
     gtk_widget_show (tmp_box);
     gtk_box_pack_start (GTK_BOX (vbox), tmp_box, FALSE, FALSE, 0);
 
-    table = gtk_table_new (4, 2, FALSE);
-    gtk_table_set_row_spacings (GTK_TABLE (table), 2);
-    gtk_table_set_col_spacings (GTK_TABLE (table), 5);
-    gtk_widget_show (table);
-    gtk_box_pack_start (GTK_BOX (tmp_box), table, FALSE, FALSE, 0);
+    self->userinfo_table = gtk_table_new (4, 2, FALSE);
+    gtk_table_set_row_spacings (GTK_TABLE (self->userinfo_table), 2);
+    gtk_table_set_col_spacings (GTK_TABLE (self->userinfo_table), 5);
+    gtk_widget_show (self->userinfo_table);
+    gtk_box_pack_start (GTK_BOX (tmp_box), self->userinfo_table, FALSE, FALSE, 0);
 
     /* TRANSLATORS: labels of entries in service configuration form */
     label = gtk_label_new (_("Username"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_widget_show (label);
-    gtk_table_attach_defaults (GTK_TABLE (table), label,
+    gtk_table_attach_defaults (GTK_TABLE (self->userinfo_table), label,
                                0, 1,
                                0, 1);
 
@@ -1688,14 +1807,14 @@ sync_config_widget_init (SyncConfigWidget *self)
     gtk_widget_show (self->username_entry);
     gtk_entry_set_width_chars (GTK_ENTRY (self->username_entry), 40);
     gtk_entry_set_max_length (GTK_ENTRY (self->username_entry), 99);
-    gtk_table_attach_defaults (GTK_TABLE (table), self->username_entry,
+    gtk_table_attach_defaults (GTK_TABLE (self->userinfo_table), self->username_entry,
                                1, 2,
                                0, 1);
 
     label = gtk_label_new (_("Password"));
     gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
     gtk_widget_show (label);
-    gtk_table_attach_defaults (GTK_TABLE (table), label,
+    gtk_table_attach_defaults (GTK_TABLE (self->userinfo_table), label,
                                0, 1,
                                1, 2);
 
@@ -1704,7 +1823,7 @@ sync_config_widget_init (SyncConfigWidget *self)
     gtk_entry_set_width_chars (GTK_ENTRY (self->password_entry), 40);
     gtk_entry_set_visibility (GTK_ENTRY (self->password_entry), FALSE);
     gtk_entry_set_max_length (GTK_ENTRY (self->password_entry), 99);
-    gtk_table_attach_defaults (GTK_TABLE (table), self->password_entry,
+    gtk_table_attach_defaults (GTK_TABLE (self->userinfo_table), self->password_entry,
                                1, 2,
                                1, 2);
 
@@ -1783,6 +1902,7 @@ sync_config_widget_init (SyncConfigWidget *self)
 GtkWidget*
 sync_config_widget_new (SyncevoServer *server,
                         const char *name,
+                        SyncevoConfig *config,
                         gboolean current,
                         const char *current_service_name,
                         gboolean configured,
@@ -1791,6 +1911,7 @@ sync_config_widget_new (SyncevoServer *server,
   return g_object_new (SYNC_TYPE_CONFIG_WIDGET,
                        "server", server,
                        "name", name,
+                       "config", config,
                        "current", current,
                        "current-service-name", current_service_name,
                        "configured", configured,
@@ -1850,6 +1971,7 @@ sync_config_widget_set_configured (SyncConfigWidget *self, gboolean configured)
 {
     if (self->configured != configured) {
         self->configured = configured;
+        self->device_template_selected = configured;
         update_buttons (self);
     }
 }
@@ -1870,8 +1992,20 @@ sync_config_widget_get_current (SyncConfigWidget *widget)
 const char*
 sync_config_widget_get_name (SyncConfigWidget *widget)
 {
-    if (!widget->config)
-        return NULL;
+    return widget->config_name;
+}
 
-    return widget->config->name;
+void
+sync_config_widget_add_alternative_config (SyncConfigWidget *self,
+                                           const char *name,
+                                           SyncevoConfig *config,
+                                           gboolean configured)
+{
+    if (configured) {
+        sync_config_widget_set_config (self, config);
+        sync_config_widget_set_configured (self, TRUE);    
+    } else  {
+        sync_config_widget_add_config (self, name, config);
+    }
+    sync_config_widget_update_expander (self);
 }
index 41af192..c29f3a1 100644 (file)
@@ -5,7 +5,6 @@
 #include <gtk/gtk.h>
 
 #include "syncevo-server.h"
-#include "sync-ui-config.h"
 
 G_BEGIN_DECLS
 
@@ -32,18 +31,29 @@ typedef struct {
     GtkWidget *expando_box;
     GtkWidget *label_box;
 
+    GtkWidget *device_selector_box;
+    GtkWidget *combo;
+
+    GtkWidget *settings_box;
+
     gboolean current; /* is this currently used config */
     char *current_service_name; /* name of the current service */
     gboolean configured; /* actual service configuration exists on server */
+    gboolean device_template_selected;
     gboolean has_template; /* this service configuration has a matching template */
-    gboolean showing;
     gboolean expanded;
 
     SyncevoServer *server;
-    server_config *config;
-    
+    SyncevoConfig *config;
+    GHashTable *configs; /* possible configs. config above is one of these */
+
+    char *config_name;
+    char *pretty_name;
+
     char *running_session;
 
+    char *expand_id;
+
     /* label */
     GtkWidget *image;
     GtkWidget *label;
@@ -53,6 +63,7 @@ typedef struct {
 
     /* content */
     GtkWidget *description_label;
+    GtkWidget *userinfo_table;
     GtkWidget *name_label;
     GtkWidget *name_entry;
     GtkWidget *complex_config_info_bar;
@@ -87,6 +98,7 @@ GType sync_config_widget_get_type (void);
 
 GtkWidget *sync_config_widget_new (SyncevoServer *server,
                                    const char *name,
+                                   SyncevoConfig *config,
                                    gboolean current,
                                    const char *current_service_name,
                                    gboolean configured,
@@ -105,6 +117,9 @@ void sync_config_widget_set_configured (SyncConfigWidget *self, gboolean configu
 gboolean sync_config_widget_get_configured (SyncConfigWidget *self);
 
 const char *sync_config_widget_get_name (SyncConfigWidget *widget);
+
+void sync_config_widget_expand_id (SyncConfigWidget *self, const char *id);
+void sync_config_widget_add_alternative_config (SyncConfigWidget *self, const char *name, SyncevoConfig *config, gboolean configured);
 G_END_DECLS
 
 
diff --git a/src/gtk-ui/sync-spinner.gif b/src/gtk-ui/sync-spinner.gif
new file mode 100644 (file)
index 0000000..f4bc16d
Binary files /dev/null and b/src/gtk-ui/sync-spinner.gif differ
index eafe066..4154ee4 100644 (file)
@@ -82,9 +82,26 @@ server_config_init (server_config *server, SyncevoConfig *config)
     syncevo_config_foreach_source (config,
                                    (ConfigFunc)add_source_config,
                                    server->source_configs);
-
+    if (!syncevo_config_get_value (config, NULL, "PeerName", &server->pretty_name)) {
+        server->pretty_name = server->name;
+    }
 }
 
+gboolean
+source_config_is_usable (source_config *source)
+{
+    const char *source_uri;
+
+    source_uri = g_hash_table_lookup (source->config, "uri");
+
+    if (!source_config_is_enabled (source) ||
+        !source_uri ||
+        strlen (source_uri) == 0 ||
+        !source->supported_locally) {
+        return FALSE;
+    }
+    return TRUE;
+}
 
 gboolean
 source_config_is_enabled (source_config *source)
@@ -92,7 +109,6 @@ source_config_is_enabled (source_config *source)
     char *mode;
 
     mode = g_hash_table_lookup (source->config, "sync");
-
     if (mode &&
         (strcmp (mode, "none") == 0 ||
          strcmp (mode, "disabled") == 0)) {
index 839afb6..696bd61 100644 (file)
 #include "syncevo-session.h"
 #include "syncevo-server.h"
 
-/* need a separate struct for sources because we need to know local support ... */
 typedef struct source_config {
     char *name;
     gboolean supported_locally;
 
+    SyncevoSourcePhase phase;
+
     gboolean stats_set;
     long status;
     long local_changes;
@@ -45,6 +46,7 @@ typedef struct source_config {
 
 typedef struct server_config {
     char *name;
+    char *pretty_name;
     char *password;
     /* any field in config has changed */
     gboolean changed;
@@ -59,6 +61,7 @@ typedef struct server_config {
     SyncevoConfig *config;
 } server_config;
 
+gboolean source_config_is_usable (source_config *source);
 gboolean source_config_is_enabled (source_config *source);
 void source_config_free (source_config *source);
 
index ddb3556..23bff2a 100644 (file)
@@ -26,6 +26,7 @@
 #include <glib/gi18n.h>
 #include <gio/gio.h>
 #include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
 
 #include "syncevo-server.h"
 #include "syncevo-session.h"
 #endif
 
 static gboolean support_canceling = FALSE;
+#define REPORTS_PER_CALL 10
 
 #define SYNC_UI_ICON_SIZE 48
 
 #define STRING_VARIANT_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
 
+typedef enum bluetooth_type {
+    SYNC_BLUETOOTH_NONE,
+    SYNC_BLUETOOTH_GNOME,
+    SYNC_BLUETOOTH_MOBLIN
+} bluetooth_type;
+
 typedef enum app_state {
     SYNC_UI_STATE_CURRENT_STATE,
     SYNC_UI_STATE_GETTING_SERVER,
@@ -64,7 +72,23 @@ typedef enum app_state {
     SYNC_UI_STATE_SYNCING,
 } app_state;
 
-typedef struct app_data {
+typedef enum ui_operation {
+    OP_SYNC, /* use sync mode from config */
+    OP_SYNC_SLOW,
+    OP_SYNC_REFRESH_FROM_CLIENT,
+    OP_SYNC_REFRESH_FROM_SERVER,
+    OP_SAVE,
+    OP_RESTORE,
+} ui_operation;
+
+typedef struct operation_data {
+    app_data *data;
+    ui_operation operation;
+    gboolean started;
+    const char *dir; /* for OP_RESTORE */
+} operation_data;
+
+struct _app_data {
     GtkWidget *sync_win;
 
     GtkWidget *services_win; /* will be NULL when USE_MOBLIN_UX is set*/
@@ -82,6 +106,7 @@ typedef struct app_data {
     GtkWidget *offline_label;
     GtkWidget *progress;
     GtkWidget *sync_status_label;
+    GtkWidget *spinner_image;
     GtkWidget *sync_btn;
     GtkWidget *change_service_btn;
     GtkWidget *emergency_btn;
@@ -91,7 +116,9 @@ typedef struct app_data {
     GtkWidget *sources_box;
 
     GtkWidget *new_service_btn;
+    GtkWidget *new_device_btn;
     GtkWidget *services_box;
+    GtkWidget *devices_box;
     GtkWidget *scrolled_window;
     GtkWidget *back_btn;
     GtkWidget *expanded_config;
@@ -99,11 +126,16 @@ typedef struct app_data {
     GtkWidget *emergency_label;
     GtkWidget *emergency_expander;
     GtkWidget *emergency_source_table;
-    GtkWidget *emergency_from_client_btn;
-    GtkWidget *emergency_from_server_btn;
+    GtkWidget *refresh_from_server_btn_label;
+    GtkWidget *refresh_from_client_btn_label;
+    GtkWidget *emergency_backup_table;
+
+    GtkWidget *password_dialog_entry;
+    char *password_dialog_id;
 
     gboolean forced_emergency;
     GHashTable *emergency_sources;
+    guint backup_count;
 
     gboolean online;
 
@@ -112,34 +144,27 @@ typedef struct app_data {
     int last_sync;
     guint last_sync_src_id;
 
+    ui_operation current_operation;
     server_config *current_service;
     app_state current_state;
     gboolean open_current; /* should the service list open the current 
                               service when it populates next time*/
+    char *config_id_to_open;
 
     SyncevoServer *server;
 
     SyncevoSession *running_session; /* session that is currently active */
-} app_data;
 
-typedef struct operation_data {
-    app_data *data;
-    enum op {
-        OP_SYNC, /* use sync mode from config */
-        OP_SYNC_SLOW,
-        OP_SYNC_REFRESH_FROM_CLIENT,
-        OP_SYNC_REFRESH_FROM_SERVER,
-        OP_SAVE,
-    } operation;
-    gboolean started;
-} operation_data;
+    bluetooth_type bluetooth_wizard;
+};
 
 static void set_sync_progress (app_data *data, float progress, char *status);
 static void set_app_state (app_data *data, app_state state);
 static void show_main_view (app_data *data);
 static void update_emergency_view (app_data *data);
+static void update_emergency_expander (app_data *data);
 static void show_emergency_view (app_data *data);
-static void show_services_list (app_data *data);
+static void show_services_list (app_data *data, const char *config_id_to_open);
 static void update_services_list (app_data *data);
 static void update_service_ui (app_data *data);
 static void setup_new_service_clicked (GtkButton *btn, app_data *data);
@@ -153,6 +178,31 @@ static void start_session_cb (SyncevoServer *server, char *path,
 static void get_config_for_main_win_cb (SyncevoServer *server, SyncevoConfig *config,
                                         GError *error, app_data *data);
 
+
+void
+toggle_set_active (GtkWidget *toggle, gboolean active)
+{
+#ifdef USE_MOBLIN_UX
+    /* MxGtkLightSwitch does not have "active" property yet */
+    mx_gtk_light_switch_set_active (MX_GTK_LIGHT_SWITCH (toggle), active);
+#else
+    g_object_set (toggle, "active", active, NULL);
+#endif
+}
+
+gboolean
+toggle_get_active (GtkWidget *toggle)
+{
+#ifdef USE_MOBLIN_UX
+    /* MxGtkLightSwitch does not have "active" property yet */
+    return mx_gtk_light_switch_get_active (MX_GTK_LIGHT_SWITCH (toggle));
+#else
+    gboolean active;
+    g_object_get (toggle, "active", &active, NULL);
+    return active;
+#endif
+}
+
 void
 show_error_dialog (GtkWidget *widget, const char* message)
 {
@@ -180,7 +230,7 @@ static void
 change_service_clicked_cb (GtkButton *btn, app_data *data)
 {
     /* data->open_current = TRUE; */
-    show_services_list (data);
+    show_services_list (data, NULL);
 }
 
 static void 
@@ -203,7 +253,7 @@ get_pretty_source_name (const char *source_name)
     } else if (strcmp (source_name, "todo") == 0) {
         return g_strdup (_("Tasks"));
     } else if (strcmp (source_name, "memo") == 0) {
-        return g_strdup (_("Memo"));
+        return g_strdup (_("Notes"));
     } else if (strcmp (source_name, "calendar+todo") == 0) {
         /* TRANSLATORS: This is a "combination source" for syncing with devices
          * that combine appointments and tasks. the name should match the ones
@@ -217,41 +267,16 @@ get_pretty_source_name (const char *source_name)
     }
 }
 
-/*
-static void
-add_error_info (app_data *data, const char *message, const char *external_reason)
+char*
+get_pretty_source_name_markup (const char *source_name)
 {
-    GtkWidget *lbl;
-    GList *l, *children;
-
-    children = gtk_container_get_children (GTK_CONTAINER (data->service_error_box));
-    for (l = children; l; l = l->next) {
-        GtkLabel *old_lbl = GTK_LABEL (l->data);
-
-        if (strcmp (message, gtk_label_get_text (old_lbl)) == 0) {
-            g_list_free (children);
-            return;
-        }
-    }
-    g_list_free (children);
+    char *plain, *markup;
 
-    gtk_widget_show (data->errors_box);
-
-    lbl = gtk_label_new (message);
-    gtk_label_set_line_wrap (GTK_LABEL (lbl), TRUE);
-
-    gtk_widget_set_size_request (lbl, 160, -1);
-    gtk_widget_show (lbl);
-    gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5);
-    gtk_box_pack_start (GTK_BOX (data->service_error_box), lbl, FALSE, FALSE, 0);
-
-    if (external_reason) {
-        g_warning ("%s: %s", message, external_reason);
-    } else {
-        g_warning ("%s", message);
-    }
+    plain = get_pretty_source_name (source_name);
+    markup = g_markup_escape_text (plain, -1);
+    g_free (plain);
+    return markup;
 }
-*/
 
 static void
 reload_config (app_data *data, const char *server)
@@ -312,38 +337,42 @@ sync_cb (SyncevoSession *session,
     set_app_state (data, SYNC_UI_STATE_SYNCING);
 }
 
-static gboolean
-confirm (app_data *data, const char *message, const char *yes)
+gboolean
+show_confirmation (GtkWidget *widget, const char *message,
+                   const char *yes, const char *no)
 {
     GtkWidget *w;
     int ret;
 
-    w = gtk_message_dialog_new (GTK_WINDOW (data->sync_win),
+    w = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (widget)),
                                 GTK_DIALOG_MODAL,
                                 GTK_MESSAGE_QUESTION,
                                 GTK_BUTTONS_NONE,
                                 "%s",
                                 message);
     gtk_dialog_add_buttons (GTK_DIALOG (w),
-                            _("No, cancel sync"),
-                            GTK_RESPONSE_NO,
-                            yes,
-                            GTK_RESPONSE_YES,
+                            no, GTK_RESPONSE_NO,
+                            yes, GTK_RESPONSE_YES,
                             NULL);
     ret = gtk_dialog_run (GTK_DIALOG (w));
     gtk_widget_destroy (w);
 
     return (ret == GTK_RESPONSE_YES);
 }
+
 static void
-slow_sync_clicked_cb (GtkButton *btn, app_data *data)
+slow_sync (app_data *data)
 {
     operation_data *op_data;
     char *message;
 
+    /* TRANSLATORS: slow sync confirmation dialog message. Placeholder
+     * is service/device name */
     message = g_strdup_printf (_("Do you want to slow sync with %s?"),
-                               data->current_service->name);
-    if (!confirm (data, message, _("Yes, do slow sync"))) {
+                               data->current_service->pretty_name);
+    /* TRANSLATORS: slow sync confirmation dialog buttons */
+    if (!show_confirmation (data->sync_win, message,
+                            _("Yes, do slow sync"), _("No, cancel sync"))) {
         g_free (message);
         return;
     }
@@ -361,6 +390,12 @@ slow_sync_clicked_cb (GtkButton *btn, app_data *data)
     show_main_view (data);
 }
 
+static void
+slow_sync_clicked_cb (GtkButton *btn, app_data *data)
+{
+    slow_sync (data);
+}
+
 
 static void
 refresh_from_server_clicked_cb (GtkButton *btn, app_data *data)
@@ -368,10 +403,14 @@ refresh_from_server_clicked_cb (GtkButton *btn, app_data *data)
     operation_data *op_data;
     char *message;
 
+    /* TRANSLATORS: confirmation dialog for refresh-from-server. Placeholder
+     * is service/device name */
     message = g_strdup_printf (_("Do you want to delete all local data and replace it with "
                                  "data from %s? This is not usually advised."),
-                               data->current_service->name);
-    if (!confirm (data, message, _("Yes, delete and replace"))) {
+                               data->current_service->pretty_name);
+    /* TRANSLATORS: refresh-from-server confirmation dialog buttons */
+    if (!show_confirmation (data->sync_win, message,
+                            _("Yes, delete and replace"), _("No"))) {
         g_free (message);
         return;
     }
@@ -395,10 +434,14 @@ refresh_from_client_clicked_cb (GtkButton *btn, app_data *data)
     operation_data *op_data;
     char *message;
 
+    /* TRANSLATORS: confirmation dialog for refresh-from-client. Placeholder
+     * is service/device name */
     message = g_strdup_printf (_("Do you want to delete all data in %s and replace it with "
                                  "your local data? This is not usually advised."),
-                               data->current_service->name);
-    if (!confirm (data, message, _("Yes, delete and replace"))) {
+                               data->current_service->pretty_name);
+    /* TRANSLATORS: refresh-from-client confirmation dialog buttons */
+    if (!show_confirmation (data->sync_win, message,
+                            _("Yes, delete and replace"), _("No"))) {
         g_free (message);
         return;
     }
@@ -417,7 +460,7 @@ refresh_from_client_clicked_cb (GtkButton *btn, app_data *data)
 }
 
 static void
-sync_clicked_cb (GtkButton *btn, app_data *data)
+start_sync (app_data *data)
 {
     operation_data *op_data;
 
@@ -439,6 +482,13 @@ sync_clicked_cb (GtkButton *btn, app_data *data)
     }
 }
 
+
+static void
+sync_clicked_cb (GtkButton *btn, app_data *data)
+{
+    start_sync (data);
+}
+
 static gboolean
 refresh_last_synced_label (app_data *data)
 {
@@ -454,39 +504,39 @@ refresh_last_synced_label (app_data *data)
         msg = g_strdup (_("No service selected"));
         delay = -1;
     } else if (data->last_sync <= 0) {
-        msg = g_strdup (data->current_service->name); /* we don't know */
+        msg = g_strdup (data->current_service->pretty_name); /* we don't know */
         delay = -1;
     } else if (diff < 30) {
         /* TRANSLATORS: This is the title on main view. Placeholder is 
          * the service name. Example: "Google - synced just now" */
         msg = g_strdup_printf (_("%s - synced just now"),
-                               data->current_service->name);
+                               data->current_service->pretty_name);
         delay = 30;
     } else if (diff < 90) {
         msg = g_strdup_printf (_("%s - synced a minute ago"),
-                               data->current_service->name);
+                               data->current_service->pretty_name);
         delay = 60;
     } else if (diff < 60 * 60) {
         msg = g_strdup_printf (_("%s - synced %ld minutes ago"),
-                               data->current_service->name,
+                               data->current_service->pretty_name,
                                (diff + 30) / 60);
         delay = 60;
     } else if (diff < 60 * 90) {
         msg = g_strdup_printf (_("%s - synced an hour ago"),
-                               data->current_service->name);
+                               data->current_service->pretty_name);
         delay = 60 * 60;
     } else if (diff < 60 * 60 * 24) {
         msg = g_strdup_printf (_("%s - synced %ld hours ago"),
-                               data->current_service->name,
+                               data->current_service->pretty_name,
                                (diff + 60 * 30) / (60 * 60));
         delay = 60 * 60;
     } else if (diff < 60 * 60 * 24 - (60 * 30)) {
         msg = g_strdup_printf (_("%s - synced a day ago"),
-                               data->current_service->name);
+                               data->current_service->pretty_name);
         delay = 60 * 60 * 24;
     } else {
         msg = g_strdup_printf (_("%s - synced %ld days ago"),
-                               data->current_service->name,
+                               data->current_service->pretty_name,
                                (diff + 24 * 60 * 30) / (60 * 60 * 24));
         delay = 60 * 60 * 24;
     }
@@ -531,14 +581,27 @@ set_info_bar (GtkWidget *widget,
                            (GtkCallback)remove_child,
                            container);
     switch (response_id) {
+    case SYNC_ERROR_RESPONSE_SYNC:
+        /* TRANSLATORS: Action button in info bar in main view. Shown with e.g.
+         * "You've just restored a backup. The changes have not been "
+         * "synced with %s yet" */
+        gtk_info_bar_add_button (bar, _("Sync now"), response_id);
+        break;
     case SYNC_ERROR_RESPONSE_EMERGENCY:
-        /* TRANSLATORS: Buttons in error/info bars. */
-        gtk_info_bar_add_button (bar, _("Fix it"), response_id);
+        /* TRANSLATORS: Action button in info bar in main view. Shown with e.g.
+         * "A normal sync is not possible at this time..." message.
+         * "Other options" will open Emergency view */
+        gtk_info_bar_add_button (bar, _("Slow sync"), SYNC_ERROR_RESPONSE_EMERGENCY_SLOW_SYNC);
+        gtk_info_bar_add_button (bar, _("Other options..."), response_id);
         break;
     case SYNC_ERROR_RESPONSE_SETTINGS_SELECT:
+        /* TRANSLATORS: Action button in info bar in main view. Shown e.g.
+         * when no service is selected. Will open configuration view */
         gtk_info_bar_add_button (bar, _("Select sync service"), response_id);
         break;
     case SYNC_ERROR_RESPONSE_SETTINGS_OPEN:
+        /* TRANSLATORS: Action button in info bar in main view. Shown e.g. 
+         * login to service fails. Will open configuration view for this service */
         gtk_info_bar_add_button (bar, _("Edit service settings"), response_id);
         break;
     case SYNC_ERROR_RESPONSE_NONE:
@@ -630,7 +693,7 @@ set_app_state (app_data *data, app_state state)
         gtk_widget_set_sensitive (data->emergency_btn, TRUE);
         /* TRANSLATORS: These are for the button in main view, right side.
            Keep line length below ~20 characters, use two lines if needed */
-        if (data->synced_this_session)
+        if (data->synced_this_session && data->current_operation != OP_RESTORE)
             gtk_button_set_label (GTK_BUTTON (data->sync_btn), _("Sync again"));
         else
             gtk_button_set_label (GTK_BUTTON (data->sync_btn), _("Sync now"));
@@ -643,13 +706,18 @@ set_app_state (app_data *data, app_state state)
         /* we have a active session, and a session is running
            (the running session may or may not be ours) */
         gtk_widget_show (data->progress);
-        gtk_label_set_text (GTK_LABEL (data->sync_status_label), _("Syncing"));
+        if (data->current_operation == OP_RESTORE) {
+            gtk_label_set_text (GTK_LABEL (data->sync_status_label), _("Restoring"));
+        } else {
+            gtk_label_set_text (GTK_LABEL (data->sync_status_label), _("Syncing"));
+        }
         gtk_widget_set_sensitive (data->main_frame, FALSE);
         gtk_widget_set_sensitive (data->change_service_btn, FALSE);
         gtk_widget_set_sensitive (data->emergency_btn, FALSE);
 
-        gtk_widget_set_sensitive (data->sync_btn, support_canceling);
-        if (support_canceling) {
+        gtk_widget_set_sensitive (data->sync_btn, 
+                                  support_canceling && data->current_operation != OP_RESTORE);
+        if (support_canceling && support_canceling && data->current_operation != OP_RESTORE) {
             /* TRANSLATORS: This is for the button in main view, right side.
                Keep line length below ~20 characters, use two lines if needed */
             gtk_button_set_label (GTK_BUTTON (data->sync_btn), _("Cancel sync"));
@@ -663,13 +731,6 @@ set_app_state (app_data *data, app_state state)
 }
 
 #ifdef USE_MOBLIN_UX
-static void
-settings_visibility_changed_cb (GtkWidget *window, app_data *data)
-{
-    if (mux_window_get_settings_visible (MUX_WINDOW (window))) {
-        update_services_list (data);
-    }
-}
 
 /* truly stupid, but glade doesn't allow custom containers.
    Now glade file has dummy containers that will be replaced here.
@@ -719,6 +780,12 @@ key_press_cb (GtkWidget *widget,
 }
 
 static void
+settings_visibility_changed_cb (MuxWindow *win, app_data *data)
+{
+    update_services_list (data);
+}
+
+static void
 setup_windows (app_data *data,
                GtkWidget *main,
                GtkWidget *settings,
@@ -731,34 +798,30 @@ setup_windows (app_data *data,
     g_assert (GTK_IS_WINDOW (settings));
     g_assert (GTK_IS_WINDOW (emergency));
 
-    mux_main = mux_window_new ();
+    /* TRANSLATORS: button in the Moblin window title bar when main view is 
+     * not visible */
+    mux_main = mux_window_new (_("Back to sync"));
     gtk_window_set_title (GTK_WINDOW (mux_main),
                           gtk_window_get_title (GTK_WINDOW (main)));
     gtk_window_set_default_size (GTK_WINDOW (mux_main), 1024, 600);
     gtk_widget_set_name (mux_main, gtk_widget_get_name (main));
     gtk_widget_reparent (gtk_bin_get_child (GTK_BIN (main)), mux_main);
     mux_window_set_decorations (MUX_WINDOW (mux_main), MUX_DECOR_SETTINGS|MUX_DECOR_CLOSE);
-    g_signal_connect (mux_main, "settings-visibility-changed",
-                      G_CALLBACK (settings_visibility_changed_cb), data);
     g_signal_connect (mux_main, "key-press-event",
                       G_CALLBACK (key_press_cb), data);
+    g_signal_connect (mux_main, "settings-visibility-changed",
+                      G_CALLBACK (settings_visibility_changed_cb), data);
 
 
     tmp = g_object_ref (gtk_bin_get_child (GTK_BIN (settings)));
     gtk_container_remove (GTK_CONTAINER (settings), tmp);
-    mux_window_append_page (MUX_WINDOW (mux_main),
-                                        gtk_window_get_title (GTK_WINDOW (settings)),
-                                        tmp,
-                                        TRUE);
+    mux_window_append_page (MUX_WINDOW (mux_main), tmp, TRUE);
     g_object_unref (tmp);
 
     tmp = g_object_ref (gtk_bin_get_child (GTK_BIN (emergency)));
     gtk_container_remove (GTK_CONTAINER (emergency), tmp);
     data->emergency_index =
-        mux_window_append_page (MUX_WINDOW (mux_main),
-                                gtk_window_get_title (GTK_WINDOW (emergency)),
-                                tmp,
-                                FALSE);
+        mux_window_append_page (MUX_WINDOW (mux_main), tmp, FALSE);
     g_object_unref (tmp);
 
     data->sync_win = mux_main;
@@ -823,21 +886,115 @@ info_bar_response_cb (GtkInfoBar          *info_bar,
                       app_data            *data)
 {
     switch (response_id) {
+    case SYNC_ERROR_RESPONSE_SYNC:
+        start_sync (data);
+        break;
+    case SYNC_ERROR_RESPONSE_EMERGENCY_SLOW_SYNC:
+        slow_sync (data);
+        break;
     case SYNC_ERROR_RESPONSE_EMERGENCY:
         show_emergency_view (data);
         break;
     case SYNC_ERROR_RESPONSE_SETTINGS_OPEN:
         data->open_current = TRUE;
-        show_services_list (data);
+        show_services_list (data, NULL);
         break;
     case SYNC_ERROR_RESPONSE_SETTINGS_SELECT:
-        show_services_list (data);
+        show_services_list (data, NULL);
         break;
     default:
         g_warn_if_reached ();
     }
 }
 
+
+static void
+new_device_clicked_cb (GtkButton *btn, app_data *data)
+{
+    DBusGProxy *proxy;
+    DBusGConnection *bus;
+    char *argv[2] = {"bluetooth-wizard", NULL};
+    GError *error = NULL;
+
+    switch (data->bluetooth_wizard) {
+    case SYNC_BLUETOOTH_MOBLIN:
+
+        bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+        if (bus) {
+            proxy = dbus_g_proxy_new_for_name (bus,
+                                               "org.moblin.UX.Shell.Toolbar",
+                                               "/org/moblin/UX/Shell/Toolbar",
+                                               "org.moblin.UX.Shell.Toolbar");
+            dbus_g_proxy_call_no_reply (proxy, "ShowPanel",
+                                        G_TYPE_STRING, "bluetooth-panel",
+                                        G_TYPE_INVALID,
+                                        G_TYPE_INVALID);
+            g_object_unref (proxy);
+        }
+        break;
+
+    case SYNC_BLUETOOTH_GNOME:
+        if (!gdk_spawn_on_screen (gtk_window_get_screen (GTK_WINDOW (data->sync_win)),
+                                  NULL,
+                                  argv,
+                                  NULL,
+                                  G_SPAWN_SEARCH_PATH,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  &error)) {
+            g_warning ("Failed to spawn bluetooth-wizard: %s", error->message);
+            g_error_free (error);
+            return;
+        }
+        break;
+    default:
+        ;
+    }
+}
+
+static void
+name_has_owner_cb (DBusGProxy *proxy, gboolean has_owner,
+                   GError *error, app_data *data)
+{
+    if (has_owner) {
+        gtk_widget_show (data->new_device_btn);
+        data->bluetooth_wizard = SYNC_BLUETOOTH_MOBLIN;
+    }
+    g_object_unref (proxy);
+}
+
+static void
+init_bluetooth_ui (app_data *data)
+{
+    char *bt_wizard;
+    DBusGConnection *bus;
+    DBusGProxy *proxy;
+
+    data->bluetooth_wizard = SYNC_BLUETOOTH_NONE;
+
+    /* look for gnome bluetooth wizard first */
+    bt_wizard = g_find_program_in_path ("bluetooth-wizard");
+    if (bt_wizard) {
+        gtk_widget_show (data->new_device_btn);
+        data->bluetooth_wizard = SYNC_BLUETOOTH_GNOME;
+        g_free (bt_wizard);
+    } else {
+        /* try Moblin shell next (bluetooth panel integrates bt wizard) */
+        bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+        proxy = dbus_g_proxy_new_for_name (bus,
+                                           DBUS_SERVICE_DBUS,
+                                           DBUS_PATH_DBUS,
+                                           DBUS_INTERFACE_DBUS);
+        if (proxy) {
+            org_freedesktop_DBus_name_has_owner_async (proxy,
+                                                       "org.moblin.UX.Shell.Toolbar",
+                                                       (org_freedesktop_DBus_name_has_owner_reply)name_has_owner_cb,
+                                                       data);
+        }
+    }
+}
+
 static gboolean
 init_ui (app_data *data)
 {
@@ -877,6 +1034,10 @@ init_ui (app_data *data)
     data->emergency_btn = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_btn"));
     data->sync_btn = GTK_WIDGET (gtk_builder_get_object (builder, "sync_btn"));
     data->sync_status_label = GTK_WIDGET (gtk_builder_get_object (builder, "sync_status_label"));
+    data->spinner_image = GTK_WIDGET (gtk_builder_get_object (builder, "spinner_image"));
+    gtk_image_set_from_file (GTK_IMAGE (data->spinner_image), THEMEDIR "sync-spinner.gif");
+    gtk_widget_set_no_show_all (data->spinner_image, TRUE);
+    gtk_widget_hide (data->spinner_image);
 
     data->server_label = GTK_WIDGET (gtk_builder_get_object (builder, "sync_service_label"));
     data->last_synced_label = GTK_WIDGET (gtk_builder_get_object (builder, "last_synced_label"));
@@ -895,22 +1056,26 @@ init_ui (app_data *data)
     gtk_container_set_focus_vadjustment (GTK_CONTAINER (data->services_box), adj);
     g_signal_connect(data->services_box, "size-allocate",
                      G_CALLBACK (services_box_allocate_cb), data);
+
+    data->devices_box = GTK_WIDGET (gtk_builder_get_object (builder, "devices_box"));
+
     data->back_btn = GTK_WIDGET (gtk_builder_get_object (builder, "back_btn"));
 
     /* emergency view */
     btn = GTK_WIDGET (gtk_builder_get_object (builder, "slow_sync_btn"));
     g_signal_connect (btn, "clicked",
                       G_CALLBACK (slow_sync_clicked_cb), data);
-    data->emergency_from_server_btn = GTK_WIDGET (gtk_builder_get_object (builder, "refresh_from_server_btn"));
-    g_signal_connect (data->emergency_from_server_btn, "clicked",
+    data->refresh_from_server_btn_label = GTK_WIDGET (gtk_builder_get_object (builder, "refresh_from_server_btn_label"));
+    g_signal_connect (GTK_WIDGET (gtk_builder_get_object (builder, "refresh_from_server_btn")), "clicked",
                       G_CALLBACK (refresh_from_server_clicked_cb), data);
-    data->emergency_from_client_btn = GTK_WIDGET (gtk_builder_get_object (builder, "refresh_from_client_btn"));
-    g_signal_connect (data->emergency_from_client_btn, "clicked",
+    data->refresh_from_client_btn_label = GTK_WIDGET (gtk_builder_get_object (builder, "refresh_from_client_btn_label"));
+    g_signal_connect (GTK_WIDGET (gtk_builder_get_object (builder, "refresh_from_client_btn")), "clicked",
                       G_CALLBACK (refresh_from_client_clicked_cb), data);
 
     data->emergency_label = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_label"));
     data->emergency_expander = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_expander"));
     data->emergency_source_table = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_source_table"));
+    data->emergency_backup_table = GTK_WIDGET (gtk_builder_get_object (builder, "emergency_backup_table"));
 
     /* No (documented) way to add own widgets to gtkbuilder it seems...
        swap the all dummy widgets with Muxwidgets */
@@ -926,8 +1091,6 @@ init_ui (app_data *data)
 
     g_signal_connect (data->sync_win, "destroy",
                       G_CALLBACK (gtk_main_quit), NULL);
-    g_signal_connect_swapped (data->back_btn, "clicked",
-                      G_CALLBACK (show_main_view), data);
     g_signal_connect (data->change_service_btn, "clicked",
                       G_CALLBACK (change_service_clicked_cb), data);
     g_signal_connect (data->emergency_btn, "clicked",
@@ -935,7 +1098,14 @@ init_ui (app_data *data)
     g_signal_connect (data->sync_btn, "clicked", 
                       G_CALLBACK (sync_clicked_cb), data);
 
+    data->new_device_btn = GTK_WIDGET (gtk_builder_get_object (builder, "new_device_btn"));
+    g_signal_connect (data->new_device_btn, "clicked", 
+                      G_CALLBACK (new_device_clicked_cb), data);    
+
+    init_bluetooth_ui (data);
+
     g_object_unref (builder);
+
     return TRUE;
 }
 
@@ -977,6 +1147,27 @@ load_icon (const char *uri, GtkBox *icon_box, guint icon_size)
     gtk_widget_show (image);
 }
 
+static void
+emergency_toggle_notify_active_cb (GtkWidget *widget,
+                                   gpointer p,
+                                   app_data *data)
+{
+    gboolean active;
+    char *source;
+
+    active = toggle_get_active (widget);
+    source = g_object_get_data (G_OBJECT (widget), "source");
+
+    g_return_if_fail (source);
+
+    if (active) {
+        g_hash_table_insert (data->emergency_sources, g_strdup (source), "");
+    } else {
+        g_hash_table_remove (data->emergency_sources, source);
+    }
+    update_emergency_expander (data);
+}
+
 static GtkWidget*
 add_emergency_toggle_widget (app_data *data,
                              const char *title,
@@ -995,10 +1186,15 @@ add_emergency_toggle_widget (app_data *data,
                       col, col + 1, row, row + 1,
                       GTK_FILL, GTK_FILL, 16, 0);
     toggle = mx_gtk_light_switch_new ();
+    toggle_set_active (toggle, active);
+    g_signal_connect (toggle, "switch-flipped",
+                      G_CALLBACK (emergency_toggle_notify_active_cb), data);
 #else
     toggle = gtk_check_button_new_with_label (title);
+    toggle_set_active (toggle, active);
+    g_signal_connect (toggle, "notify::active",
+                      G_CALLBACK (emergency_toggle_notify_active_cb), data);
 #endif
-    g_object_set (toggle, "active", active, NULL);
     gtk_widget_show (toggle);
     gtk_table_attach (GTK_TABLE (data->emergency_source_table), toggle,
                       col + 1, col + 2, row, row + 1,
@@ -1032,7 +1228,7 @@ update_emergency_expander (app_data *data)
          * name, second a comma separeted list of sources.
          * E.g. "Affected data: Google Contacts, Appointments" */
         text = g_strdup_printf (_("Affected data: %s %s"),
-                                data->current_service->name,
+                                data->current_service->pretty_name,
                                 sources);
         g_free (sources);
     } else {
@@ -1044,28 +1240,6 @@ update_emergency_expander (app_data *data)
 }
 
 static void
-emergency_toggle_notify_active_cb (GtkWidget *widget,
-                                   GParamSpec *pspec,
-                                   app_data *data)
-{
-    gboolean active;
-    char *source;
-
-    g_object_get (G_OBJECT (widget), "active", &active, NULL);
-    source = g_object_get_data (G_OBJECT (widget), "source");
-
-    g_return_if_fail (source);
-
-    if (active) {
-        g_hash_table_insert (data->emergency_sources, g_strdup (source), "");
-    } else {
-        g_hash_table_remove (data->emergency_sources, source);
-    }
-    update_emergency_expander (data);
-}
-
-
-static void
 add_emergency_source (const char *name, source_config *conf, app_data *data)
 {
     GtkWidget *toggle;
@@ -1091,11 +1265,210 @@ add_emergency_source (const char *name, source_config *conf, app_data *data)
 
     pretty_name = get_pretty_source_name (name);
     toggle = add_emergency_toggle_widget (data, pretty_name, active, row, col);
+    gtk_widget_set_sensitive (toggle, source_config_is_usable (conf));
     g_object_set_data_full (G_OBJECT (toggle), "source", g_strdup (name), g_free);
     g_free (pretty_name);
+}
 
-    g_signal_connect (toggle, "notify::active",
-                      G_CALLBACK (emergency_toggle_notify_active_cb), data);
+static void
+update_backup_visibilities (app_data *data)
+{
+    char *key;
+    GHashTableIter iter;
+    GList *l, *widgets;
+
+    widgets = gtk_container_get_children (
+        GTK_CONTAINER (data->emergency_backup_table));
+    gtk_widget_show_all (data->emergency_backup_table);
+
+    /* hide backup widgets that do not contain selected sources */
+    g_hash_table_iter_init (&iter, data->emergency_sources);
+    while (g_hash_table_iter_next (&iter, (gpointer)&key, NULL)) {
+        for (l = widgets; l; l = l->next) {
+            if (!g_object_get_data (G_OBJECT (l->data), key)) {
+                gtk_widget_hide (GTK_WIDGET (l->data));
+            }
+        }
+    }
+
+    g_list_free (widgets);
+}
+
+static void 
+restore_clicked_cb (GtkButton *btn, app_data *data)
+{
+    const char *dir, *time_str;
+    operation_data *op_data;
+    char *message;
+
+    dir = g_object_get_data (G_OBJECT (btn), "dir");
+    time_str = g_object_get_data (G_OBJECT (btn), "time");
+    g_return_if_fail (dir && time_str);
+
+    /* TRANSLATORS: confirmation for restoring a backup. placeholder is the
+     * backup time string defined below */
+    message = g_strdup_printf (_("Do you want to restore the backup from %s? "
+                                 "All changes you have made since then will be lost."),
+                               time_str);
+    if (!show_confirmation (data->sync_win, message, _("Yes, restore"), _("No"))) {
+        g_free (message);
+        return;
+    }
+    g_free (message);
+
+    op_data = g_slice_new (operation_data);
+    op_data->data = data;
+    op_data->operation = OP_RESTORE;
+    op_data->dir = dir;
+    op_data->started = FALSE;
+    syncevo_server_start_session (data->server,
+                                  data->current_service->name,
+                                  (SyncevoServerStartSessionCb)start_session_cb,
+                                  op_data);
+
+    show_main_view (data);
+}
+
+static void
+add_backup (app_data *data, const char *peername, const char *dir,
+            long endtime, GList *sources)
+{
+    GtkWidget *timelabel, *label, *blabel, *button, *box;;
+    guint rows;
+    char *text;
+    char time_str[60];
+    struct tm *tim;
+
+    /* TRANSLATORS: date/time for strftime(), used in emergency view backup
+     * label. Any time format that shows date and time is good. */
+    tim = localtime (&endtime);
+    strftime (time_str, sizeof (time_str), _("%x %X"), tim);
+
+    g_object_get (data->emergency_backup_table,
+                  "n-rows", &rows,
+                  NULL);
+
+    box = gtk_vbox_new (TRUE, 6);
+    gtk_table_attach (GTK_TABLE (data->emergency_backup_table), box,
+                      0, 1, rows, rows + 1,
+                      GTK_EXPAND|GTK_FILL, GTK_FILL, 16, 0);
+
+    timelabel = gtk_label_new (time_str);
+    gtk_misc_set_alignment (GTK_MISC (timelabel), 0.0, 0.5);
+    gtk_label_set_line_wrap (GTK_LABEL (timelabel), TRUE);
+    gtk_widget_set_size_request (timelabel, 600, -1);
+    gtk_box_pack_start_defaults (GTK_BOX (box), timelabel);
+
+    /* TRANSLATORS: label for a backup in emergency view. Placeholder is 
+     * service or device name */
+    text = g_strdup_printf (_("Backed up before syncing with %s"), peername);
+    label = gtk_label_new (text);
+    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+    gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+    gtk_widget_set_size_request (label, 600, -1);
+    gtk_box_pack_start_defaults (GTK_BOX (box), label);
+    g_free (text);
+
+    button = gtk_button_new ();
+    gtk_table_attach (GTK_TABLE (data->emergency_backup_table), button,
+                      1, 2, rows, rows + 1,
+                      GTK_FILL, GTK_SHRINK, 32, 0);
+    g_object_set_data_full (G_OBJECT (button), "dir", g_strdup(dir), g_free);
+    g_object_set_data_full (G_OBJECT (button), "time", g_strdup(time_str), g_free);
+    g_signal_connect (button, "clicked",
+                      G_CALLBACK (restore_clicked_cb), data);
+
+    blabel = gtk_label_new (_("Restore"));
+    gtk_misc_set_padding (GTK_MISC (blabel), 32, 0);
+    gtk_container_add (GTK_CONTAINER (button), blabel);
+
+    for (; sources; sources = sources->next) {
+        g_object_set_data (G_OBJECT (box), (char *)sources->data, "");
+        g_object_set_data (G_OBJECT (button), (char *)sources->data, "");
+    }
+}
+
+static void
+get_reports_for_backups_cb (SyncevoServer *server,
+                            SyncevoReports *reports,
+                            GError *error,
+                            app_data *data)
+{
+    guint len, i;
+
+    if (error) {
+        g_warning ("Error in Session.GetReports: %s", error->message);
+        g_error_free (error);
+        /* non-fatal, unknown error */
+        return;
+    }
+
+    len = syncevo_reports_get_length (reports);
+    for (i = 0; i < len; i++) {
+        GHashTable *report = syncevo_reports_index (reports, i);
+        GHashTableIter iter;
+        char *key, *val;
+        long status = -1;
+        long endtime = -1;
+        char *peername = NULL;
+        char *dir = NULL;
+        GList *backup_sources = NULL;
+
+        g_hash_table_iter_init (&iter, report);
+        while (g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&val)) {
+            char **strs;
+
+            strs = g_strsplit (key, "-", 6);
+            if (!strs) {
+                continue;
+            }
+
+            if (g_strcmp0 (strs[0], "source") == 0 &&
+                g_strcmp0 (strs[2], "backup") == 0 &&
+                g_strcmp0 (strs[3], "before") == 0) {
+                backup_sources = g_list_prepend (backup_sources,
+                                                 g_strdup (strs[1]));
+            } else if (g_strcmp0 (strs[0], "end") == 0) {
+                endtime = strtol (val, NULL, 10);
+            } else if (g_strcmp0 (strs[0], "status") == 0) {
+                status = strtol (val, NULL, 10);
+            } else if (g_strcmp0 (strs[0], "peer") == 0) {
+                peername = val;
+            } else if (g_strcmp0 (strs[0], "dir") == 0) {
+                dir = val;
+            }
+            g_strfreev (strs);
+        }
+
+        if (peername && dir && endtime > 0) {
+            add_backup (data, peername, dir, endtime, backup_sources);
+        }
+        g_list_foreach (backup_sources, (GFunc)g_free, NULL);
+        g_list_free (backup_sources);
+    }
+
+    data->backup_count += len;
+    if (len == REPORTS_PER_CALL) {
+        syncevo_server_get_reports (data->server,
+                                    "",
+                                    data->backup_count, REPORTS_PER_CALL,
+                                    (SyncevoServerGetReportsCb)get_reports_for_backups_cb,
+                                    data);
+    }
+
+    update_backup_visibilities (data);
+}
+
+static const char*
+get_syncevo_context (const char *config_name)
+{
+    char *context;
+
+    context = g_strrstr (config_name, "@");
+    if (!context) {
+        context = "";
+    }
+    return context;
 }
 
 static void
@@ -1112,17 +1485,18 @@ update_emergency_view (app_data *data)
         text = g_strdup_printf (
                 /* TRANSLATORS: this is an explanation in Emergency view.
                  * Placeholder is a service/device name */
-                _("A normal sync with %s is not possible at this time."
-                  "We can do a slow two-way sync, start from scratch or "
-                  "restore from backup."),
-                data->current_service->name);
+                _("A normal sync with %s is not possible at this time. "
+                  "You can do a slow two-way sync or start from scratch. You "
+                  "can also restore a backup, but a slow sync or starting from "
+                  "scratch will still be required before normal sync is "
+                  "possible."),
+                data->current_service->pretty_name);
     } else {
         /* TRANSLATORS: this is an explanation in Emergency view.
          * Placeholder is a service/device name */
         text = g_strdup_printf (
-                _("If something has gone horribly wrong with %s, we can try a "
-                  "slow sync, start from scratch or restore from backup."),
-                data->current_service->name);
+                _("If something has gone horribly wrong, you can try a "
+                  "slow sync, start from scratch or restore from backup."));
     }
     gtk_label_set_text (GTK_LABEL (data->emergency_label), text);
     g_free (text);
@@ -1133,14 +1507,14 @@ update_emergency_view (app_data *data)
     text = g_strdup_printf (_("Delete all your local\n"
                               "data and replace with\n"
                               "data from %s"),
-                            data->current_service->name);
-    gtk_button_set_label (GTK_BUTTON (data->emergency_from_server_btn), text);
+                            data->current_service->pretty_name);
+    gtk_label_set_text (GTK_LABEL (data->refresh_from_server_btn_label), text);
     g_free (text);
     text = g_strdup_printf (_("Delete all data on\n"
                               "%s and replace\n"
                               "with your local data"),
-                            data->current_service->name);
-    gtk_button_set_label (GTK_BUTTON (data->emergency_from_client_btn), text);
+                            data->current_service->pretty_name);
+    gtk_label_set_text (GTK_LABEL (data->refresh_from_client_btn_label), text);
     g_free (text);
 
     gtk_container_foreach (GTK_CONTAINER (data->emergency_source_table),
@@ -1151,24 +1525,27 @@ update_emergency_view (app_data *data)
                           (GHFunc)add_emergency_source,
                           data);
     update_emergency_expander (data);
+
+    data->backup_count = 0;
+    gtk_container_foreach (GTK_CONTAINER (data->emergency_backup_table),
+                           (GtkCallback)remove_child,
+                           data->emergency_backup_table);
+    gtk_table_resize (GTK_TABLE (data->emergency_backup_table), 1, 1);
+    syncevo_server_get_reports (data->server,
+                                get_syncevo_context (data->current_service->name),
+                                0, REPORTS_PER_CALL,
+                                (SyncevoServerGetReportsCb)get_reports_for_backups_cb,
+                                data);
+
 }
 
 static void
 update_service_source_ui (const char *name, source_config *conf, app_data *data)
 {
     GtkWidget *lbl, *box;
-    char *pretty_name;
-    const char *source_uri, *sync;
+    char *pretty_name, *title;
 
-    source_uri = g_hash_table_lookup (conf->config, "uri");
-    sync = g_hash_table_lookup (conf->config, "sync");
-
-    if (!sync || 
-        strcmp (sync, "disabled") == 0 ||
-        strcmp (sync, "none") == 0 ||
-        !source_uri ||
-        strlen (source_uri) == 0 ||
-        !conf->supported_locally) {
+    if (!source_config_is_usable (conf)) {
         return;
     }
 
@@ -1176,9 +1553,12 @@ update_service_source_ui (const char *name, source_config *conf, app_data *data)
     gtk_box_pack_start (GTK_BOX (data->sources_box), conf->box,
                         FALSE, FALSE, 8);
 
-    pretty_name = get_pretty_source_name (name);
-    lbl = gtk_label_new (pretty_name);
+    pretty_name = get_pretty_source_name_markup (name);
+    title = g_strdup_printf ("<b>%s</b>", pretty_name);
+    lbl = gtk_label_new (NULL);
+    gtk_label_set_markup (GTK_LABEL (lbl), title);
     g_free (pretty_name);
+    g_free (title);
     gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5);
     gtk_box_pack_start_defaults (GTK_BOX (conf->box), lbl);
 
@@ -1249,9 +1629,6 @@ update_service_ui (app_data *data)
 
     refresh_last_synced_label (data);
 
-/* TODO: make sure all default sources are visible
- * (iow add missing sources as insensitive) */
-
     gtk_widget_show_all (data->sources_box);
 }
 
@@ -1288,28 +1665,30 @@ config_widget_changed_cb (GtkWidget *widget, app_data *data)
     }
 }
 
-static GtkWidget*
-add_server_to_box (GtkBox *box,
-                   const char *name,
-                   gboolean configured,
-                   gboolean has_template,
-                   app_data *data)
+static SyncConfigWidget*
+add_configuration_to_box (GtkBox *box,
+                          SyncevoConfig *config,
+                          const char *name,
+                          gboolean has_template,
+                          gboolean has_configuration,
+                          app_data *data)
 {
     GtkWidget *item = NULL;
     gboolean current = FALSE;
     const char *current_name = NULL;
 
     if (data->current_service) {
-        current_name = data->current_service->name;
+        current_name = data->current_service->pretty_name;
         if (data->current_service->name && name && 
-            strcmp (name, data->current_service->name) == 0) {
+            g_strcasecmp (name, data->current_service->name) == 0) {
             current = TRUE;
         }
      }
 
     item = sync_config_widget_new (data->server, name,
+                                   config,
                                    current, current_name,
-                                   configured, has_template);
+                                   has_configuration, has_template);
     g_signal_connect (item, "changed",
                       G_CALLBACK (config_widget_changed_cb), data);
     g_signal_connect (item, "notify::expanded",
@@ -1320,10 +1699,19 @@ add_server_to_box (GtkBox *box,
     if (current) {
         sync_config_widget_set_expanded (SYNC_CONFIG_WIDGET (item),
                                         data->open_current);
-        data->open_current = FALSE;
     }
+    if (g_strcmp0 (name, "default") == 0) {
+        sync_config_widget_set_expanded (SYNC_CONFIG_WIDGET (item),
+                                         TRUE);
+    }
+
+    if (data->config_id_to_open) {
+        sync_config_widget_expand_id (SYNC_CONFIG_WIDGET (item),
+                                      data->config_id_to_open);
+    }
+
+    return SYNC_CONFIG_WIDGET (item);
 
-    return item;
 }
 
 static void
@@ -1337,6 +1725,121 @@ find_new_service_config (SyncConfigWidget *w, GtkWidget **found)
     }
 }
 
+typedef struct config_data {
+    app_data *data;
+    char *name;
+    gboolean has_configuration;
+    gboolean has_template;
+    GHashTable *device_templates;
+
+} config_data;
+
+static void
+get_config_for_config_widget_cb (SyncevoServer *server,
+                                 SyncevoConfig *config,
+                                 GError *error,
+                                 config_data *c_data)
+{
+    char *ready, *is_peer, *url;
+
+    if (error) {
+        /* show in UI? */
+        g_warning ("Server.GetConfig() failed: %s", error->message);
+        g_error_free (error);
+        return;
+    }
+
+    syncevo_config_get_value (config, NULL, "ConsumerReady", &ready);
+    syncevo_config_get_value (config, NULL, "PeerIsClient", &is_peer);
+    syncevo_config_get_value (config, NULL, "syncURL", &url);
+
+    if (is_peer && g_strcmp0 ("1", is_peer) == 0) {
+        if (url) {
+            SyncConfigWidget *w;
+            char *fp, *device_name = NULL;
+            char **fpv = NULL;
+
+            /* NOTE: using device_name here means a new config will be saved with
+             * device_name (and not the template name). Not sure if this is
+             * what we really want... */
+            syncevo_config_get_value (config, NULL, "fingerPrint", &fp);
+
+            if (fp) {
+                fpv = g_strsplit_set (fp, ",;", 2);
+                if (g_strv_length (fpv) > 0) {
+                    device_name = fpv[0];
+                }
+            }
+            if (!device_name) {
+                device_name = c_data->name;
+            }
+
+            /* keep a list of added devices */
+            w = g_hash_table_lookup (c_data->device_templates, url);
+            if (!w) {
+                if (c_data->has_configuration || g_strcmp0 ("1", ready) == 0) {
+                    w = add_configuration_to_box (GTK_BOX (c_data->data->devices_box),
+                                                           config,
+                                                           device_name,
+                                                           c_data->has_template,
+                                                           c_data->has_configuration,
+                                                           c_data->data);
+                    g_hash_table_insert (c_data->device_templates, url, w);
+                }
+            } else {
+                /* TODO: might want to add a new widget, if user has created more
+                 * configs for same device: this really requires us to look at 
+                 * all configs / templates, then decide what to sho w*/
+
+                /* there is a widget for this device already, add this info there*/
+                sync_config_widget_add_alternative_config (w, device_name, config, 
+                                                           c_data->has_configuration);
+            }
+
+            g_strfreev (fpv);
+        }
+    } else {
+        if (c_data->has_configuration || g_strcmp0 ("1", ready) == 0) {
+            add_configuration_to_box (GTK_BOX (c_data->data->services_box),
+                                      config,
+                                      c_data->name,
+                                      c_data->has_template,
+                                      c_data->has_configuration,
+                                      c_data->data);
+        }
+    }
+
+    g_free (c_data->name);
+    g_hash_table_unref (c_data->device_templates);
+    g_slice_free (config_data, c_data);
+}
+
+static void
+get_config_for_config_widget (app_data *data,
+                              const char *config,
+                              gboolean has_template,
+                              gboolean has_configuration,
+                              GHashTable *device_templates)
+
+{
+    config_data *c_data;
+
+    c_data = g_slice_new0 (config_data);
+    c_data->data = data;
+    c_data->name = g_strdup (config);
+    c_data->has_template = has_template;
+    c_data->has_configuration = has_configuration;
+    if (device_templates) {
+        c_data->device_templates = g_hash_table_ref (device_templates);
+    }
+
+    syncevo_server_get_config (data->server,
+                               config,
+                               !has_configuration,
+                               (SyncevoServerGetConfigCb)get_config_for_config_widget_cb,
+                               c_data);
+}
+
 static void
 setup_new_service_clicked (GtkButton *btn, app_data *data)
 {
@@ -1352,15 +1855,12 @@ setup_new_service_clicked (GtkButton *btn, app_data *data)
                            (GtkCallback)find_new_service_config,
                            &widget);
     if (!widget) {
-        widget = add_server_to_box (GTK_BOX (data->services_box),
-                                    "default",
-                                    FALSE, TRUE,
-                                    data);
+        get_config_for_config_widget (data, "default", TRUE, FALSE, NULL);
+    } else {
+        sync_config_widget_set_expanded (SYNC_CONFIG_WIDGET (widget), TRUE);
     }
-    sync_config_widget_set_expanded (SYNC_CONFIG_WIDGET (widget), TRUE);
 }
 
-
 typedef struct templates_data {
     app_data *data;
     char **templates;
@@ -1374,7 +1874,7 @@ get_configs_cb (SyncevoServer *server,
 {
     char **config_iter, **template_iter, **templates;
     app_data *data;
-    GtkWidget *widget;
+    GHashTable *device_templates;
 
     templates = templ_data->templates;
     data = templ_data->data;
@@ -1383,35 +1883,33 @@ get_configs_cb (SyncevoServer *server,
     if (error) {
         show_main_view (data);
 
-        /* TODO show in UI: failed to show service list */
         g_warning ("Server.GetConfigs() failed: %s", error->message);
         g_strfreev (templates);
         g_error_free (error);
         return;
     }
 
+    device_templates = g_hash_table_new (g_str_hash, g_str_equal);
+
     for (template_iter = templates; *template_iter; template_iter++){
         gboolean found_config = FALSE;
 
         for (config_iter = configs; *config_iter; config_iter++) {
-            if (*template_iter && 
-                *config_iter && 
+            if (*template_iter && *config_iter && 
                 g_ascii_strncasecmp (*template_iter,
                                      *config_iter,
                                      strlen (*config_iter)) == 0) {
-                widget = add_server_to_box (GTK_BOX (data->services_box),
-                                            *template_iter,
-                                            TRUE, TRUE,
-                                            data);
+                /* have template and config */
+                get_config_for_config_widget (data, *config_iter,
+                                              TRUE, TRUE, device_templates);
                 found_config = TRUE;
                 break;
             }
         }
         if (!found_config) {
-            widget = add_server_to_box (GTK_BOX (data->services_box),
-                                        *template_iter,
-                                        FALSE, TRUE,
-                                        data);
+            /* have template, no config */
+            get_config_for_config_widget (data, *template_iter,
+                                          TRUE, FALSE, device_templates);
         }
     }
 
@@ -1430,13 +1928,14 @@ get_configs_cb (SyncevoServer *server,
             }
         }
         if (!found_template) {
-            widget = add_server_to_box (GTK_BOX (data->services_box),
-                                        *config_iter,
-                                        TRUE, FALSE,
-                                        data);
+            /* have config, no template */
+            get_config_for_config_widget (data, *config_iter,
+                                          FALSE, TRUE, device_templates);
         }
     }
 
+    /* config initialization might ref/unref as well... */
+    g_hash_table_unref (device_templates);
     g_strfreev (configs);
     g_strfreev (templates);
 }
@@ -1451,7 +1950,7 @@ get_template_configs_cb (SyncevoServer *server,
 
     if (error) {
         show_main_view (data);
-        /* TODO show in UI: failed to show service list */
+
         show_error_dialog (data->sync_win, 
                            _("Failed to get list of supported services from SyncEvolution"));
         g_warning ("Server.GetConfigs() failed: %s", error->message);
@@ -1472,12 +1971,12 @@ get_template_configs_cb (SyncevoServer *server,
 static void
 update_services_list (app_data *data)
 {
-    /* NOTE: could get this on ui startup as well for instant action.
-       Downside is stale data.... */
-
     gtk_container_foreach (GTK_CONTAINER (data->services_box),
                            (GtkCallback)remove_child,
                            data->services_box);
+    gtk_container_foreach (GTK_CONTAINER (data->devices_box),
+                           (GtkCallback)remove_child,
+                           data->devices_box);
 
     syncevo_server_get_configs (data->server,
                                 TRUE,
@@ -1541,57 +2040,47 @@ get_config_for_main_win_cb (SyncevoServer *server,
 }
 
 static void
-set_running_session_status (app_data *data, SyncevoSessionStatus status)
+set_running_session_status (app_data *data,
+                            SyncevoSessionStatus status,
+                            int error_code)
 {
-    switch (status) {
-    case SYNCEVO_STATUS_QUEUEING:
+    if (status & SYNCEVO_STATUS_QUEUEING) {
         g_warning ("Running session is queued, this shouldn't happen...");
-        break;
-    case SYNCEVO_STATUS_IDLE:
+    } else if (status & SYNCEVO_STATUS_IDLE) {
         set_app_state (data, SYNC_UI_STATE_SERVER_OK);
-        break;
-    case SYNCEVO_STATUS_RUNNING:
-    case SYNCEVO_STATUS_SUSPENDING:
-    case SYNCEVO_STATUS_ABORTING:
-        set_app_state (data, SYNC_UI_STATE_SYNCING);
-        break;
-    case SYNCEVO_STATUS_DONE:
-        gtk_label_set_text (GTK_LABEL (data->sync_status_label), 
-                            _("Sync complete"));
+    } else if (status & SYNCEVO_STATUS_DONE) {
+        char *err;
+        err = get_error_string_for_code (error_code, NULL);
+        if (err) {
+            if (data->current_operation == OP_RESTORE) {
+                gtk_label_set_text (GTK_LABEL (data->sync_status_label),
+                                    _("Restore failed"));
+            } else {
+                gtk_label_set_text (GTK_LABEL (data->sync_status_label),
+                                    _("Sync failed"));
+            }
+            g_free (err);
+        } else {
+            if (data->current_operation == OP_RESTORE) {
+                gtk_label_set_text (GTK_LABEL (data->sync_status_label),
+                                    _("Restore complete"));
+            } else {
+                gtk_label_set_text (GTK_LABEL (data->sync_status_label),
+                                    _("Sync complete"));
+            }
+        }
         set_app_state (data, SYNC_UI_STATE_SERVER_OK);
         set_sync_progress (data, 1.0, "");
-        
-        break;
-    default:
-        g_warning ("unknown session status  %d used!", status);
-    }
-}
-
-static void
-update_source_status (char *name,
-                      SyncevoSyncMode mode,
-                      SyncevoSourceStatus status,
-                      guint error_code,
-                      app_data *data)
-{
-    char *error;
-    static char *waiting_source = NULL;
-
-    error = get_error_string_for_code (error_code, NULL);
-    if (error) {
-        /* TODO show sync error in UI -- but not duplicates */
-        g_warning ("Source '%s' error: %s", name, error);
-        g_free (error);
+    } else if (status & SYNCEVO_STATUS_RUNNING ||
+               status & SYNCEVO_STATUS_SUSPENDING ||
+               status & SYNCEVO_STATUS_ABORTING) {
+        set_app_state (data, SYNC_UI_STATE_SYNCING);
     }
 
-    if (status & SYNCEVO_SOURCE_WAITING) {
-        g_free (waiting_source);
-        waiting_source = g_strdup (name);
-        /* TODO: start spinner */
-    } else if (waiting_source && strcmp (waiting_source, name) == 0) {
-        g_free (waiting_source);
-        waiting_source = NULL;
-        /* TODO: stop spinner */
+    if (status & SYNCEVO_STATUS_WAITING) {
+        gtk_widget_show (data->spinner_image);
+    } else {
+        gtk_widget_hide (data->spinner_image);
     }
 }
 
@@ -1602,11 +2091,7 @@ running_session_status_changed_cb (SyncevoSession *session,
                                    SyncevoSourceStatuses *source_statuses,
                                    app_data *data)
 {
-    set_running_session_status (data, status);
-
-    syncevo_source_statuses_foreach (source_statuses,
-                                     (SourceStatusFunc)update_source_status,
-                                     data);
+    set_running_session_status (data, status, error_code);
 }
 
 static void
@@ -1624,7 +2109,30 @@ get_running_session_status_cb (SyncevoSession *session,
         return;
     }
 
-    set_running_session_status (data, status);
+    set_running_session_status (data, status, error_code);
+}
+
+typedef struct source_progress_data {
+    app_data *data;
+    SyncevoSourcePhase phase;
+    const char *source;
+} source_progress_data;
+
+static void
+find_updated_source_progress (const char *name,
+                              SyncevoSourcePhase phase,
+                              source_progress_data *prog_data)
+{
+    GHashTable *configs = prog_data->data->current_service->source_configs;
+    source_config *config;
+    config = g_hash_table_lookup (configs, name);
+    if (config) {
+        if (phase != config->phase) {
+            config->phase = phase;
+            prog_data->phase = config->phase;
+            prog_data->source = name;
+        }
+    }
 }
 
 static void
@@ -1633,38 +2141,44 @@ running_session_progress_changed_cb (SyncevoSession *session,
                                      SyncevoSourceProgresses *source_progresses,
                                      app_data *data)
 {
-    SyncevoSourceProgress *s_progress;
-    char *name;
-    char *msg = NULL;
-
-    s_progress = syncevo_source_progresses_get_current (source_progresses);
-    if (!s_progress) {
-        return;
-    }
+    source_progress_data *prog_data = g_slice_new0 (source_progress_data);
+    prog_data->data = data;
+    prog_data->phase = SYNCEVO_PHASE_NONE;
+    prog_data->source = NULL;
 
-    name = get_pretty_source_name (s_progress->name);
+    syncevo_source_progresses_foreach (source_progresses,
+                                       (SourceProgressFunc)find_updated_source_progress,
+                                       prog_data);
+    if (!prog_data->source) {
+        set_sync_progress (data, ((float)progress) / 100, NULL);
+    } else {
+        char *name;
+        char *msg = NULL;
 
-    switch (s_progress->phase) {
-    case SYNCEVO_PHASE_PREPARING:
-        msg = g_strdup_printf (_("Preparing '%s'"), name);
-        break;
-    case SYNCEVO_PHASE_RECEIVING:
-        msg = g_strdup_printf (_("Receiving '%s'"), name);
-        break;
-    case SYNCEVO_PHASE_SENDING:
-        msg = g_strdup_printf (_("Sending '%s'"), name);
-        break;
-    default:
-        ;
-    }
-    g_free (name);
+        name = get_pretty_source_name (prog_data->source);
+        switch (prog_data->phase) {
+        case SYNCEVO_PHASE_PREPARING:
+            msg = g_strdup_printf (_("Preparing '%s'"), name);
+            break;
+        case SYNCEVO_PHASE_RECEIVING:
+            msg = g_strdup_printf (_("Receiving '%s'"), name);
+            break;
+        case SYNCEVO_PHASE_SENDING:
+            msg = g_strdup_printf (_("Sending '%s'"), name);
+            break;
+        default:
+            ;
+        }
 
-    if (msg) {
-        set_sync_progress (data, ((float)progress) / 100, msg);
+        if (msg) {
+            set_sync_progress (data, ((float)progress) / 100, msg);
+        }
         g_free (msg);
+        g_free (name);
+        
     }
 
-    syncevo_source_progress_free (s_progress);
+    g_slice_free (source_progress_data, prog_data);
 }
 
 typedef struct source_stats {
@@ -1826,7 +2340,6 @@ source_config_update_widget (source_config *source)
         return TRUE;
     }
 
-    /* TODO improve error visibility */
     msg = get_error_string_for_code (source->status, &response);
     if (msg) {
         show_error = TRUE;
@@ -1859,6 +2372,8 @@ get_reports_cb (SyncevoServer *server,
     char *error_msg;
     SyncErrorResponse response;
     gboolean have_source_errors;
+    GHashTable *report = NULL;
+    guint len;
 
     if (error) {
         g_warning ("Error in Session.GetReports: %s", error->message);
@@ -1870,10 +2385,22 @@ get_reports_cb (SyncevoServer *server,
     sources = g_hash_table_new_full (g_str_hash, g_str_equal,
                                      g_free, (GDestroyNotify)free_source_stats);
 
+    len = syncevo_reports_get_length (reports);
 
-    if (syncevo_reports_get_length (reports) > 0) {
-        GHashTable *report = syncevo_reports_index (reports, 0);
+    if (len > 0) {
+        report = syncevo_reports_index (reports, 0);
+        val = g_hash_table_lookup (report, "dir");
+        if (!val || strlen (val) == 0) {
+            /* dummy report for first time sync info*/
+            if (len > 1) {
+                report = syncevo_reports_index (reports, 1);
+            } else {
+                report = NULL;
+            }
+        }
+    }
 
+    if (report) {
         g_hash_table_iter_init (&iter, report);
         while (g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&val)) {
             char **strs;
@@ -1933,13 +2460,19 @@ get_reports_cb (SyncevoServer *server,
         }
     }
 
+    if (status != 200) {
+        /* don't want to show a sync time for failed syncs */
+        data->last_sync = -1;
+    }
+
     if (!data->forced_emergency) {
         /* if user initiates a emergency sync wihtout forced_emergency, 
            enable all sources by default*/
-        g_hash_table_iter_init (&iter, sources);
-        while (g_hash_table_iter_next (&iter, (gpointer)&key, NULL)) {
-            g_hash_table_insert (data->emergency_sources,
-                                 g_strdup (key), "");
+        g_hash_table_iter_init (&iter, data->current_service->source_configs);
+        while (g_hash_table_iter_next (&iter, (gpointer)&key, (gpointer)&source_conf)) {
+            if (source_config_is_usable (source_conf)) {
+                g_hash_table_insert (data->emergency_sources, g_strdup (key), "");
+            }
         }
     }
 
@@ -1980,10 +2513,23 @@ get_reports_cb (SyncevoServer *server,
     /* update service UI */
     refresh_last_synced_label (data);
     if (error_msg) {
+        GtkMessageType type = GTK_MESSAGE_ERROR;
+
+        if (response == SYNC_ERROR_RESPONSE_EMERGENCY) {
+            type = GTK_MESSAGE_QUESTION;
+        }
+
+        set_info_bar (data->info_bar, type, response, error_msg);
+        g_free (error_msg);
+    } else if (data->current_operation == OP_RESTORE) {
+        /* special case for just after restoring */
+        error_msg = g_strdup_printf
+            (_("You've just restored a backup. The changes have not been "
+               "synced with %s yet"), data->current_service->pretty_name);
         set_info_bar (data->info_bar,
-                      GTK_MESSAGE_ERROR, response,
+                      GTK_MESSAGE_INFO,
+                      SYNC_ERROR_RESPONSE_SYNC,
                       error_msg);
-        g_free (error_msg);
     }
 
     g_hash_table_destroy (sources);
@@ -2003,6 +2549,43 @@ set_config_cb (SyncevoSession *session,
 }
 
 static void
+restore_cb (SyncevoSession *session,
+            GError *error,
+            app_data *data)
+{
+    if (error) {
+        g_warning ("Error in Session.Restore: %s", error->message);
+        g_error_free (error);
+
+        return;
+    }
+}
+
+static void
+restore_backup (app_data *data, SyncevoSession *session, const char *dir)
+{
+    char **sources;
+    GHashTableIter iter;
+    int i = 0;
+    char *source;
+
+    sources = g_malloc0 (sizeof (char*) *
+                         (g_hash_table_size (data->emergency_sources) + 1));
+
+    g_hash_table_iter_init (&iter, data->emergency_sources);
+    while (g_hash_table_iter_next (&iter, (gpointer)&source, NULL)) {
+        sources[i++] = g_strdup (source);
+    }
+    sources[i] = NULL;
+
+    syncevo_session_restore (session, dir, TRUE, (const char**)sources,
+                             (SyncevoSessionGenericCb)restore_cb,
+                             data);
+
+    g_strfreev (sources);
+}
+
+static void
 save_config (app_data *data, SyncevoSession *session)
 {
     syncevo_session_set_config (session,
@@ -2097,6 +2680,7 @@ run_operation (operation_data *op_data, SyncevoSession *session)
         return;
     }
     op_data->started = TRUE;
+    op_data->data->current_operation = op_data->operation;
 
     /* time for business */
     switch (op_data->operation) {
@@ -2120,6 +2704,9 @@ run_operation (operation_data *op_data, SyncevoSession *session)
     case OP_SAVE:
         save_config (op_data->data, session);
         break;
+    case OP_RESTORE:
+        restore_backup (op_data->data, session, op_data->dir);
+        break;
     default:
         g_warn_if_reached ();
     }
@@ -2247,16 +2834,21 @@ show_emergency_view (app_data *data)
     mux_window_set_current_page (MUX_WINDOW (data->sync_win),
                                  data->emergency_index);
 #else
+    gtk_widget_hide (data->services_win);
     gtk_window_present (GTK_WINDOW (data->emergency_win));
 #endif
 }
 
 static void
-show_services_list (app_data *data)
+show_services_list (app_data *data, const char *config_id_to_open)
 {
+    g_free (data->config_id_to_open);
+    data->config_id_to_open = g_strdup (config_id_to_open);
+
 #ifdef USE_MOBLIN_UX
     mux_window_set_settings_visible (MUX_WINDOW (data->sync_win), TRUE);
 #else
+    gtk_widget_hide (data->emergency_win);
     gtk_window_present (GTK_WINDOW (data->services_win));
     update_services_list (data);
 #endif
@@ -2269,6 +2861,7 @@ show_main_view (app_data *data)
     mux_window_set_current_page (MUX_WINDOW (data->sync_win), -1);
 #else
     gtk_widget_hide (data->services_win);
+    gtk_widget_hide (data->emergency_win);
 #endif
     gtk_window_present (GTK_WINDOW (data->sync_win));
 }
@@ -2292,8 +2885,11 @@ get_error_string_for_code (int error_code, SyncErrorResponse *response)
         if (response) {
             *response = SYNC_ERROR_RESPONSE_EMERGENCY;
         }
-        return g_strdup (_("A normal sync is not possible at this time. You "
-                           "will need to fix things before we can sync again."));
+        return g_strdup (_("A normal sync is not possible at this time. The server "
+                           "suggests a slow sync, but this might not always be "
+                           "what you want if both ends already have data."));
+    case 22002:
+        return g_strdup (_("The sync service died unexpectedly."));
     case DB_Unauthorized:
         if (response) {
             *response = SYNC_ERROR_RESPONSE_SETTINGS_OPEN;
@@ -2309,8 +2905,11 @@ get_error_string_for_code (int error_code, SyncErrorResponse *response)
         return g_strdup(_("The source could not be found. Could there be a "
                           "problem with the server settings?"));
     case DB_Fatal:
-        /* This can happen when EDS is borked, restart may help... */
         return g_strdup(_("Fatal database error"));
+    case LOCAL_STATUS_CODE + DB_Fatal:
+        /* This can happen when EDS is borked, restart it may help... */
+        return g_strdup(_("There is a problem with the local database. "
+                          "Syncing again or rebooting may help."));
     case DB_Error:
         return g_strdup(_("Database error"));
     case DB_Full:
@@ -2429,16 +3028,104 @@ get_presence_cb (SyncevoServer *server,
 }
 
 static void
+password_dialog_response_cb (GtkWidget *dialog, int response, app_data *data)
+{
+    const char *password;
+    GHashTable *return_dict;
+
+    return_dict = g_hash_table_new (g_str_hash, g_str_equal);
+
+    if (response == GTK_RESPONSE_OK) {
+        password = gtk_entry_get_text (GTK_ENTRY (data->password_dialog_entry));
+        g_hash_table_insert (return_dict, "password", (gpointer)password);
+    }
+
+    syncevo_server_info_response (data->server, data->password_dialog_id,
+                                  "response", return_dict, NULL, NULL);
+
+    g_hash_table_destroy (return_dict);
+
+    g_free (data->password_dialog_id);
+    data->password_dialog_id = NULL;
+    gtk_widget_destroy (dialog);
+}
+
+static void
 info_request_cb (SyncevoServer *syncevo,
                  char *id,
                  char *session_path,
                  char *state,
                  char *handler_path,
                  char *type,
+                 GHashTable *parameters,
                  app_data *data)
 {
-    /* Implementation waiting for moblin bug #6376*/
-    g_warning ("InfoRequest handler not implemented yet");
+    GHashTable *t;
+    GtkWidget *dialog, *content, *label, *align;
+    char *msg;
+
+    if (g_strcmp0 (state, "request") != 0 ||
+        g_strcmp0 (type, "password") != 0) {
+        /* not handling other stuff */
+        return;
+    }
+
+    if (!data->running_session ||
+        g_strcmp0 (session_path,
+                   syncevo_session_get_path (data->running_session)) != 0) {
+        /* not our problem */
+        return;
+    }
+
+    t = g_hash_table_new (g_str_hash, g_str_equal);
+    syncevo_server_info_response (syncevo, id, "working", t, NULL, NULL);
+    g_hash_table_destroy (t);
+
+    data->password_dialog_id = g_strdup (id);
+
+    /* TRANSLATORS: password request dialog contents: title, cancel button
+     * and ok button */
+    dialog = gtk_dialog_new_with_buttons (_("Password is required for sync"),
+                                          GTK_WINDOW (data->sync_win),
+                                          GTK_DIALOG_DESTROY_WITH_PARENT,
+                                          _("Cancel sync"), GTK_RESPONSE_CANCEL,
+                                          _("Sync with password"), GTK_RESPONSE_OK,
+                                          NULL);
+    content = GTK_DIALOG (dialog)->vbox;
+
+    align = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
+    gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 16, 16);
+    gtk_widget_show (align);
+    gtk_box_pack_start (GTK_BOX (content), align, FALSE, FALSE, 6);
+
+    /* TRANSLATORS: password request dialog message, placeholder is service name */
+    msg = g_strdup_printf (_("Please enter password for syncing with %s:"),
+                           data->current_service->pretty_name);
+    label = gtk_label_new (msg);
+    gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+    gtk_widget_set_size_request (label, 500, -1);
+
+    gtk_widget_show (label);
+    gtk_container_add (GTK_CONTAINER (align), label);
+    g_free (msg);
+
+    align = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
+    gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 16, 16);
+    gtk_widget_show (align);
+    gtk_box_pack_start (GTK_BOX (content), align, FALSE, FALSE, 6);
+
+    data->password_dialog_entry = gtk_entry_new_with_max_length (99);
+    gtk_entry_set_width_chars (GTK_ENTRY (data->password_dialog_entry), 30);
+    gtk_entry_set_visibility (GTK_ENTRY (data->password_dialog_entry), FALSE);
+
+    gtk_widget_show (data->password_dialog_entry);
+    gtk_container_add (GTK_CONTAINER (align), data->password_dialog_entry);
+
+    g_signal_connect (dialog, "response",
+                      G_CALLBACK (password_dialog_response_cb), data);
+
+    gtk_window_present (GTK_WINDOW (dialog));
+    gtk_widget_grab_focus (data->password_dialog_entry);
 }
 
 static void
@@ -2457,6 +3144,15 @@ server_presence_changed_cb (SyncevoServer *server,
 }
 
 static void
+server_templates_changed_cb (SyncevoServer *server,
+                             app_data *data)
+{
+    if (GTK_WIDGET_VISIBLE (data->services_box)) {
+        update_services_list (data);
+    }
+}
+
+static void
 server_session_changed_cb (SyncevoServer *server,
                            char *path,
                            gboolean started,
@@ -2514,8 +3210,8 @@ get_config_for_default_peer_cb (SyncevoServer *syncevo,
     syncevo_config_free (config);
 }
 
-GtkWidget*
-sync_ui_create_main_window ()
+app_data*
+sync_ui_create ()
 {
     app_data *data;
 
@@ -2536,6 +3232,8 @@ sync_ui_create_main_window ()
                       G_CALLBACK (server_session_changed_cb), data);
     g_signal_connect (data->server, "presence_changed",
                       G_CALLBACK (server_presence_changed_cb), data);
+    g_signal_connect (data->server, "templates_changed",
+                      G_CALLBACK (server_templates_changed_cb), data);
     g_signal_connect (data->server, "info-request",
                       G_CALLBACK (info_request_cb), data);
 
@@ -2551,5 +3249,16 @@ sync_ui_create_main_window ()
 
     gtk_window_present (GTK_WINDOW (data->sync_win));
 
-    return data->sync_win;
+    return data;
+}
+
+void sync_ui_show_settings (app_data *data, const char *id)
+{
+    show_services_list (data, id);
+}
+
+GtkWindow*
+sync_ui_get_main_window (app_data *data)
+{
+    return GTK_WINDOW(data->sync_win);
 }
index 15ecf22..1ffc38c 100644 (file)
 #define SYNC_UI_LIST_ICON_SIZE 32
 #define SYNC_UI_LIST_BTN_WIDTH 150
 
+typedef struct _app_data app_data;
+
 typedef enum SyncErrorResponse {
     SYNC_ERROR_RESPONSE_NONE,
+    SYNC_ERROR_RESPONSE_SYNC,
     SYNC_ERROR_RESPONSE_SETTINGS_SELECT,
     SYNC_ERROR_RESPONSE_SETTINGS_OPEN,
     SYNC_ERROR_RESPONSE_EMERGENCY,
+    SYNC_ERROR_RESPONSE_EMERGENCY_SLOW_SYNC,
 } SyncErrorResponse;
 
 
 char* get_pretty_source_name (const char *source_name);
 char* get_error_string_for_code (int error_code, SyncErrorResponse *response);
 void show_error_dialog (GtkWidget *widget, const char* message);
+gboolean show_confirmation (GtkWidget *widget, const char *message, const char *yes, const char *no);
+
+app_data *sync_ui_create ();
+GtkWindow *sync_ui_get_main_window (app_data *data);
+void sync_ui_show_settings (app_data *data, const char *id);
 
-GtkWidget* sync_ui_create_main_window ();
+void toggle_set_active (GtkWidget *toggle, gboolean active);
+gboolean toggle_get_active (GtkWidget *toggle);
 
 #endif
index 6b4aa9b..077a160 100644 (file)
@@ -3,6 +3,7 @@
   <!-- interface-requires gtk+ 2.10 -->
   <!-- interface-naming-policy toplevel-contextual -->
   <widget class="GtkWindow" id="sync_win">
+    <property name="width_request">1024</property>
     <property name="border_width">5</property>
     <property name="title" translatable="yes">Sync</property>
     <property name="default_width">800</property>
@@ -55,6 +56,8 @@
                             <property name="xalign">0</property>
                             <property name="yalign">0.80000001192092896</property>
                             <property name="use_markup">True</property>
+                            <property name="ellipsize">end</property>
+                            <property name="max_width_chars">45</property>
                           </widget>
                           <packing>
                             <property name="expand">False</property>
                                                   </widget>
                                                 </child>
                                                 <child>
-                                                  <widget class="GtkLabel" id="label2">
-                                                    <property name="visible">True</property>
-                                                    <property name="xpad">5</property>
-                                                    <property name="label" translatable="yes">&lt;b&gt;Data&lt;/b&gt;</property>
-                                                    <property name="use_markup">True</property>
-                                                  </widget>
+                                                  <placeholder/>
                                                   <packing>
                                                     <property name="type">label_item</property>
                                                   </packing>
                                   </widget>
                                   <packing>
                                     <property name="expand">False</property>
+                                    <property name="fill">False</property>
                                     <property name="padding">6</property>
                                     <property name="position">0</property>
                                   </packing>
                                 </child>
+                                <child>
+                                  <widget class="GtkVBox" id="spinner_box">
+                                    <property name="height_request">24</property>
+                                    <property name="visible">True</property>
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <widget class="GtkImage" id="spinner_image">
+                                        <property name="visible">True</property>
+                                        <property name="stock">gtk-missing-image</property>
+                                      </widget>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
                               </widget>
                               <packing>
                                 <property name="position">0</property>
                               </packing>
                             </child>
-                            <child>
-                              <widget class="GtkHBox" id="hbox8">
-                                <property name="visible">True</property>
-                              </widget>
-                              <packing>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
                           </widget>
                           <packing>
                             <property name="expand">False</property>
                           <widget class="GtkHBox" id="hbox20">
                             <property name="visible">True</property>
                             <child>
-                              <widget class="GtkVBox" id="vbox19">
-                                <property name="height_request">30</property>
-                                <property name="visible">True</property>
-                                <property name="orientation">vertical</property>
-                                <child>
-                                  <placeholder/>
-                                </child>
-                              </widget>
-                              <packing>
-                                <property name="position">0</property>
-                              </packing>
-                            </child>
-                            <child>
                               <widget class="GtkVBox" id="info_box">
                                 <property name="visible">True</property>
                                 <property name="orientation">vertical</property>
                                     <property name="position">0</property>
                                   </packing>
                                 </child>
-                                <child>
-                                  <widget class="GtkHBox" id="errors_box">
-                                    <property name="spacing">5</property>
-                                    <child>
-                                      <widget class="GtkImage" id="error_img">
-                                        <property name="visible">True</property>
-                                        <property name="xalign">0</property>
-                                        <property name="yalign">0</property>
-                                        <property name="stock">gtk-dialog-error</property>
-                                        <property name="icon-size">5</property>
-                                      </widget>
-                                      <packing>
-                                        <property name="expand">False</property>
-                                        <property name="fill">False</property>
-                                        <property name="position">0</property>
-                                      </packing>
-                                    </child>
-                                    <child>
-                                      <widget class="GtkVBox" id="error_box">
-                                        <property name="visible">True</property>
-                                        <property name="orientation">vertical</property>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                      </widget>
-                                      <packing>
-                                        <property name="position">1</property>
-                                      </packing>
-                                    </child>
-                                  </widget>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
                               </widget>
                               <packing>
                                 <property name="expand">False</property>
                                 <property name="padding">5</property>
-                                <property name="position">1</property>
+                                <property name="position">0</property>
                               </packing>
                             </child>
                           </widget>
                                 <property name="orientation">vertical</property>
                                 <property name="spacing">10</property>
                                 <child>
+                                  <widget class="GtkLabel" id="action_label">
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes" comments="title for the buttons on the right side of main view">&lt;b&gt;Actions&lt;/b&gt;</property>
+                                    <property name="use_markup">True</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
                                   <widget class="GtkButton" id="sync_btn">
                                     <property name="label">Sync now</property>
                                     <property name="visible">True</property>
                                   </widget>
                                   <packing>
                                     <property name="fill">False</property>
-                                    <property name="position">0</property>
+                                    <property name="position">1</property>
                                   </packing>
                                 </child>
                                 <child>
@@ -404,7 +379,7 @@ sync service</property>
                                   </widget>
                                   <packing>
                                     <property name="fill">False</property>
-                                    <property name="position">1</property>
+                                    <property name="position">2</property>
                                   </packing>
                                 </child>
                                 <child>
@@ -418,7 +393,7 @@ emergency</property>
                                   <packing>
                                     <property name="expand">False</property>
                                     <property name="fill">False</property>
-                                    <property name="position">2</property>
+                                    <property name="position">3</property>
                                   </packing>
                                 </child>
                               </widget>
@@ -472,6 +447,7 @@ emergency</property>
     </child>
   </widget>
   <widget class="GtkWindow" id="services_win">
+    <property name="width_request">1024</property>
     <property name="border_width">5</property>
     <property name="title" translatable="yes">Settings</property>
     <property name="modal">True</property>
@@ -489,170 +465,252 @@ emergency</property>
             <property name="label_xalign">0</property>
             <property name="shadow_type">none</property>
             <child>
-              <widget class="GtkHBox" id="hbox4">
+              <widget class="GtkScrolledWindow" id="settings_scrolledwindow">
                 <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="vscrollbar_policy">automatic</property>
                 <child>
-                  <widget class="GtkVBox" id="vbox7">
+                  <widget class="GtkViewport" id="settings_viewport">
                     <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
+                    <property name="resize_mode">queue</property>
                     <child>
-                      <widget class="GtkVBox" id="vbox13">
+                      <widget class="GtkHBox" id="hbox4">
                         <property name="visible">True</property>
-                        <property name="orientation">vertical</property>
-                        <property name="spacing">5</property>
-                        <child>
-                          <widget class="GtkLabel" id="label8">
-                            <property name="visible">True</property>
-                            <property name="xalign">0</property>
-                            <property name="label" translatable="yes">&lt;big&gt;Supported services&lt;/big&gt;</property>
-                            <property name="use_markup">True</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkHBox" id="hbox11">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkLabel" id="label5">
-                                <property name="visible">True</property>
-                                <property name="label" translatable="yes">To sync you'll need a network connection and an account with a sync service.
-We support the following services: </property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="position">0</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
                         <child>
-                          <widget class="GtkScrolledWindow" id="scrolledwindow">
+                          <widget class="GtkAlignment" id="alignment_1">
                             <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="hscrollbar_policy">never</property>
-                            <property name="vscrollbar_policy">automatic</property>
+                            <property name="bottom_padding">12</property>
+                            <property name="left_padding">36</property>
+                            <property name="right_padding">12</property>
                             <child>
-                              <widget class="GtkViewport" id="viewport1">
+                              <widget class="GtkVBox" id="vbox7">
                                 <property name="visible">True</property>
-                                <property name="resize_mode">queue</property>
+                                <property name="orientation">vertical</property>
+                                <property name="spacing">12</property>
                                 <child>
-                                  <widget class="GtkVBox" id="services_box">
+                                  <widget class="GtkVBox" id="vbox13">
                                     <property name="visible">True</property>
                                     <property name="orientation">vertical</property>
+                                    <property name="spacing">5</property>
                                     <child>
-                                      <placeholder/>
+                                      <widget class="GtkLabel" id="label8">
+                                        <property name="visible">True</property>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">&lt;big&gt;Network sync&lt;/big&gt;</property>
+                                        <property name="use_markup">True</property>
+                                      </widget>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="position">0</property>
+                                      </packing>
                                     </child>
                                     <child>
-                                      <placeholder/>
+                                      <widget class="GtkHBox" id="hbox11">
+                                        <property name="visible">True</property>
+                                        <child>
+                                          <widget class="GtkLabel" id="label5">
+                                            <property name="visible">True</property>
+                                            <property name="label" translatable="yes">To sync you'll need a network connection and an account with a sync service.
+We support the following services: </property>
+                                          </widget>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="position">0</property>
+                                          </packing>
+                                        </child>
+                                      </widget>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkScrolledWindow" id="scrolledwindow">
+                                        <property name="height_request">300</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="hscrollbar_policy">automatic</property>
+                                        <property name="vscrollbar_policy">automatic</property>
+                                        <child>
+                                          <widget class="GtkViewport" id="viewport1">
+                                            <property name="visible">True</property>
+                                            <property name="resize_mode">queue</property>
+                                            <child>
+                                              <widget class="GtkVBox" id="services_box">
+                                                <property name="visible">True</property>
+                                                <property name="orientation">vertical</property>
+                                                <child>
+                                                  <placeholder/>
+                                                </child>
+                                                <child>
+                                                  <placeholder/>
+                                                </child>
+                                              </widget>
+                                            </child>
+                                          </widget>
+                                        </child>
+                                      </widget>
+                                      <packing>
+                                        <property name="position">2</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkHBox" id="hbox14">
+                                        <property name="visible">True</property>
+                                        <child>
+                                          <widget class="GtkLabel" id="label6">
+                                            <property name="visible">True</property>
+                                            <property name="xalign">0</property>
+                                            <property name="label" translatable="yes">If you don't see your service above but know that your sync provider uses SyncML
+you can setup a service manually.</property>
+                                          </widget>
+                                          <packing>
+                                            <property name="position">0</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <widget class="GtkButton" id="new_service_btn">
+                                            <property name="label" translatable="yes">Add new service</property>
+                                            <property name="width_request">150</property>
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">True</property>
+                                            <property name="receives_default">True</property>
+                                          </widget>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="padding">8</property>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                      </widget>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">3</property>
+                                      </packing>
                                     </child>
                                   </widget>
+                                  <packing>
+                                    <property name="padding">8</property>
+                                    <property name="position">0</property>
+                                  </packing>
                                 </child>
-                              </widget>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="position">2</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="padding">8</property>
-                        <property name="position">0</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkVBox" id="vbox12">
-                        <property name="visible">True</property>
-                        <property name="orientation">vertical</property>
-                        <property name="spacing">5</property>
-                        <child>
-                          <widget class="GtkLabel" id="label7">
-                            <property name="visible">True</property>
-                            <property name="xalign">0</property>
-                            <property name="label" translatable="yes">&lt;big&gt;Manual setup&lt;/big&gt;</property>
-                            <property name="use_markup">True</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
-                        <child>
-                          <widget class="GtkHBox" id="hbox14">
-                            <property name="visible">True</property>
-                            <child>
-                              <widget class="GtkLabel" id="label6">
-                                <property name="visible">True</property>
-                                <property name="xalign">0</property>
-                                <property name="label" translatable="yes">If you don't see your service above but know that your sync provider uses SyncML
-you can setup a service manually.</property>
-                              </widget>
-                              <packing>
-                                <property name="position">0</property>
-                              </packing>
-                            </child>
-                            <child>
-                              <widget class="GtkButton" id="new_service_btn">
-                                <property name="label" translatable="yes">Add new service</property>
-                                <property name="width_request">150</property>
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="receives_default">True</property>
-                              </widget>
-                              <packing>
-                                <property name="expand">False</property>
-                                <property name="padding">8</property>
-                                <property name="position">1</property>
-                              </packing>
-                            </child>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="position">1</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="padding">8</property>
-                        <property name="position">1</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <widget class="GtkHBox" id="hbox1">
-                        <property name="visible">True</property>
-                        <child>
-                          <widget class="GtkButton" id="back_btn">
-                            <property name="label" translatable="yes">Back to sync</property>
-                            <property name="visible">True</property>
-                            <property name="can_focus">True</property>
-                            <property name="receives_default">True</property>
-                          </widget>
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="position">0</property>
-                          </packing>
-                        </child>
-                      </widget>
-                      <packing>
-                        <property name="expand">False</property>
-                        <property name="padding">16</property>
-                        <property name="pack_type">end</property>
-                        <property name="position">2</property>
-                      </packing>
+                                <child>
+                                  <widget class="GtkVBox" id="direct_sync_box">
+                                    <property name="height_request">400</property>
+                                    <property name="visible">True</property>
+                                    <property name="orientation">vertical</property>
+                                    <property name="spacing">5</property>
+                                    <child>
+                                      <widget class="GtkLabel" id="label2">
+                                        <property name="visible">True</property>
+                                        <property name="xalign">0</property>
+                                        <property name="label" translatable="yes">&lt;big&gt;Direct sync&lt;/big&gt;</property>
+                                        <property name="use_markup">True</property>
+                                      </widget>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkHBox" id="hbox1">
+                                        <property name="visible">True</property>
+                                        <child>
+                                          <widget class="GtkLabel" id="label3">
+                                            <property name="visible">True</property>
+                                            <property name="label" translatable="yes">Use Bluetooth to Sync your data from one device to another.</property>
+                                          </widget>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="position">0</property>
+                                          </packing>
+                                        </child>
+                                      </widget>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkScrolledWindow" id="device_scrolledwindow">
+                                        <property name="height_request">300</property>
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="hscrollbar_policy">automatic</property>
+                                        <property name="vscrollbar_policy">automatic</property>
+                                        <child>
+                                          <widget class="GtkViewport" id="viewport2">
+                                            <property name="visible">True</property>
+                                            <property name="resize_mode">queue</property>
+                                            <child>
+                                              <widget class="GtkVBox" id="devices_box">
+                                                <property name="visible">True</property>
+                                                <property name="orientation">vertical</property>
+                                                <child>
+                                                  <placeholder/>
+                                                </child>
+                                                <child>
+                                                  <placeholder/>
+                                                </child>
+                                              </widget>
+                                            </child>
+                                          </widget>
+                                        </child>
+                                      </widget>
+                                      <packing>
+                                        <property name="position">2</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <widget class="GtkHBox" id="add_bt_device_box">
+                                        <property name="visible">True</property>
+                                        <child>
+                                          <widget class="GtkLabel" id="label_4">
+                                            <property name="visible">True</property>
+                                            <property name="xalign">0</property>
+                                            <property name="label" translatable="yes">You will need to add Bluetooth devices before they can be synced.</property>
+                                          </widget>
+                                          <packing>
+                                            <property name="position">0</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <widget class="GtkButton" id="new_device_btn">
+                                            <property name="label" translatable="yes">Add new device</property>
+                                            <property name="width_request">150</property>
+                                            <property name="can_focus">True</property>
+                                            <property name="receives_default">True</property>
+                                          </widget>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="padding">8</property>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                      </widget>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">3</property>
+                                      </packing>
+                                    </child>
+                                  </widget>
+                                  <packing>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </widget>
+                            </child>
+                          </widget>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </widget>
                     </child>
                   </widget>
-                  <packing>
-                    <property name="padding">40</property>
-                    <property name="position">0</property>
-                  </packing>
                 </child>
               </widget>
             </child>
@@ -667,6 +725,7 @@ you can setup a service manually.</property>
             </child>
           </widget>
           <packing>
+            <property name="padding">6</property>
             <property name="position">0</property>
           </packing>
         </child>
@@ -674,6 +733,7 @@ you can setup a service manually.</property>
     </child>
   </widget>
   <widget class="GtkWindow" id="emergency_win">
+    <property name="width_request">1024</property>
     <property name="border_width">5</property>
     <property name="title" translatable="yes">Sync Emergency</property>
     <property name="modal">True</property>
@@ -758,7 +818,7 @@ you can setup a service manually.</property>
                             <child>
                               <widget class="GtkLabel" id="emergency_expander_label">
                                 <property name="visible">True</property>
-                                <property name="label">Affected data: ZYB Contacts, Calendar -- not implemented yet</property>
+                                <property name="label">Affected data:</property>
                               </widget>
                               <packing>
                                 <property name="type">label_item</property>
@@ -804,19 +864,26 @@ you can setup a service manually.</property>
                             <child>
                               <widget class="GtkHBox" id="hbox19">
                                 <property name="visible">True</property>
+                                <property name="spacing">6</property>
                                 <child>
                                   <widget class="GtkButton" id="slow_sync_btn">
-                                    <property name="label" translatable="yes">Slow sync </property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
                                     <property name="receives_default">True</property>
+                                    <child>
+                                      <widget class="GtkLabel" id="slow_sync_btn_label">
+                                        <property name="visible">True</property>
+                                        <property name="xpad">16</property>
+                                        <property name="label" translatable="yes">Slow sync</property>
+                                      </widget>
+                                    </child>
                                   </widget>
                                   <packing>
                                     <property name="position">0</property>
                                   </packing>
                                 </child>
                                 <child>
-                                  <widget class="GtkLabel" id="label19">
+                                  <widget class="GtkLabel" id="slow_sync_explanation_label">
                                     <property name="visible">True</property>
                                     <property name="xpad">8</property>
                                     <property name="label" translatable="yes">A slow sync compares items from both sides and tries to merge them. 
@@ -871,12 +938,18 @@ This may fail in some cases, leading to duplicates or lost information.</propert
                                 <property name="visible">True</property>
                                 <child>
                                   <widget class="GtkButton" id="refresh_from_server_btn">
-                                    <property name="label">Delete all your local
-information and replace
-with data from Zyb</property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
                                     <property name="receives_default">True</property>
+                                    <child>
+                                      <widget class="GtkLabel" id="refresh_from_server_btn_label">
+                                        <property name="visible">True</property>
+                                        <property name="xpad">16</property>
+                                        <property name="label" translatable="yes">Delete all your local
+information and replace
+with data from Zyb</property>
+                                      </widget>
+                                    </child>
                                   </widget>
                                   <packing>
                                     <property name="expand">False</property>
@@ -887,7 +960,8 @@ with data from Zyb</property>
                                   <widget class="GtkLabel" id="label4">
                                     <property name="visible">True</property>
                                     <property name="xpad">15</property>
-                                    <property name="label" translatable="yes" comments="text between the two &quot;start from scratch&quot; buttons in emergency view">or</property>
+                                    <property name="label" translatable="yes" comments="text between the two &quot;start from scratch&quot; buttons in emergency view">&lt;b&gt;or&lt;/b&gt;</property>
+                                    <property name="use_markup">True</property>
                                   </widget>
                                   <packing>
                                     <property name="expand">False</property>
@@ -897,12 +971,18 @@ with data from Zyb</property>
                                 </child>
                                 <child>
                                   <widget class="GtkButton" id="refresh_from_client_btn">
-                                    <property name="label">Delete all data on Zyb 
-and replace with your
-local information</property>
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
                                     <property name="receives_default">True</property>
+                                    <child>
+                                      <widget class="GtkLabel" id="refresh_from_client_btn_label">
+                                        <property name="visible">True</property>
+                                        <property name="xpad">16</property>
+                                        <property name="label" translatable="yes">Delete all data on Zyb 
+and replace with your
+local information</property>
+                                      </widget>
+                                    </child>
                                   </widget>
                                   <packing>
                                     <property name="expand">False</property>
@@ -926,6 +1006,7 @@ local information</property>
                     </child>
                     <child>
                       <widget class="GtkVBox" id="vbox21">
+                        <property name="visible">True</property>
                         <property name="orientation">vertical</property>
                         <property name="spacing">5</property>
                         <child>
@@ -943,175 +1024,197 @@ local information</property>
                         <child>
                           <widget class="GtkAlignment" id="alignment2">
                             <property name="visible">True</property>
+                            <property name="resize_mode">queue</property>
                             <property name="left_padding">40</property>
-                            <property name="right_padding">200</property>
+                            <property name="right_padding">40</property>
                             <child>
-                              <widget class="GtkScrolledWindow" id="scrolledwindow1">
+                              <widget class="GtkVBox" id="vbox22">
                                 <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <property name="hscrollbar_policy">automatic</property>
-                                <property name="vscrollbar_policy">automatic</property>
+                                <property name="orientation">vertical</property>
+                                <child>
+                                  <widget class="GtkLabel" id="label12">
+                                    <property name="width_request">800</property>
+                                    <property name="visible">True</property>
+                                    <property name="xalign">0</property>
+                                    <property name="label" translatable="yes" comments="explanation of &quot;Restore backup&quot; function">Backups are made before every time we Sync. Choose a backup to restore. Any changes you have made since then will be lost.</property>
+                                    <property name="wrap">True</property>
+                                  </widget>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
                                 <child>
-                                  <widget class="GtkViewport" id="backup_viewport">
+                                  <widget class="GtkScrolledWindow" id="scrolledwindow1">
                                     <property name="visible">True</property>
-                                    <property name="resize_mode">queue</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="hscrollbar_policy">automatic</property>
+                                    <property name="vscrollbar_policy">automatic</property>
                                     <child>
-                                      <widget class="GtkTable" id="table1">
+                                      <widget class="GtkViewport" id="backup_viewport">
                                         <property name="visible">True</property>
-                                        <property name="border_width">3</property>
-                                        <property name="n_rows">10</property>
-                                        <property name="n_columns">2</property>
-                                        <property name="row_spacing">3</property>
-                                        <child>
-                                          <widget class="GtkLabel" id="label59">
-                                            <property name="visible">True</property>
-                                            <property name="xalign">0</property>
-                                            <property name="ypad">5</property>
-                                            <property name="label">Backup before syncing Dec 5th 2009 16:35</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="top_attach">2</property>
-                                            <property name="bottom_attach">3</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <widget class="GtkLabel" id="label69">
-                                            <property name="visible">True</property>
-                                            <property name="xalign">0</property>
-                                            <property name="ypad">5</property>
-                                            <property name="label">Backup before syncing Dec 5th 2009 13:35</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="top_attach">3</property>
-                                            <property name="bottom_attach">4</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <widget class="GtkLabel" id="label79">
-                                            <property name="visible">True</property>
-                                            <property name="xalign">0</property>
-                                            <property name="ypad">5</property>
-                                            <property name="label">Backup before syncing Dec 5th 2009 11:35</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="top_attach">4</property>
-                                            <property name="bottom_attach">5</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
+                                        <property name="resize_mode">queue</property>
                                         <child>
-                                          <widget class="GtkLabel" id="label89">
+                                          <widget class="GtkTable" id="emergency_backup_table">
                                             <property name="visible">True</property>
-                                            <property name="xalign">0</property>
-                                            <property name="ypad">5</property>
-                                            <property name="label">Backup before syncing Dec 3rd</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="top_attach">5</property>
-                                            <property name="bottom_attach">6</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <widget class="GtkLabel" id="label109">
-                                            <property name="visible">True</property>
-                                            <property name="xalign">0</property>
-                                            <property name="ypad">5</property>
-                                            <property name="label">Backup before syncing Dec 1st</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="top_attach">6</property>
-                                            <property name="bottom_attach">7</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <widget class="GtkLabel" id="label29">
-                                            <property name="visible">True</property>
-                                            <property name="xalign">0</property>
-                                            <property name="ypad">5</property>
-                                            <property name="label">Backup before syncing yesterday</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="top_attach">1</property>
-                                            <property name="bottom_attach">2</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <widget class="GtkLabel" id="label30">
-                                            <property name="visible">True</property>
-                                            <property name="xalign">0</property>
-                                            <property name="ypad">5</property>
-                                            <property name="label">Backup before syncing two hours ago</property>
-                                          </widget>
-                                          <packing>
-                                            <property name="y_options">GTK_FILL</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <widget class="GtkButton" id="button1">
-                                            <property name="label">not implemented yet</property>
-                                            <property name="width_request">150</property>
-                                            <property name="visible">True</property>
-                                            <property name="can_focus">True</property>
-                                            <property name="receives_default">True</property>
+                                            <property name="n_rows">10</property>
+                                            <property name="n_columns">2</property>
+                                            <property name="row_spacing">16</property>
+                                            <child>
+                                              <widget class="GtkLabel" id="label59">
+                                                <property name="visible">True</property>
+                                                <property name="xalign">0</property>
+                                                <property name="ypad">5</property>
+                                                <property name="label">Backup before syncing Dec 5th 2009 16:35</property>
+                                              </widget>
+                                              <packing>
+                                                <property name="top_attach">2</property>
+                                                <property name="bottom_attach">3</property>
+                                                <property name="x_options">GTK_FILL</property>
+                                                <property name="y_options">GTK_FILL</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <widget class="GtkLabel" id="label69">
+                                                <property name="visible">True</property>
+                                                <property name="xalign">0</property>
+                                                <property name="ypad">5</property>
+                                                <property name="label">Backup before syncing Dec 5th 2009 13:35</property>
+                                              </widget>
+                                              <packing>
+                                                <property name="top_attach">3</property>
+                                                <property name="bottom_attach">4</property>
+                                                <property name="x_options">GTK_FILL</property>
+                                                <property name="y_options">GTK_FILL</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <widget class="GtkLabel" id="label79">
+                                                <property name="visible">True</property>
+                                                <property name="xalign">0</property>
+                                                <property name="ypad">5</property>
+                                                <property name="label">Backup before syncing Dec 5th 2009 11:35</property>
+                                              </widget>
+                                              <packing>
+                                                <property name="top_attach">4</property>
+                                                <property name="bottom_attach">5</property>
+                                                <property name="x_options">GTK_FILL</property>
+                                                <property name="y_options">GTK_FILL</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <widget class="GtkLabel" id="label89">
+                                                <property name="visible">True</property>
+                                                <property name="xalign">0</property>
+                                                <property name="ypad">5</property>
+                                                <property name="label">Backup before syncing Dec 3rd</property>
+                                              </widget>
+                                              <packing>
+                                                <property name="top_attach">5</property>
+                                                <property name="bottom_attach">6</property>
+                                                <property name="x_options">GTK_FILL</property>
+                                                <property name="y_options">GTK_FILL</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <widget class="GtkLabel" id="label109">
+                                                <property name="visible">True</property>
+                                                <property name="xalign">0</property>
+                                                <property name="ypad">5</property>
+                                                <property name="label">Backup before syncing Dec 1st</property>
+                                              </widget>
+                                              <packing>
+                                                <property name="top_attach">6</property>
+                                                <property name="bottom_attach">7</property>
+                                                <property name="x_options">GTK_FILL</property>
+                                                <property name="y_options">GTK_FILL</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <widget class="GtkLabel" id="label29">
+                                                <property name="visible">True</property>
+                                                <property name="xalign">0</property>
+                                                <property name="ypad">5</property>
+                                                <property name="label">Backup before syncing yesterday</property>
+                                              </widget>
+                                              <packing>
+                                                <property name="top_attach">1</property>
+                                                <property name="bottom_attach">2</property>
+                                                <property name="x_options">GTK_FILL</property>
+                                                <property name="y_options">GTK_FILL</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <widget class="GtkLabel" id="label30">
+                                                <property name="visible">True</property>
+                                                <property name="xalign">0</property>
+                                                <property name="ypad">5</property>
+                                                <property name="label">Backup before syncing two hours ago</property>
+                                              </widget>
+                                              <packing>
+                                                <property name="y_options">GTK_FILL</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <widget class="GtkButton" id="button1">
+                                                <property name="label">Restore</property>
+                                                <property name="visible">True</property>
+                                                <property name="can_focus">True</property>
+                                                <property name="receives_default">True</property>
+                                              </widget>
+                                              <packing>
+                                                <property name="left_attach">1</property>
+                                                <property name="right_attach">2</property>
+                                                <property name="x_options">GTK_FILL</property>
+                                                <property name="y_options">GTK_FILL</property>
+                                                <property name="x_padding">16</property>
+                                              </packing>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
+                                            <child>
+                                              <placeholder/>
+                                            </child>
                                           </widget>
-                                          <packing>
-                                            <property name="left_attach">1</property>
-                                            <property name="right_attach">2</property>
-                                            <property name="x_options">GTK_FILL</property>
-                                            <property name="y_options">GTK_FILL</property>
-                                            <property name="x_padding">16</property>
-                                          </packing>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
-                                        </child>
-                                        <child>
-                                          <placeholder/>
                                         </child>
                                       </widget>
                                     </child>
                                   </widget>
+                                  <packing>
+                                    <property name="position">1</property>
+                                  </packing>
                                 </child>
                               </widget>
                             </child>
diff --git a/src/syncclient_sample_config.xml b/src/syncclient_sample_config.xml
deleted file mode 100644 (file)
index 3581a01..0000000
+++ /dev/null
@@ -1,1435 +0,0 @@
-<?xml version="1.0"?>
-<!-- SYNTHESIS SYNCML CLIENT Version 3.2 Configuration file -->
-
-<sysync_config version="1.0">
-
-  <configvar name="logpath" value="$(defout_path)"/>
-
-  <!-- this string is output to every session debug logfile to identify the config in use -->
-  <configidstring>SyncEvolution client config</configidstring>
-
-  <!-- information about maximum supported message and object size (in bytes) -->
-  <maxmsgsize/>
-  <maxobjsize/>
-
-  <!-- information for DevInf -->
-  <model/>
-  <manufacturer/>
-  <hardwareversion/>
-  <firmwareversion/>
-  <devicetype/>
-  <configdate/>
-
-  <debug/>
-
-  <transport type="xpt">
-    <!-- allow HTTP 1.1 kepp-alive (multiple request-answer-exchanges in single TCP connection) -->
-    <keepconnection>true</keepconnection>
-  </transport>
-
-
-  <scripting>
-    <looptimeout>5</looptimeout>
-
-    <function><![CDATA[
-      // create a UID
-      string newuid() {
-        return "syuid" + NUMFORMAT(RANDOM(1000000),6,"0") + "." + (string)MILLISECONDS(NOW());
-      }
-    ]]></function>
-    <macro name="VCARD_BEFOREWRITE_SCRIPT_EVOLUTION"><![CDATA[
-      // a wordaround for cellphone in evolution. for incoming contacts, if there is only one CELL,
-      // strip the HOME or WORK flag from it. Evolution then should show it. */
-      INTEGER i, wanted, cell_phones;
-      i = 0;
-      cell_phones = 0;
-      while(i < SIZE(TEL_FLAGS)) {
-        // 0x10 is the flag of 'cell' type of telephone
-        if(TEL_FLAGS[i] & 0x10) {
-          cell_phones = cell_phones + 1;
-          wanted = i;
-        }
-        i = i + 1;
-      }
-      if(cell_phones == 1) {
-        TEL_FLAGS[wanted] = 0x10;
-      }
-
-      // Google sends TYPE=WORK and TYPE=HOME when it means
-      // normal VOICE phone numbers. Add that flag when
-      // importing into Evolution, because Evolution does not
-      // display the numbers without VOICE.
-      i = 0;
-      while(i < SIZE(TEL_FLAGS)) {
-        if(TEL_FLAGS[i] == 1 || TEL_FLAGS[i] == 2) {
-          TEL_FLAGS[i] = TEL_FLAGS[i] | 8;
-        }
-        i = i + 1;
-      }
-    ]]></macro>
-    <macro name="VCALENDAR_20TO10_PRIORITY_CONVERSION"><![CDATA[
-      //vCalendar10 has different interpretation from iCalendar20 in 'priority'.
-      //see mappings:  
-      //  Category      vCalendar1.0     iCalendar2.0
-      //   undefined         0               0
-      //   high              1             1 ~ 4
-      //   normal            2               5
-      //   low               3             6 ~ bigger
-      if(PRIORITY<5 && PRIORITY>0) {
-        PRIORITY=1;
-      }else if(PRIORITY==5){
-        PRIORITY=2;
-      }else if(PRIORITY>5){
-        PRIORITY=3;
-      } // 0 is undefined and remains unchanged
-    ]]></macro>
-    <macro name="VCALENDAR_10TO20_PRIORITY_CONVERSION"><![CDATA[
-      if(PRIORITY==2) {
-        PRIORITY=5;
-      }else if(PRIORITY==3){
-        PRIORITY=7;
-      } //others remain unchanged
-    ]]></macro>
-    <macro name="VCALENDAR10_BEFOREWRITE_SCRIPT"><![CDATA[
-      $VCALENDAR_20TO10_PRIORITY_CONVERSION;
-    ]]></macro>
-    <macro name="VCALENDAR10_AFTERREAD_SCRIPT"><![CDATA[
-      $VCALENDAR_10TO20_PRIORITY_CONVERSION;
-    ]]></macro>
-
-    <macro name="VCARD_INCOMING_NAMECHANGE_SCRIPT"><![CDATA[
-      STRING tmp;
-      tmp=NORMALIZED(FN);
-      if (tmp==EMPTY){
-        tmp=N_FIRST;
-        if (N_MIDDLE != EMPTY) {
-          if (tmp != EMPTY) {
-            tmp = tmp + " ";
-          }
-          tmp = tmp + N_MIDDLE;
-        }
-        if (N_LAST != EMPTY) {
-          if (tmp != EMPTY) {
-            tmp = tmp + " ";
-          }
-          tmp = tmp + N_LAST;
-        }
-        FN = tmp;
-      }
-    ]]></macro>
-
-               <!-- define script macros for scripts that are used by both vCalendar 1.0 and iCalendar 2.0 -->
-
-    <macro name="VCALENDAR_INCOMING_SCRIPT"><![CDATA[
-      STRING MATCHES[];
-      STRING CAT,CN,EM;
-      INTEGER i;
-      // make sure we have all trailing and leading spaces eliminated
-      DESCRIPTION=NORMALIZED(DESCRIPTION);
-      SUMMARY=NORMALIZED(SUMMARY);
-      // make sure that we have a DESCRIPTION
-      if (DESCRIPTION==EMPTY) DESCRIPTION=SUMMARY;
-      // calendar or todo
-      if (ISEVENT) {
-        // VEVENT
-        // - handle duration cases 
-        if (ISDURATION(DURATION)) {
-          if (DTEND==EMPTY) DTEND = DTSTART + DURATION;
-          if (DTSTART==EMPTY) DTSTART = DTEND - DURATION;
-        }
-        // - detect alldays in vCalendar 1.0 (0:00-0:00 or 23:59 localtime)
-        i = ALLDAYCOUNT(DTSTART,DTEND,TRUE);
-        if (ITEMDATATYPE()=="vCalendar10" && i>0) {
-          // DTSTART and DTEND represent allday event, make them date-only values
-          // - convert start to user zone (or floating) so it represents midnight
-          DTSTART = CONVERTTOUSERZONE(DTSTART);
-          MAKEALLDAY(DTSTART,DTEND,i);
-        }
-
-        // Make sure that all EXDATE times are in the same timezone as the start
-        // time. Some servers send them as UTC, which is all fine and well, but
-        // only if the timezone definition doesn't change. Also, libical does not
-        // handle such UTC EXDATEs, so let's convert it while the UTC and
-        // time zone definition (hopefully) are in sync.
-        if (TIMEZONE(DTSTART) != "UTC" && !ISFLOATING(DTSTART)) {
-          i = 0;
-          timestamp exdate;
-          while (i<SIZE(EXDATES)) {
-            exdate = EXDATES[i];
-            if (!ISDATEONLY(exdate) &&
-                (TIMEZONE(exdate) == "UTC" || ISFLOATING(exdate))) {
-              // "unfloat" floating time stamps: not sure whether that occcurs
-              // in practice, but it looks as wrong as UTC EXDATEs
-              EXDATES[i] = CONVERTTOZONE(exdate,DTSTART,TRUE);
-            }
-            i=i+1;
-          }
-        }
-        // If vcalendar1.0, rrule is not secondly, minutely, or hourly, we strip time information
-        // and only reserve date information
-        if (ITEMDATATYPE()=="vCalendar10" && RR_FREQ!="h" && RR_FREQ!="m" && RR_FREQ!="s") {
-          timestamp exdate;
-          i = 0;
-          while (i<SIZE(EXDATES)) {
-            exdate = EXDATES[i];
-            if (!ISDATEONLY(exdate)) {
-              EXDATES[i] = DATEONLY(exdate);
-            }
-            i=i+1;
-          }  
-        }
-
-        // - shape attendees (and make sure ATTENDEES[] is assigned even for empty email addresses)
-        i=0;
-        while(i<SIZE(ATTENDEES) || i<SIZE(ATTENDEE_CNS)) {
-          PARSEEMAILSPEC(ATTENDEES[i], CN, EM);
-          ATTENDEES[i] = EM; // pure email address
-          // in case we have no specific common name, use the one extracted from the email
-          // This catches the vCalendar 1.0 case and eventually ill-formed iCalendar 2.0 as well
-          if (ATTENDEE_CNS[i]==EMPTY)
-            ATTENDEE_CNS[i]=CN;
-          // default participation status to needs-action
-          if (ATTENDEE_PARTSTATS[i]==EMPTY)
-            ATTENDEE_PARTSTATS[i]=1; // 1=needs action
-          i=i+1;
-        }
-        // - shape organizer
-        PARSEEMAILSPEC(ORGANIZER, CN, EM);
-        ORGANIZER = EM; // pure email address
-        if (ORGANIZER_CN==EMPTY)
-          ORGANIZER_CN=CN;
-      }
-      else {
-        // VTODO
-        // - make sure we have at least a summary
-        if (SUMMARY==EMPTY) SUMMARY=DESCRIPTION; // use description if we don't have a summary
-        if (SUMMARY==EMPTY) SUMMARY="unnamed"; // set dummy summary if we still don't have one
-        // due shaping for non-iCalendar 2.0
-        if (ITEMDATATYPE()=="vCalendar10" && ALLDAYCOUNT(DUE,DUE,TRUE,TRUE)>0) {
-               DUE = DATEONLY(DUE);
-        }
-        if (ITEMDATATYPE()=="vCalendar10") {
-          $VCALENDAR_10TO20_PRIORITY_CONVERSION;
-        }
-      }
-      // a workaround for funambol: adding 'action' for 'alarm'
-      if (ITEMDATATYPE()=="iCalendar20") {
-        if (ALARM_TIME!=EMPTY && ALARM_ACTION==EMPTY) {
-            ALARM_ACTION = "DISPLAY";
-        }
-      }
-    ]]></macro>
-
-    <macro name="VCALENDAR_OUTGOING_SCRIPT"><![CDATA[
-      // set UTC time of generation for iCalendar 2.0 DTSTAMP
-      DGENERATED = NOW();
-      // make sure we have all trailing and leading spaces eliminated
-      DESCRIPTION=NORMALIZED(DESCRIPTION);
-      SUMMARY=NORMALIZED(SUMMARY);
-      if (ISEVENT) {
-        // VEVENT
-        // - combine attendee email address and common name into single string for vCalendar 1.0
-        if (ITEMDATATYPE()=="vCalendar10") {
-          i=0;
-          while(i<SIZE(ATTENDEES)) {
-            ATTENDEES[i] = MAKEEMAILSPEC(ATTENDEE_CNS[i], ATTENDEES[i]);
-            i=i+1;
-          }
-          ORGANIZER = MAKEEMAILSPEC(ORGANIZER_CN, ORGANIZER);
-        }
-      }
-      else {
-        // VTODO 
-        // interal representation is iCalendar20
-        if (ITEMDATATYPE()=="vCalendar10") {
-          $VCALENDAR_20TO10_PRIORITY_CONVERSION;
-        }
-      }
-      // make sure we have at least a summary
-      if (SUMMARY==EMPTY) SUMMARY=SUBSTR(DESCRIPTION,0,32); // derive from description
-      if (SUMMARY==EMPTY) SUMMARY="unnamed"; // in case description is empty as well
-      // make sure that we have a DESCRIPTION
-      if (DESCRIPTION==EMPTY) DESCRIPTION=SUMMARY;
-      // do NOT send duration (some servers crash when doing so)
-      DURATION = UNASSIGNED;
-      // shape alarm
-      if (ALARM_TIME!=EMPTY) {
-        if (ITEMDATATYPE()=="iCalendar20") {
-          if (ALARM_ACTION==EMPTY) ALARM_ACTION = "AUDIO";
-        }
-        else {
-          if (ALARM_MSG==EMPTY) ALARM_MSG="alarm";
-        }
-      }
-    ]]></macro>
-
-  </scripting>
-
-
-  <datatypes>
-
-    <!-- list of internal fields representing vCard data -->
-    <fieldlist name="contacts">
-      <field name="REV" type="timestamp" compare="never" age="yes"/>
-
-      <!-- Name elements -->
-      <field name="N_LAST" type="string" compare="always"/>
-      <field name="N_FIRST" type="string" compare="always"/>
-      <field name="N_MIDDLE" type="string" compare="always"/>
-      <field name="N_PREFIX" type="string" compare="conflict"/>
-      <field name="N_SUFFIX" type="string" compare="conflict"/>
-      <field name="NICKNAME" type="string" compare="conflict"/>
-      <field name="TITLE" type="string" compare="conflict" merge="fillempty"/>
-
-      <field name="FN" type="string" compare="conflict" merge="fillempty"/>
-      <field name="FILE-AS" type="string" compare="conflict" merge="fillempty"/>
-
-      <field name="GENDER" type="string" compare="conflict" merge="fillempty"/>
-
-      <!-- categories and classification -->
-      <field name="CATEGORIES" array="yes" type="string" compare="conflict"/>
-
-      <!-- organisation -->
-      <field name="ORG_NAME" type="string" compare="slowsync" merge="fillempty"/>
-      <field name="ORG_DIVISION" type="string" compare="conflict" merge="fillempty"/>
-      <field name="ORG_OFFICE" type="string" compare="conflict" merge="fillempty"/>
-      <field name="ORG_TEAM" type="string" compare="conflict" merge="fillempty"/>
-      <field name="ROLE" type="string" compare="conflict" merge="fillempty"/>
-
-      <!-- birthday and anniversary (not necessarily the same) -->
-      <field name="BDAY" type="date" compare="conflict" merge="fillempty"/>
-      <field name="ANNIVERSARY" type="date" compare="conflict" merge="fillempty"/>
-
-      <!-- telephone numbers -->
-      <field name="TEL"         array="yes" type="telephone" compare="conflict"/>
-      <field name="TEL_FLAGS"   array="yes" type="integer"   compare="conflict"/> <!-- offset 0 -->
-      <field name="TEL_LABEL"   array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
-      <field name="TEL_ID"      array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
-      <field name="TEL_SLOT"    array="yes" type="integer"   compare="never"/>    <!-- offset 3 -->
-
-      <!-- emails -->
-      <field name="EMAIL"       array="yes" type="multiline" compare="conflict"/>
-      <field name="EMAIL_FLAGS" array="yes" type="integer"   compare="conflict"/> <!-- offset 0 -->
-      <field name="EMAIL_LABEL" array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
-      <field name="EMAIL_ID"    array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
-      <field name="EMAIL_SLOT"  array="yes" type="integer"   compare="never"/>    <!-- offset 3 -->
-
-      <!-- web addresses -->
-      <field name="WEB"         array="yes" type="string" compare="conflict"/>
-      <field name="WEB_FLAGS"   array="yes" type="integer"   compare="conflict"/> <!-- offset 0 -->
-      <field name="WEB_LABEL"   array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
-      <field name="WEB_ID"      array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
-
-      <!-- would be nicer to have as part of WEB, but parser/encoder does not support mapping
-           with more than one property per field -->
-      <field name="CALURI"      array="yes" type="string" compare="conflict"/>
-      <field name="FBURL"       array="yes" type="string" compare="conflict"/>
-      <field name="BLOGURL"     array="yes" type="string" compare="conflict"/>
-      <field name="VIDEOURL"    array="yes" type="string" compare="conflict"/>
-
-      <!-- related persons: should be turned into array, like WEB and CALURI/FBURL -->
-      <field name="MANAGER"    type="string" compare="conflict"/>
-      <field name="ASSISTANT"  type="string" compare="conflict"/>
-      <field name="SPOUSE"     type="string" compare="conflict"/>
-
-      <!-- does this person want HTML mails? Valid values are TRUE/FALSE; a "boolean"
-           type would be useful, maybe add that later. -->
-      <field name="WANTS_HTML"  type="string" compare="conflict"/>
-
-      <!-- chat handles: should be turned into one array, like WEB and CALURI/FBURL -->
-      <field name="AIM_HANDLE"        array="yes" type="string" compare="conflict"/>
-      <field name="AIM_SLOT"          array="yes" type="string" compare="conflict"/>
-      <field name="GADUGADU_HANDLE"        array="yes" type="string" compare="conflict"/>
-      <field name="GADUGADU_SLOT"          array="yes" type="string" compare="conflict"/>
-      <field name="GROUPWISE_HANDLE"        array="yes" type="string" compare="conflict"/>
-      <field name="GROUPWISE_SLOT"          array="yes" type="string" compare="conflict"/>
-      <field name="ICQ_HANDLE"        array="yes" type="string" compare="conflict"/>
-      <field name="ICQ_SLOT"          array="yes" type="string" compare="conflict"/>
-      <field name="JABBER_HANDLE"        array="yes" type="string" compare="conflict"/>
-      <field name="JABBER_SLOT"          array="yes" type="string" compare="conflict"/>
-      <field name="MSN_HANDLE"        array="yes" type="string" compare="conflict"/>
-      <field name="MSN_SLOT"          array="yes" type="string" compare="conflict"/>
-      <field name="YAHOO_HANDLE"        array="yes" type="string" compare="conflict"/>
-      <field name="YAHOO_SLOT"          array="yes" type="string" compare="conflict"/>
-      <field name="SKYPE_HANDLE"      array="yes" type="string" compare="conflict"/>
-      <field name="SKYPE_SLOT"        array="yes" type="string" compare="conflict"/>
-      <field name="SIP_HANDLE"        array="yes" type="string" compare="conflict"/>
-      <field name="SIP_SLOT"          array="yes" type="string" compare="conflict"/>
-
-      <!-- home address -->
-      <field name="ADR_STREET"        array="yes" type="multiline" compare="conflict"/>
-      <field name="ADR_ADDTL"         array="yes" type="multiline" compare="conflict"/>
-      <field name="ADR_STREET_FLAGS"  array="yes" type="integer"   compare="conflict"/> <!-- offset 0 (from ADR_STREET_FLAGS) -->
-      <field name="ADR_STREET_LABEL"  array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
-      <field name="ADR_STREET_ID"     array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
-      <field name="ADR_POBOX"         array="yes" type="multiline" compare="conflict"/>
-      <field name="ADR_CITY"          array="yes" type="multiline" compare="conflict"/>
-      <field name="ADR_REG"           array="yes" type="multiline" compare="conflict"/>
-      <field name="ADR_ZIP"           array="yes" type="multiline" compare="conflict"/>
-      <field name="ADR_COUNTRY"       array="yes" type="multiline" compare="conflict"/>
-
-      <!-- Note -->
-      <field name="NOTE" type="multiline" compare="conflict" merge="lines"/>
-
-      <!-- Photo -->
-      <field name="PHOTO" type="blob" compare="never" merge="fillempty"/>
-      <field name="PHOTO_TYPE" type="integer" compare="never" merge="fillempty"/>
-
-    </fieldlist>
-
-    <!-- vCard profile -->
-    <mimeprofile name="vCard" fieldlist="contacts">
-
-      <profile name="VCARD" nummandatory="0"> <!-- we allow records without "N" as Address book can store them -->
-        <property name="VERSION">
-          <value conversion="version"/>
-        </property>
-
-        <property onlyformode="standard" name="PRODID" mandatory="no">
-          <value conversion="prodid"/>
-        </property>
-
-        <property name="REV">
-          <value field="REV"/>
-        </property>
-
-        <property name="N" values="5" mandatory="yes"> <!-- Note: makes N parse and generate even if not in remote's CTCap -->
-          <value index="0" field="N_LAST"/>
-          <value index="1" field="N_FIRST"/>
-          <value index="2" field="N_MIDDLE"/>
-          <value index="3" field="N_PREFIX"/>
-          <value index="4" field="N_SUFFIX"/>
-        </property>
-
-        <property name="FN">
-          <value field="FN"/>
-        </property>
-
-        <property name="X-EVOLUTION-FILE-AS">
-          <value field="FILE-AS"/>
-        </property>
-
-        <property name="X-GENDER">
-          <value field="GENDER"/>
-        </property>
-
-        <!-- onlyformode="standard": not part of vCard 2.1, but some
-             peers (like the Funambol server) accept it anyway in
-             vCard 2.1 -->
-        <property name="NICKNAME">
-          <value field="NICKNAME"/>
-        </property>
-
-        <property name="TITLE">
-          <value field="TITLE"/>
-        </property>
-
-        <property name="CATEGORIES" values="list" valueseparator="," altvalueseparator=";" > <!-- non-standard, but 1:1 as in vCard 3.0 (NOT like in vCalendar 1.0, where separator is ";") -->
-          <value field="CATEGORIES"/>
-          <position field="CATEGORIES" repeat="array" increment="1" minshow="0"/>
-        </property>
-
-        <property name="ORG" values="4">
-          <value index="0" field="ORG_NAME"/>
-          <value index="1" field="ORG_DIVISION"/>
-          <value index="2" field="ORG_OFFICE"/>
-          <value index="3" field="ORG_TEAM"/>
-        </property>
-
-        <property name="ROLE">
-          <value field="ROLE"/>
-        </property>
-
-        <property name="TEL">
-          <value field="TEL"/>
-          <position field="TEL" repeat="array" increment="1" minshow="1"/>
-          <parameter name="TYPE" default="yes" positional="no" show="yes">
-            <value field="TEL_FLAGS" conversion="multimix" combine=",">
-              <enum name="HOME"     value="B0"/>
-              <enum name="WORK"     value="B1"/>
-              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
-              <enum name="VOICE"    value="B3"/>
-              <enum name="CELL"     value="B4"/>
-              <enum name="FAX"      value="B5"/>
-              <enum name="PAGER"    value="B6"/>
-              <enum name="PREF"     value="B7"/>
-              <enum name="CAR"      value="B8"/>
-              <enum name="X-EVOLUTION-CALLBACK" value="B9"/>
-              <enum name="X-EVOLUTION-RADIO" value="B10"/>
-              <enum name="X-EVOLUTION-TELEX" value="B11"/>
-              <enum name="X-EVOLUTION-TTYTDD" value="B12"/>
-
-              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
-              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
-            </value>
-          </parameter>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="TEL_SLOT"/>
-          </parameter>
-        </property>
-
-        <property name="EMAIL">
-          <value field="EMAIL"/>
-          <position field="EMAIL" repeat="array" increment="1" minshow="1"/>
-          <parameter name="TYPE" default="yes" positional="no" show="yes">
-            <value field="EMAIL_FLAGS" conversion="multimix" combine=",">
-              <enum name="HOME"     value="B0"/>
-              <enum name="WORK"     value="B1"/>
-              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
-              <enum name="INTERNET" value="B3"/>
-
-              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
-              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
-            </value>
-          </parameter>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="EMAIL_SLOT"/>
-          </parameter>
-        </property>
-
-        <property name="URL">
-          <value field="WEB"/>
-          <position field="WEB" repeat="array" increment="1" minshow="1"/>
-          <parameter name="TYPE" default="yes" positional="no" show="yes">
-            <value field="WEB_FLAGS" conversion="multimix" combine=",">
-              <enum name="HOME"     value="B0"/>
-              <enum name="WORK"     value="B1"/>
-              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
-              <enum name="PREF"     value="B3"/>
-
-              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
-              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
-            </value>
-          </parameter>
-        </property>
-
-        <property name="CALURI" suppressempty="yes">
-          <value field="CALURI" show="yes"/>
-        </property>
-        <property name="FBURL" suppressempty="yes">
-          <value field="FBURL" show="yes"/>
-        </property>
-        <property name="X-EVOLUTION-BLOG-URL" suppressempty="yes">
-          <value field="BLOGURL" show="yes"/>
-        </property>
-        <property name="X-EVOLUTION-VIDEO-URL" suppressempty="yes">
-          <value field="VIDEOURL" show="yes"/>
-        </property>
-
-        <!-- item for SyncML server: EVOLUTION rule not active,
-             both X-EVOLUTION-MANAGER and X-MANAGER are sent.
-
-             item from SyncML server: EVOLUTION rule not active,
-             both X-EVOLUTION-MANAGER and X-MANAGER are checked,
-             but X-EVOLUTION-MANAGER later so that it overwrites
-             a value set earlier by X-MANAGER (if any). This is
-             a more or less arbitrary priority, chosen because
-             servers that know about SyncEvolution (ScheduleWorld,
-             Memotoo) use the X-EVOLUTION variant.
-
-             item to/from Evolution: EVOLUTION rule is active,
-             only X-EVOLUTION-MANAGER is used. -->
-        <property name="X-EVOLUTION-MANAGER" suppressempty="yes" delayedparsing="1">
-          <value field="MANAGER" show="yes"/>
-        </property>
-        <property name="X-MANAGER" suppressempty="yes" rule="EVOLUTION"/> <!-- disables the X-MANAGER for EVOLUTION -->
-        <property name="X-MANAGER" suppressempty="yes" rule="other">
-          <value field="MANAGER" show="yes"/>
-        </property>
-
-        <property name="X-EVOLUTION-ASSISTANT" suppressempty="yes" delayedparsing="1">
-          <value field="ASSISTANT" show="yes"/>
-        </property>
-        <property name="X-ASSISTANT" suppressempty="yes" rule="EVOLUTION"/>
-        <property name="X-ASSISTANT" suppressempty="yes" rule="other">
-          <value field="ASSISTANT" show="yes"/>
-        </property>
-
-        <property name="X-EVOLUTION-SPOUSE" suppressempty="yes" delayedparsing="1">
-          <value field="SPOUSE" show="yes"/>
-        </property>
-        <property name="X-SPOUSE" suppressempty="yes" rule="EVOLUTION"/>
-        <property name="X-SPOUSE" suppressempty="yes" rule="other">
-          <value field="SPOUSE" show="yes"/>
-        </property>
-
-        <property name="X-EVOLUTION-ANNIVERSARY" suppressempty="yes" delayedparsing="1">
-          <value field="ANNIVERSARY" show="yes"/>
-        </property>
-        <property name="X-ANNIVERSARY" suppressempty="yes" rule="EVOLUTION"/>
-        <property name="X-ANNIVERSARY" suppressempty="yes" rule="other">
-          <value field="ANNIVERSARY" show="yes"/>
-        </property>
-
-        <property name="X-MOZILLA-HTML">
-          <value field="WANTS_HTML" show="yes"/>
-        </property>
-
-        <property name="X-AIM" suppressempty="yes">
-          <value field="AIM_HANDLE"/>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="AIM_SLOT"/>
-          </parameter>
-        </property>
-        <property name="X-GADUGADU" suppressempty="yes">
-          <value field="GADUGADU_HANDLE"/>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="GADUGADU_SLOT"/>
-          </parameter>
-        </property>
-        <property name="X-GROUPWISE" suppressempty="yes">
-          <value field="GROUPWISE_HANDLE"/>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="GROUPWISE_SLOT"/>
-          </parameter>
-        </property>
-        <property name="X-ICQ" suppressempty="yes">
-          <value field="ICQ_HANDLE"/>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="ICQ_SLOT"/>
-          </parameter>
-        </property>
-        <property name="X-JABBER" suppressempty="yes">
-          <value field="JABBER_HANDLE"/>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="JABBER_SLOT"/>
-          </parameter>
-        </property>
-        <property name="X-MSN" suppressempty="yes">
-          <value field="MSN_HANDLE"/>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="MSN_SLOT"/>
-          </parameter>
-        </property>
-        <property name="X-YAHOO" suppressempty="yes">
-          <value field="YAHOO_HANDLE"/>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="YAHOO_SLOT"/>
-          </parameter>
-        </property>
-
-        <property name="X-SKYPE" suppressempty="yes"> 
-          <value field="SKYPE_HANDLE"/>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="SKYPE_SLOT"/>
-          </parameter>
-        </property>
-
-        <property name="X-SIP" suppressempty="yes">
-          <value field="SIP_HANDLE"/>
-          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
-            <value field="SIP_SLOT"/>
-          </parameter>
-        </property>
-
-        <property name="ADR" values="7">
-          <value index="0" field="ADR_POBOX"/>
-          <value index="1" field="ADR_ADDTL"/>
-          <value index="2" field="ADR_STREET"/>
-          <value index="3" field="ADR_CITY"/>
-          <value index="4" field="ADR_REG"/>
-          <value index="5" field="ADR_ZIP"/>
-          <value index="6" field="ADR_COUNTRY"/>
-          <position field="ADR_POBOX" repeat="array" increment="1" minshow="1"/>
-          <parameter name="TYPE" default="yes" positional="no" show="yes">
-            <value field="ADR_STREET_FLAGS" conversion="multimix" combine=",">
-              <enum name="HOME"     value="B0"/>
-              <enum name="WORK"     value="B1"/>
-              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
-
-              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
-              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
-            </value>
-          </parameter>
-        </property>
-
-        <property name="BDAY">
-          <value field="BDAY"/>
-        </property>
-
-        <property name="NOTE" filter="no">
-          <value field="NOTE"/>
-        </property>
-
-        <property name="PHOTO" filter="no">
-          <value field="PHOTO" conversion="BLOB_B64"/>
-          <parameter name="TYPE" default="no" show="yes">
-            <value field="PHOTO_TYPE">
-              <enum name="JPEG" value="0"/>
-            </value>
-          </parameter>
-        </property>
-
-      </profile>
-    </mimeprofile>
-
-    <!-- vCard 2.1 datatype, using vCard profile defined above -->
-    <datatype name="vCard21" basetype="vcard">
-      <version>2.1</version>
-      <use mimeprofile="vCard"/>
-     <incomingscript><![CDATA[
-        $VCARD_INCOMING_NAMECHANGE_SCRIPT
-      ]]></incomingscript>
-    </datatype>
-
-    <!-- vCard 3.0 datatype, using vCard profile defined above -->
-    <datatype name="vCard30" basetype="vcard">
-      <version>3.0</version>
-      <use mimeprofile="vCard"/>
-      <incomingscript><![CDATA[
-        $VCARD_INCOMING_NAMECHANGE_SCRIPT
-      ]]></incomingscript>
-    </datatype>
-
-
-    <!-- common field list for events and todos (both represented by vCalendar/iCalendar) -->
-    <fieldlist name="calendar">
-      <field name="ISEVENT" type="integer" compare="always"/>
-
-      <field name="DMODIFIED" type="timestamp" compare="never" age="yes"/>
-      <field name="DCREATED" type="timestamp" compare="never"/>
-
-      <field name="DGENERATED" type="timestamp" compare="never"/>
-
-      <field name="UID" type="string" compare="never"/>
-
-      <field name="CATEGORIES" array="yes" type="string" compare="conflict" merge="fillempty"/>
-      <field name="CLASS" type="integer" compare="conflict" merge="fillempty"/>
-      <field name="TRANSP" type="integer" compare="conflict" merge="fillempty"/>
-
-      <field name="SUMMARY" type="multiline" compare="always"/>
-      <field name="DESCRIPTION" type="multiline" compare="slowsync" merge="lines"/>
-      <field name="LOCATION" type="multiline" compare="slowsync" merge="lines"/>
-      <field name="URL" type="url" compare="conflict"/>
-
-      <!-- recurrence rule block, fields must be in that order, including
-           DTSTART as last field !! -->
-      <field name="RR_FREQ" type="string" compare="conflict"/>
-      <field name="RR_INTERVAL" type="integer" compare="conflict"/>
-      <field name="RR_FMASK" type="integer" compare="conflict"/>
-      <field name="RR_LMASK" type="integer" compare="conflict"/>
-      <field name="RR_END" type="timestamp" compare="conflict"/>
-
-      <!-- Note: DTSTART/DTEND are compared in the <comparescript>,
-                 therefore compare is set no "never" here -->
-      <field name="DTSTART" type="timestamp" compare="never"/>
-      <field name="DTEND" type="timestamp" compare="never"/>
-      <field name="DURATION" type="timestamp" compare="never"/>
-      <field name="COMPLETED" type="timestamp" compare="never"/>
-      <field name="DUE" type="timestamp" compare="never"/>
-
-      <field name="GEO_LAT" type="string" compare="never"/>
-      <field name="GEO_LONG" type="string" compare="never"/>
-
-      <field name="PRIORITY" type="integer" compare="conflict"/>
-      <field name="STATUS" type="integer" compare="conflict" merge="fillempty"/>
-      <field name="PERCENT_COMPLETE" type="integer" compare="conflict"/>
-
-      <field name="ALARM_TIME" type="timestamp" compare="conflict"/>
-      <field name="ALARM_SNOOZE" type="string" compare="conflict"/>
-      <field name="ALARM_REPEAT" type="string" compare="conflict"/>
-      <field name="ALARM_MSG" type="string" compare="conflict"/>
-      <field name="ALARM_ACTION" type="string" compare="conflict"/>
-      <field name="ALARM_REL" type="integer" compare="never"/>
-      <field name="ALARM_UID" type="string" compare="conflict"/>
-
-      <!-- non-standard -->
-      <field name="PARENT_UID" type="string" compare="never"/>
-
-      <!-- for events -->
-      <field name="EXDATES" array="yes" type="timestamp" compare="never"/>
-
-      <field name="ORIGSTART" array="no" type="timestamp" compare="never"/>
-      <field name="SEQNO" array="no" type="integer" compare="never"/>
-
-      <field name="ATTENDEES" array="yes" type="string" compare="never"/>
-      <field name="ATTENDEE_CNS" array="yes" type="string" compare="never"/>
-      <field name="ATTENDEE_PARTSTATS" array="yes" type="integer" compare="never"/>
-      <field name="ATTENDEE_ROLE" array="yes" type="integer" compare="never"/>
-      <field name="ATTENDEE_RSVP" array="yes" type="integer" compare="never"/>
-      <field name="ATTENDEE_LANG" array="yes" type="string" compare="never"/>
-      <field name="ATTENDEE_CUTYPE" array="yes" type="integer" compare="never"/>
-      <field name="ORGANIZER" array="no" type="string" compare="never"/>
-      <field name="ORGANIZER_CN" array="no" type="string" compare="never"/>
-
-    </fieldlist>
-
-
-    <!-- vCalendar with VTODO and VEVENT variants -->
-    <mimeprofile name="vCalendar" fieldlist="calendar">
-
-      <vtimezonegenmode>current</vtimezonegenmode>
-      <tzidgenmode>olson</tzidgenmode>
-
-      <profile name="VCALENDAR" nummandatory="1">
-
-        <property name="VERSION" mandatory="yes">
-          <value conversion="version"/>
-        </property>
-
-        <property onlyformode="standard" name="PRODID" mandatory="no">
-          <value conversion="prodid"/>
-        </property>
-
-        <property onlyformode="old" name="TZ" filter="false" suppressempty="yes">
-          <value field="DTSTART" conversion="tz"/>
-        </property>
-
-        <property onlyformode="old" name="DAYLIGHT" mode="daylight" filter="false" suppressempty="yes">
-          <value field="DTSTART" conversion="daylight"/>
-        </property>
-
-        <property name="GEO" values="2" suppressempty="yes" onlyformode="old" valueseparator=",">
-          <!-- LON,LAT in vCalendar 1.0 -->
-          <value index="0" field="GEO_LAT"/>
-          <value index="1" field="GEO_LONG"/>
-        </property>
-
-        <subprofile onlyformode="standard" name="VTIMEZONE" mode="vtimezones"/>
-
-        <!-- sub-profile for todoz -->
-        <subprofile name="VTODO" nummandatory="1" showifselectedonly="yes" field="ISEVENT" value="0">
-
-          <property name="LAST-MODIFIED" suppressempty="yes">
-            <value field="DMODIFIED"/>
-          </property>
-
-          <property name="DTSTAMP" suppressempty="yes" onlyformode="standard">
-            <value field="DGENERATED"/>
-          </property>
-
-          <property name="DCREATED" suppressempty="yes" onlyformode="old">
-            <value field="DCREATED"/>
-          </property>
-          <property name="CREATED" suppressempty="yes" onlyformode="standard">
-            <value field="DCREATED"/>
-          </property>
-
-          <property name="UID" suppressempty="yes">
-            <value field="UID"/>
-          </property>
-
-          <property name="SEQUENCE" suppressempty="yes">
-            <value field="SEQNO"/>
-          </property>
-
-          <property name="GEO" values="2" suppressempty="yes" onlyformode="standard" valueseparator=";">
-            <!-- LAT;LON in iCalendar 2.0 -->
-            <value index="0" field="GEO_LONG"/>
-            <value index="1" field="GEO_LAT"/>
-          </property>
-
-          <property onlyformode="standard" name="CATEGORIES" values="list" valueseparator="," suppressempty="yes">
-            <value field="CATEGORIES" />
-            <position field="CATEGORIES" repeat="array" minshow="0"/>
-          </property>
-
-          <property onlyformode="old" name="CATEGORIES" values="list" valueseparator=";" altvalueseparator="," suppressempty="yes">
-            <value field="CATEGORIES" />
-            <position field="CATEGORIES" repeat="array" minshow="0"/>
-          </property>
-
-          <property name="CLASS" suppressempty="yes">
-            <value field="CLASS">
-              <enum name="PUBLIC"       value="0"/>
-              <enum name="PRIVATE"      value="1"/>
-              <enum name="CONFIDENTIAL" value="2"/>
-            </value>
-          </property>
-
-          <property name="SUMMARY" mandatory="yes">
-            <value field="SUMMARY"/>
-          </property>
-
-          <!-- DESCRIPTION is an optional property and libical does not like
-               empty properties, so suppress it here. However, in the scripts
-               we ensure that the DESCRIPTION field should never be empty. -->
-          <property name="DESCRIPTION" suppressempty="yes" mandatory="no">
-            <value field="DESCRIPTION"/>
-          </property>
-
-          <property name="LOCATION" suppressempty="yes" mandatory="no">
-            <value field="LOCATION"/>
-          </property>
-
-          <property name="URL" suppressempty="yes" mandatory="no">
-            <value field="URL"/>
-          </property>
-
-          <property name="DTSTART" suppressempty="yes" delayedparsing="1">
-            <value field="DTSTART" conversion="autodate"/>
-            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
-              <value field="DTSTART" conversion="TZID"/>
-            </parameter>
-            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
-              <value field="DTSTART" conversion="VALUETYPE"/>
-            </parameter>
-          </property>
-
-          <property name="COMPLETED" suppressempty="yes" delayedparsing="1">
-            <value field="COMPLETED" conversion="autoenddate"/>
-            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
-              <value field="COMPLETED" conversion="TZID"/>
-            </parameter>
-            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
-              <value field="COMPLETED" conversion="VALUETYPE"/>
-            </parameter>
-          </property>
-
-          <property name="DUE" suppressempty="yes" delayedparsing="1">
-            <value field="DUE" conversion="autodate"/>
-            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
-              <value field="DUE" conversion="TZID"/>
-            </parameter>
-            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
-              <value field="DUE" conversion="VALUETYPE"/>
-            </parameter>
-          </property>
-
-          <property name="PRIORITY" suppressempty="yes">
-            <value field="PRIORITY"/>
-          </property>
-
-          <property name="STATUS" onlyformode="standard" suppressempty="yes">
-            <value field="STATUS" conversion="emptyonly">
-              <enum name="COMPLETED"      value="0"/>
-              <enum name="NEEDS-ACTION"   value="1"/>
-              <enum name="IN-PROCESS"     value="2"/>
-              <enum name="CANCELLED"      value="3"/>
-              <enum name="ACCEPTED"       value="4"/>
-              <enum name="TENTATIVE"      value="5"/>
-              <enum name="DELEGATED"      value="6"/>
-              <enum name="DECLINED"       value="7"/>
-              <enum name="SENT"           value="8"/>
-              <enum name="CONFIRMED"      value="9"/>
-              <enum name="DRAFT"          value="10"/>
-              <enum name="FINAL"          value="11"/>
-            </value>
-          </property>
-
-          <property name="STATUS" onlyformode="old" suppressempty="yes">
-            <value field="STATUS" conversion="emptyonly">
-              <enum name="COMPLETED"      value="0"/>
-              <enum name="NEEDS ACTION"   value="1"/>
-              <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
-              <enum name="IN PROCESS"     value="2"/>
-              <enum name="CANCELLED"      value="3"/>
-              <enum name="ACCEPTED"       value="4"/>
-              <enum name="TENTATIVE"      value="5"/>
-              <enum name="DELEGATED"      value="6"/>
-              <enum name="DECLINED"       value="7"/>
-              <enum name="SENT"           value="8"/>
-              <enum name="CONFIRMED"      value="9"/>
-              <enum name="DRAFT"          value="10"/>
-              <enum name="FINAL"          value="11"/>
-            </value>
-          </property>
-
-          <property name="PERCENT-COMPLETE" onlyformode="standard" suppressempty="yes">
-            <value field="PERCENT_COMPLETE"/>
-          </property>          
-
-          <!-- AALARM and DALARM both use the same fields -->
-          <property name="AALARM" onlyformode="old" values="4" suppressempty="yes">
-            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
-            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
-            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
-            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
-          </property>
-          <property name="DALARM" onlyformode="old" values="4" suppressempty="yes">
-            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
-            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
-            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
-            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
-          </property>
-
-          <subprofile onlyformode="standard" name="VALARM" nummandatory="1" field="ALARM_TIME">
-            <property name="TRIGGER" suppressempty="no" mandatory="yes">
-              <value field="ALARM_TIME"/>
-              <parameter name="VALUE" default="no" show="yes">
-                <value field="ALARM_TIME" conversion="FULLVALUETYPE"/>
-              </parameter>
-              <parameter name="RELATED" default="no" show="yes">
-                <value field="ALARM_REL">
-                  <enum mode="ignore" value="0"/>
-                  <enum name="START" value="1"/>
-                  <enum name="END"   value="2"/>
-                </value>
-              </parameter>
-            </property>
-            <property name="ACTION" suppressempty="yes" mandatory="yes">
-              <value field="ALARM_ACTION"/>
-            </property>
-            <property name="DESCRIPTION" suppressempty="yes">
-              <value field="ALARM_MSG"/>
-            </property>
-            <property name="REPEAT" suppressempty="yes">
-              <value field="ALARM_REPEAT"/>
-            </property>
-            <property name="X-EVOLUTION-ALARM-UID" suppressempty="yes">
-              <value field="ALARM_UID"/>
-            </property>
-          </subprofile>
-
-          <property onlyformode="old" name="RELATED-TO" suppressempty="yes">
-            <value field="PARENT_UID"/>
-          </property>
-
-          <property onlyformode="standard" name="RELATED-TO" suppressempty="yes">
-            <value field="PARENT_UID"/>
-            <parameter onlyformode="standard" name="RELTYPE" default="no" positional="yes" show="yes">
-              <value>
-                <enum name="PARENT"/>
-                <enum mode="defaultvalue" name="other"/>
-              </value>
-              <position hasnot="other" shows="PARENT" field="PARENT_UID"/>
-            </parameter>
-          </property>
-
-        </subprofile>
-
-        <!-- sub-profile for event -->
-        <subprofile name="VEVENT" nummandatory="1" showifselectedonly="yes" field="ISEVENT" value="1">
-
-            <property name="STATUS"  suppressempty="yes" onlyformode="old">
-                <value field="STATUS" conversion="emptyonly">
-                    <enum name="COMPLETED"      value="0"/>
-                    <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
-                    <enum name="NEEDS ACTION"   value="1"/>
-                    <enum name="CANCELLED"      value="3"/>
-                    <enum name="ACCEPTED"       value="4"/>
-                    <enum name="TENTATIVE"      value="5"/>
-                    <enum name="DELEGATED"      value="6"/>
-                    <enum name="DECLINED"       value="7"/>
-                    <enum name="SENT"           value="8"/>
-                    <enum name="CONFIRMED"      value="9"/>
-                    <enum name="FINAL"          value="11"/>
-                </value>
-            </property>
-
-            <property name="STATUS" suppressempty="yes" onlyformode="standard">
-                <value field="STATUS" conversion="emptyonly">
-                    <enum name="CANCELLED" value="3"/>
-                    <enum name="TENTATIVE" value="5"/>
-                    <enum name="CONFIRMED" value="9"/>
-                </value>
-            </property>
-
-          <property name="LAST-MODIFIED" suppressempty="yes">
-            <value field="DMODIFIED"/>
-          </property>
-
-          <property name="DTSTAMP" suppressempty="yes" onlyformode="standard">
-            <value field="DGENERATED"/>
-          </property>
-
-          <property name="DCREATED" suppressempty="yes" onlyformode="old">
-            <value field="DCREATED"/>
-          </property>
-          <property name="CREATED" suppressempty="yes" onlyformode="standard">
-            <value field="DCREATED"/>
-          </property>
-
-
-          <property name="UID" suppressempty="yes">
-            <value field="UID"/>
-          </property>
-
-          <property name="SEQUENCE" suppressempty="yes">
-            <value field="SEQNO"/>
-          </property>
-
-          <property name="GEO" values="2" suppressempty="yes" onlyformode="standard" valueseparator=";">
-            <!-- LAT;LON in iCalendar 2.0 -->
-            <value index="0" field="GEO_LONG"/>
-            <value index="1" field="GEO_LAT"/>
-          </property>
-
-          <property onlyformode="standard" name="CATEGORIES" values="list" valueseparator="," suppressempty="yes">
-            <value field="CATEGORIES" />
-            <position field="CATEGORIES" repeat="array" minshow="0"/>
-          </property>
-
-          <property onlyformode="old" name="CATEGORIES" values="list" valueseparator=";" altvalueseparator="," suppressempty="yes">
-            <value field="CATEGORIES" />
-            <position field="CATEGORIES" repeat="array" minshow="0"/>
-          </property>
-
-          <property name="CLASS" suppressempty="yes">
-            <value field="CLASS">
-              <enum name="PUBLIC"       value="0"/>
-              <enum name="PRIVATE"      value="1"/>
-              <enum name="CONFIDENTIAL" value="2"/>
-            </value>
-          </property>
-
-
-          <property name="TRANSP" suppressempty="yes" onlyformode="standard">
-            <value field="TRANSP">
-              <enum name="OPAQUE"       value="0"/>
-              <enum name="TRANSPARENT"  value="1"/>
-              <enum name="TENTATIVE"     value="2"/> <!-- according to Oracle de facto usage in vCalendar 1.0 -->
-              <enum name="OUT_OF_OFFICE" value="3"/> <!-- according to Oracle de facto usage in vCalendar 1.0 -->
-              <enum mode="defaultvalue" value="0"/>
-            </value>
-          </property>
-          <property name="TRANSP" suppressempty="yes" onlyformode="old">
-            <value field="TRANSP"/> <!-- directly numeric in vCalendar 1.0 -->
-          </property>
-
-
-          <property name="PRIORITY" suppressempty="yes">
-            <value field="PRIORITY"/>
-          </property>
-
-          <property name="SUMMARY" mandatory="yes">
-            <value field="SUMMARY"/>
-          </property>
-
-          <!-- DESCRIPTION is an optional property and libical does not like
-               empty properties, so suppress it here. However, in the scripts
-               we ensure that the DESCRIPTION field should never be empty. -->
-          <property name="DESCRIPTION" suppressempty="yes" mandatory="no">
-            <value field="DESCRIPTION"/>
-          </property>
-
-          <property name="LOCATION" suppressempty="yes" mandatory="no">
-            <value field="LOCATION"/>
-          </property>
-
-          <property name="DTSTART" suppressempty="yes" delayedparsing="1">
-            <value field="DTSTART" conversion="autodate"/>
-            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
-              <value field="DTSTART" conversion="TZID"/>
-            </parameter>
-            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
-              <value field="DTSTART" conversion="VALUETYPE"/>
-            </parameter>
-          </property>
-
-          <!-- recurrence rule (with delayed parsing, as it is dependent on DTSTART) -->
-          <property name="RRULE" suppressempty="yes" delayedparsing="2">
-            <!-- Note: RR_FREQ is the beginning of a block of fields
-                 suitable for the "rrule" conversion mode -->
-            <value field="RR_FREQ" conversion="rrule"/>
-          </property>
-
-          <!-- Symbian uses this, so it might make the client work with symbian-prepared servers better -->
-          <property name="X-RECURRENCE-ID" suppressempty="yes" onlyformode="old">
-            <value field="ORIGSTART" conversion="autodate"/>
-          </property>
-
-          <property name="RECURRENCE-ID" suppressempty="yes" onlyformode="standard" delayedparsing="1">
-            <value field="ORIGSTART" conversion="autodate"/>
-            <parameter name="TZID" default="no" show="yes">
-              <value field="ORIGSTART" conversion="TZID"/>
-            </parameter>
-            <parameter name="VALUE" default="no" show="yes">
-              <value field="ORIGSTART" conversion="VALUETYPE"/>
-            </parameter>
-          </property>
-
-          <!-- ScheduleWorld has a problem (bugzilla.moblin.org #2226)
-               with the EXDATE:value1,value2 format (correct in iCalendar 2.0):
-               as a workaround, accept all valid formats plus ; but
-               generate separate properties with one value each. -->
-          <property name="EXDATE" values="expandedlist" suppressempty="yes" onlyformode="standard" delayedparsing="1" valueseparator="," altvalueseparator=";">
-            <value field="EXDATES"/>
-            <position field="EXDATES" repeat="array" increment="1" minshow="0"/>
-            <parameter name="TZID" default="no" show="yes">
-              <value field="EXDATES" conversion="TZID"/>
-            </parameter>
-          </property>
-
-          <property name="EXDATE" values="list" suppressempty="yes" onlyformode="old" delayedparsing="1" valueseparator=";" altvalueseparator=",">
-            <value field="EXDATES"/>
-            <position field="EXDATES" repeat="array" increment="1" minshow="0"/>
-          </property>
-
-
-          <property name="DTEND" suppressempty="yes" delayedparsing="1">
-            <value field="DTEND" conversion="autoenddate"/>
-            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
-              <value field="DTEND" conversion="TZID"/>
-            </parameter>
-            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
-              <value field="DTEND" conversion="VALUETYPE"/>
-            </parameter>
-          </property>
-
-          <property name="DURATION" suppressempty="yes" delayedparsing="1" onlyformode="standard">
-            <value field="DURATION"/>
-            <parameter onlyformode="standard" name="VALUE" default="no" show="no">
-              <value field="DURATION" conversion="VALUETYPE"/>
-            </parameter>
-          </property>
-
-          <property name="ATTENDEE" suppressempty="yes" onlyformode="old">
-            <value field="ATTENDEES"/>
-            <parameter name="ROLE" default="no" positional="yes" show="yes">
-              <value>
-                <enum name="ORGANIZER"/>
-              </value>
-              <position has="ORGANIZER" field="ORGANIZER" overwriteempty="yes"/>
-              <position hasnot="ORGANIZER" field="ATTENDEES" repeat="array" increment="1" overwriteempty="yes"/>
-            </parameter>
-            <parameter name="STATUS" default="no" show="yes">
-              <value field="ATTENDEE_PARTSTATS">
-                <enum name="NEEDS ACTION"   value="1"/>
-                <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
-                <enum name="ACCEPTED"       value="4"/>
-                <enum name="DECLINED"       value="7"/>
-                <enum name="TENTATIVE"      value="5"/>
-                <enum name="DELEGATED"      value="6"/>
-              </value>
-            </parameter>
-          </property>
-
-          <property name="ATTENDEE" suppressempty="yes" onlyformode="standard">
-            <value field="ATTENDEES" conversion="mailto"/>
-            <position field="ATTENDEES" repeat="array" increment="1" minshow="0"/>
-            <parameter name="CN" default="no" show="yes" shownonempty="yes">
-              <value field="ATTENDEE_CNS"/>
-            </parameter>
-            <parameter name="PARTSTAT" default="no" show="yes">
-              <value field="ATTENDEE_PARTSTATS">
-                <enum name="NEEDS-ACTION"   value="1"/>
-                <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
-                <enum name="ACCEPTED"       value="4"/>
-                <enum name="DECLINED"       value="7"/>
-                <enum name="TENTATIVE"      value="5"/>
-                <enum name="DELEGATED"      value="6"/>
-              </value>
-            </parameter>
-            <parameter name="ROLE" default="no" show="yes">
-              <value field="ATTENDEE_ROLE">
-                <enum name="CHAIR"            value="1"/>
-                <enum name="REQ-PARTICIPANT"  value="2"/>
-                <enum name="OPT-PARTICIPANT"  value="3"/>
-                <enum name="NON-PARTICIPANT"  value="4"/>
-              </value>
-            </parameter>
-            <parameter name="RSVP" default="no" show="yes">
-              <value field="ATTENDEE_RSVP">
-                <enum name="TRUE"             value="1"/>
-                <enum name="FALSE"            value="0"/>
-              </value>
-            </parameter>
-            <parameter name="LANGUAGE" show="yes">
-              <value field="ATTENDEE_LANG"/>
-            </parameter>
-            <parameter name="CUTYPE" default="no" show="yes">
-              <value field="ATTENDEE_CUTYPE">
-                <enum name="INDIVIDUAL"  value="1"/>
-                <enum name="GROUP"       value="2"/>
-                <enum name="RESOURCE"    value="3"/>
-                <enum name="ROOM"        value="4"/>
-                <enum name="UNKNOWN"     value="5"/>
-              </value>
-            </parameter>
-          </property>
-
-          <property name="ORGANIZER" suppressempty="yes" onlyformode="standard">
-            <value field="ORGANIZER" conversion="mailto"/>
-            <parameter name="CN" default="no" show="yes">
-              <value field="ORGANIZER_CN"/>
-            </parameter>
-          </property>
-
-
-          <!-- AALARM and DALARM both use the same fields -->
-          <property name="AALARM" onlyformode="old" values="4" suppressempty="yes">
-            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
-            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
-            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
-            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
-          </property>
-          <property name="DALARM" onlyformode="old" values="4" suppressempty="yes">
-            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
-            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
-            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
-            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
-          </property>
-
-          <subprofile onlyformode="standard" name="VALARM" nummandatory="1" field="ALARM_TIME">
-            <property name="TRIGGER" suppressempty="no" mandatory="yes">
-              <value field="ALARM_TIME"/>
-              <parameter name="VALUE" default="no" show="yes">
-                <value field="ALARM_TIME" conversion="FULLVALUETYPE"/>
-              </parameter>
-              <parameter name="RELATED" default="no" show="yes">
-                <value field="ALARM_REL">
-                  <enum mode="ignore" value="0"/>
-                  <enum name="START" value="1"/>
-                  <enum name="END"   value="2"/>
-                </value>
-              </parameter>
-            </property>
-            <property name="ACTION" suppressempty="yes" mandatory="yes">
-              <value field="ALARM_ACTION"/>
-            </property>
-            <property name="DESCRIPTION" suppressempty="yes">
-              <value field="ALARM_MSG"/>
-            </property>
-            <property name="REPEAT" suppressempty="yes">
-              <value field="ALARM_REPEAT"/>
-            </property>
-            <property name="X-EVOLUTION-ALARM-UID" suppressempty="yes">
-              <value field="ALARM_UID"/>
-            </property>
-          </subprofile>
-
-        </subprofile>
-        
-      </profile>
-    </mimeprofile>
-
-
-    <!-- vCalendar 1.0 datatype, using vCalendar profile defined above -->
-    <datatype name="vCalendar10" basetype="vcalendar">
-      <version>1.0</version>
-      <use mimeprofile="vCalendar"/>
-
-      <incomingscript><![CDATA[
-        $VCALENDAR_INCOMING_SCRIPT
-      ]]></incomingscript>
-      
-      <outgoingscript><![CDATA[
-        $VCALENDAR_OUTGOING_SCRIPT
-      ]]></outgoingscript>
-
-    </datatype>
-
-
-    <!-- iCalendar 2.0 datatype, using vCalendar profile defined above -->
-    <datatype name="iCalendar20" basetype="vcalendar">
-      <version>2.0</version>
-      <use mimeprofile="vCalendar"/>
-
-      <incomingscript><![CDATA[
-        $VCALENDAR_INCOMING_SCRIPT
-      ]]></incomingscript>
-
-
-      <outgoingscript><![CDATA[
-        $VCALENDAR_OUTGOING_SCRIPT
-      ]]></outgoingscript>
-
-    </datatype>
-
-
-    <!-- list of internal fields representing plain text note data -->
-    <fieldlist name="Note">
-      <field name="SYNCLVL" type="integer" compare="never"/>
-      <field name="SUBJECT" type="multiline" compare="always"/>
-      <field name="TEXT" type="multiline" compare="conflict" merge="lines"/>
-    </fieldlist>
-
-    <textprofile name="Note" fieldlist="Note">
-      <linemap field="SUBJECT">
-        <numlines>1</numlines>
-        <inheader>false</inheader>
-        <allowempty>true</allowempty>
-        <filterkeyword>SUBJECT</filterkeyword>
-      </linemap>
-      <linemap field="TEXT">
-        <numlines>0</numlines>
-        <inheader>false</inheader>
-        <allowempty>true</allowempty>
-      </linemap>
-    </textprofile>
-
-    <datatype name="note10" basetype="text">
-      <use profile="Note"/>
-      <typestring>text/plain</typestring>
-      <versionstring>1.0</versionstring>
-    </datatype>
-
-    <datatype name="note11" basetype="text">
-      <use profile="Note"/>
-      <typestring>text/plain</typestring>
-      <versionstring>1.1</versionstring>
-    </datatype>
-
-
-    <!-- list of internal fields representing vBookmark data -->
-    <fieldlist name="bookmarks">
-      <field name="REV" type="timestamp" compare="never" age="yes"/>
-      <field name="SYNCLVL" type="integer" compare="never"/>
-
-      <!-- Name -->
-      <field name="TITLE" type="string" compare="always"/>
-
-      <!-- categories and classification -->
-      <field name="CATEGORIES" type="string" compare="conflict" merge="fillempty"/>
-      <field name="CLASS" type="string" compare="conflict" merge="fillempty"/>
-
-      <!-- web addresses -->
-      <field name="URL" type="url" compare="slowsync" merge="fillempty"/>
-
-      <!-- Note -->
-      <field name="NOTE" type="multiline" compare="conflict" merge="lines"/>
-
-    </fieldlist>
-
-    <!-- vBookmark profile -->
-    <mimeprofile name="vBookmark" fieldlist="bookmarks">
-
-      <profile name="VBKM" nummandatory="0">
-        <property name="VERSION">
-          <value conversion="version"/>
-        </property>
-
-        <property name="X-LAST-MODIFIED">
-          <value field="REV"/>
-        </property>
-
-        <property name="TITLE">
-          <value field="TITLE"/>
-        </property>
-
-        <property name="URL">
-          <value field="URL"/>
-        </property>
-
-        <!-- non-standard properties -->
-
-        <property name="CATEGORIES">
-          <value field="CATEGORIES"/>
-        </property>
-
-        <property name="CLASS" suppressempty="yes">
-          <value field="CLASS"/>
-        </property>
-
-        <property name="NOTE" filter="no">
-          <value field="NOTE"/>
-        </property>
-
-      </profile>
-    </mimeprofile>
-
-    <!-- vBookmark datatype, using vBookmark profile defined above -->
-    <datatype name="vBookmark10" basetype="mimedir">
-      <typestring>text/x-vbookmark</typestring>
-      <versionstring>1.0</versionstring>
-      <use profile="vBookmark"/>
-    </datatype>
-
-    <fieldlists/>
-    <profiles/>
-    <datatypes/>
-  </datatypes>
-
-  <clientorserver/>
-
-  <client type="plugin">
-      <remoterule name="ZYB">
-          <manufacturer>ZYB</manufacturer>
-          <model>ZYB</model>
-          <!-- information to disable anchors checking -->
-          <lenientmode>yes</lenientmode>
-      </remoterule>
-  </client>
-
-</sysync_config>
index 7103e2e..291cbb5 100644 (file)
@@ -80,6 +80,8 @@ class Session;
 class Connection;
 class Client;
 class DBusTransportAgent;
+class DBusUserInterface;
+class DBusServer;
 
 class DBusSyncException : public DBusCXXException, public Exception
 {
@@ -182,7 +184,9 @@ class ReadOperations
 public:
     const std::string m_configName;
 
-    ReadOperations(const std::string &config_name);
+    DBusServer &m_server;
+
+    ReadOperations(const std::string &config_name, DBusServer &server);
 
     /** the double dictionary used to represent configurations */
     typedef std::map< std::string, StringMap > Config_t;
@@ -195,7 +199,7 @@ public:
     typedef SyncSource::Databases SourceDatabases_t;
 
     /** implementation of D-Bus GetConfigs() */
-    static void getConfigs(bool getTemplates, std::vector<std::string> &configNames);
+    void getConfigs(bool getTemplates, std::vector<std::string> &configNames);
 
     /** implementation of D-Bus GetConfig() for m_configName as server configuration */
     void getConfig(bool getTemplate,
@@ -221,7 +225,7 @@ private:
     virtual bool setFilters(SyncConfig &config) { return false; }
 
     /** utility method which constructs a SyncConfig which references a local configuration (never a template) */
-    boost::shared_ptr<SyncConfig> getLocalConfig(const std::string &configName);
+    boost::shared_ptr<DBusUserInterface> getLocalConfig(const std::string &configName);
 };
 
 /**
@@ -315,6 +319,161 @@ class AutoTerm {
 class InfoReq;
 
 /**
+ * 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.
+ */
+class BluezManager : public DBusRemoteObject {
+public:
+    BluezManager(DBusServer &server); 
+
+    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();}
+    bool isDone() { return m_done; }
+
+private:
+    class BluezDevice;
+
+    /**
+     * 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
+     */
+    class BluezAdapter: public DBusRemoteObject
+    {
+     public:
+        BluezAdapter (BluezManager &manager, const string &path); 
+
+        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";}
+        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);
+            }
+        }
+
+        std::vector<boost::shared_ptr<BluezDevice> >& getDevices() { return m_devices; }
+
+     private:
+        /** callback of 'ListDevices' signal. Used to get all available devices of the adapter */
+        void listDevicesCb(const std::vector<DBusObject_t> &devices, const string &error);
+
+        /** callback of 'DeviceRemoved' signal. Used to track a device is removed */
+        void deviceRemoved(const DBusObject_t &object);
+
+        /** callback of 'DeviceCreated' signal. Used to track a new device is created */
+        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;
+
+        /** represents 'DeviceRemoved' signal of org.bluez.Adapter*/
+        SignalWatch1<DBusObject_t> m_deviceRemoved;
+        /** represents 'DeviceAdded' signal of org.bluez.Adapter*/
+        SignalWatch1<DBusObject_t> m_deviceAdded;
+
+        friend class BluezDevice;
+    };
+
+    /**
+     * 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
+     */
+    class BluezDevice: public DBusRemoteObject
+    {
+     public:
+        typedef map<string, boost::variant<vector<string>, string > > PropDict;
+
+        BluezDevice (BluezAdapter &adapter, const string &path);
+
+        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";}
+        virtual DBusConnection *getConnection() const {return m_adapter.m_manager.getConnection();}
+        string getMac() { return m_mac; }
+
+        /**
+         * 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);
+
+     private:
+        /** callback of 'GetProperties' method. The properties of the device is gotten */
+        void getPropertiesCb(const PropDict &props, const string &error);
+
+        /** callback of 'PropertyChanged' signal. Changed property is tracked */
+        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;
+        /** represents 'PropertyChanged' signal of org.bluez.Device */
+        PropertySignal m_propertyChanged;
+
+        friend class BluezAdapter;
+    };
+
+    /*
+     * check whether the data is generated. If errors, force initilization done
+     */
+    void setDone(bool done) { m_done = done; }
+
+    /** callback of 'DefaultAdapter' method to get the default bluetooth adapter  */
+    void defaultAdapterCb(const DBusObject_t &adapter, const string &error);
+
+    /** callback of 'DefaultAdapterChanged' signal to track changes of the default adapter */
+    void defaultAdapterChanged(const DBusObject_t &adapter);
+
+    DBusServer &m_server;
+    DBusConnectionPtr m_bluezConn;
+    boost::shared_ptr<BluezAdapter> m_adapter;
+
+    /** represents 'DefaultAdapterChanged' signal of org.bluez.Adapter*/
+    boost::shared_ptr<SignalWatch1<DBusObject_t> > m_adapterChanged;
+
+    /** flag to indicate whether the calls are all returned */
+    bool m_done;
+};
+
+/**
  * Implements the main org.syncevolution.Server interface.
  *
  * All objects created by it get a reference to the creating
@@ -336,6 +495,9 @@ class DBusServer : public DBusObjectHelper
      */
     std::list<std::pair<Caller_t, int> > m_attachedClients;
 
+    /* Event source that regurally pool network manager
+     * */
+    GLibEvent m_pollConnman;
     /**
      * The session which currently holds the main lock on the server.
      * To avoid issues with concurrent modification of data or configs,
@@ -387,6 +549,17 @@ class DBusServer : public DBusObjectHelper
     //automatic termination
     AutoTerm m_autoTerm;
 
+    // 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;
+
+    BluezManager m_bluezManager;
+
+    /** devices which have sync services */
+    SyncConfig::DeviceList m_syncDevices;
+
     /**
      * Watch callback for a specific client or connection.
      */
@@ -425,7 +598,7 @@ class DBusServer : public DBusObjectHelper
                    bool getTemplate,
                    ReadOperations::Config_t &config)
     {
-        ReadOperations ops(config_name);
+        ReadOperations ops(config_name, *this);
         ops.getConfig(getTemplate , config);
     }
 
@@ -434,7 +607,7 @@ class DBusServer : public DBusObjectHelper
                     uint32_t start, uint32_t count,
                     ReadOperations::Reports_t &reports)
     {
-        ReadOperations ops(config_name);
+        ReadOperations ops(config_name, *this);
         ops.getReports(start, count, reports);
     }
 
@@ -442,7 +615,7 @@ class DBusServer : public DBusObjectHelper
     void checkSource(const std::string &configName,
                      const std::string &sourceName)
     {
-        ReadOperations ops(configName);
+        ReadOperations ops(configName, *this);
         ops.checkSource(sourceName);
     }
 
@@ -451,10 +624,17 @@ class DBusServer : public DBusObjectHelper
                       const string &sourceName,
                       ReadOperations::SourceDatabases_t &databases)
     {
-        ReadOperations ops(configName);
+        ReadOperations ops(configName, *this);
         ops.getDatabases(sourceName, databases);
     }
 
+    void getConfigs(bool getTemplates, 
+                    std::vector<std::string> &configNames)
+    {
+        ReadOperations ops("", *this);
+        ops.getConfigs(getTemplates, configNames);
+    }
+
     /** Server.CheckPresence() */
     void checkPresence(const std::string &server,
                        std::string &status,
@@ -495,6 +675,12 @@ class DBusServer : public DBusObjectHelper
                 const std::string &,
                 const std::string &> presence;
 
+    /**
+     * Server.TemplatesChanged, triggered each time m_syncDevices, the
+     * input for the templates, is changed
+     */
+    EmitSignal0 templatesChanged;
+
     /** Server.InfoRequest */
     EmitSignal6<const std::string &,
                 const DBusObject_t &,
@@ -503,6 +689,158 @@ class DBusServer : public DBusObjectHelper
                 const std::string &,
                 const std::map<string, string> &> infoRequest;
 
+    static gboolean connmanPoll (gpointer dbus_server);
+    DBusConnectionPtr m_connmanConn;
+
+    friend class Session;
+    class PresenceStatus {
+        bool m_httpPresence;
+        bool m_btPresence;
+        bool m_initiated;
+        DBusServer &m_server;
+       
+        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)
+        {
+            init();
+        }
+        
+        void 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) {
+                        m_peers[server.first].push_back(make_pair(url,MIGHTWORK));
+                    }
+                }
+                m_initiated = true;
+            }
+        }
+
+        /* Implement DBusServer::checkPresence*/
+        void 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 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;
+            }
+        }
+
+        void updatePresenceStatus (bool httpPresence, bool btPresence) {
+            bool httpChanged = (m_httpPresence != httpPresence);
+            bool btChanged = (m_btPresence != btPresence);
+            m_httpPresence = httpPresence;
+            m_btPresence = btPresence;
+
+            if (m_initiated && !httpChanged && !btChanged) {
+                //nothing changed
+                return;
+            }
+
+            //initialize the configured peer list
+            bool initiated = m_initiated;
+            if (!m_initiated) {
+                init();
+            }
+
+            //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.presence (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.presence (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());
+                    }
+                }
+            }
+        }
+    }m_presence;
+
 public:
     DBusServer(GMainLoop *loop, const DBusConnectionPtr &conn, int duration);
     ~DBusServer();
@@ -566,6 +904,35 @@ public:
 
     /** callback to reset for auto termination checking */
     void autoTermCallback() { m_autoTerm.reset(); }
+
+    /** poll_nm callback for connman, used for presence detection*/
+    void connmanCallback(const std::map <std::string, boost::variant <std::vector <std::string> > >& props, const string &error);
+
+    PresenceStatus& getPresenceStatus() {return m_presence;}
+
+    DBusConnectionPtr getConnmanConnection() {return m_connmanConn;}
+
+    void clearPeerTempls() { m_matchedTempls.clear(); }
+    void addPeerTempl(const string &templName, const boost::shared_ptr<SyncConfig::TemplateDescription> peerTempl);
+
+    boost::shared_ptr<SyncConfig::TemplateDescription> getPeerTempl(const string &peer);
+
+    /**
+     * 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);
 };
 
 
@@ -718,10 +1085,34 @@ template<> struct dbus_traits<SourceProgress> :
 {};
 
 /**
+ * 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);
+};
+
+/**
  * A running sync engine which keeps answering on D-Bus whenever
  * possible and updates the Session while the sync runs.
  */
-class DBusSync : public SyncContext
+class DBusSync : public DBusUserInterface 
 {
     Session &m_session;
 
@@ -752,15 +1143,12 @@ protected:
     virtual int sleep(int intervals);
 
     /**
-     * Implement askPassword and savePassword to retrieve
-     * or save password in DBusSync
+     * Implement askPassword to retrieve password in gnome-keyring.
+     * If not found, then ask it from dbus clients
      */
     string askPassword(const string &passwordName, 
                        const string &descr, 
                        const ConfigPasswordKey &key);
-    bool savePassword(const string &passwordName, 
-                      const string &password, 
-                      const ConfigPasswordKey &key);
 };
 
 /**
@@ -1081,6 +1469,9 @@ class Session : public DBusObjectHelper,
     /** Session.Restore() */
     void restore(const string &dir, bool before,const std::vector<std::string> &sources);
 
+    /** Session.checkPresence() */
+    void checkPresence (string &status);
+
     /**
      * Must be called each time that properties changing the
      * overall status are changed. Ensures that the corresponding
@@ -1490,18 +1881,43 @@ private:
 
 /***************** ReadOperations implementation ****************/
 
-ReadOperations::ReadOperations(const std::string &config_name) :
-    m_configName(config_name)
+ReadOperations::ReadOperations(const std::string &config_name, DBusServer &server) :
+    m_configName(config_name), m_server(server)
 {}
 
 void ReadOperations::getConfigs(bool getTemplates, std::vector<std::string> &configNames)
 {
     if (getTemplates) {
+        // get device list from dbus server, currently only bluetooth devices
         SyncConfig::DeviceList devices;
+        m_server.getDeviceList(devices);
+
+        //clear existing templates in dbus server
+        m_server.clearPeerTempls();
+
         SyncConfig::TemplateList list = SyncConfig::getPeerTemplates(devices);
-        BOOST_FOREACH(const boost::shared_ptr<SyncConfig::TemplateDescription> server, list) {
-            configNames.push_back(server->m_name);
-        //TODO create the template filters
+        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()) {
+                configNames.push_back(peer->m_name);
+            } else {
+                string templName = "Bluetooth_";
+                templName += peer->m_id;
+                templName += "_";
+                std::map<std::string, int>::iterator it = numbers.find(peer->m_id);
+                if(it == numbers.end()) {
+                    numbers.insert(std::make_pair(peer->m_id, 1));
+                    templName += "1";
+                } else {
+                    it->second++;
+                    stringstream seq;
+                    seq << it->second;
+                    templName += seq.str();
+                }
+                configNames.push_back(templName);
+                m_server.addPeerTempl(templName, peer);
+            }
         }
     } else {
         SyncConfig::ConfigList list = SyncConfig::getConfigs();
@@ -1511,13 +1927,13 @@ void ReadOperations::getConfigs(bool getTemplates, std::vector<std::string> &con
     }
 }
 
-boost::shared_ptr<SyncConfig> ReadOperations::getLocalConfig(const string &configName)
+boost::shared_ptr<DBusUserInterface> ReadOperations::getLocalConfig(const string &configName)
 {
     string peer, context;
     SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(configName),
                                   peer, context);
 
-    boost::shared_ptr<SyncConfig> syncConfig(new SyncConfig(configName));
+    boost::shared_ptr<DBusUserInterface> syncConfig(new DBusUserInterface(configName));
 
     /** if config was not set temporarily */
     if (!setFilters(*syncConfig)) {
@@ -1535,33 +1951,77 @@ void ReadOperations::getConfig(bool getTemplate,
                                Config_t &config)
 {
     map<string, string> localConfigs;
-    boost::shared_ptr<SyncConfig> syncConfig;
+    boost::shared_ptr<SyncConfig> dbusConfig;
+    boost::shared_ptr<DBusUserInterface> dbusUI;
+    SyncConfig *syncConfig;
+    string syncURL;
     /** get server template */
     if(getTemplate) {
         string peer, context;
-        SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(m_configName),
-                                      peer, context);
 
-        syncConfig = SyncConfig::createPeerTemplate(peer);
-        if(!syncConfig.get()) {
+        boost::shared_ptr<SyncConfig::TemplateDescription> peerTemplate =
+            m_server.getPeerTempl(m_configName);
+        if(peerTemplate) {
+            SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(peerTemplate->m_name),
+                    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));
+
+            // if the peer is client, then replace syncURL with bluetooth
+            // MAC address
+            syncURL = "obex-bt://";
+            syncURL += peerTemplate->m_id;
+        } else {
+            SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(m_configName),
+                    peer, context);
+            dbusConfig = SyncConfig::createPeerTemplate(peer);
+        }
+
+        if(!dbusConfig.get()) {
             SE_THROW_EXCEPTION(NoSuchConfig, "No template '" + m_configName + "' found");
         }
 
         // use the shared properties from the right context as filter
         // so that the returned template preserves existing properties
-        boost::shared_ptr<SyncConfig> shared = getLocalConfig(string("@") + context);
+        boost::shared_ptr<DBusUserInterface> shared = getLocalConfig(string("@") + context);
 
         ConfigProps props;
         shared->getProperties()->readProperties(props);
-        syncConfig->setConfigFilter(true, "", props);
+        dbusConfig->setConfigFilter(true, "", props);
         BOOST_FOREACH(std::string source, shared->getSyncSources()) {
             SyncSourceNodes nodes = shared->getSyncSourceNodes(source, "");
             props.clear();
             nodes.getProperties()->readProperties(props);
-            syncConfig->setConfigFilter(false, source, props);
+            dbusConfig->setConfigFilter(false, source, props);
         }
+        syncConfig = dbusConfig.get();
     } else {
-        syncConfig = getLocalConfig(m_configName);
+        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();
     }
 
     /** get sync properties and their values */
@@ -1569,7 +2029,9 @@ void ReadOperations::getConfig(bool getTemplate,
     BOOST_FOREACH(const ConfigProperty *prop, syncRegistry) {
         bool isDefault = false;
         string value = prop->getProperty(*syncConfig->getProperties(), &isDefault);
-        if(!isDefault) {
+        if(boost::iequals(prop->getName(), "syncURL") && !syncURL.empty() ) {
+            localConfigs.insert(pair<string, string>(prop->getName(), syncURL));
+        } else if(!isDefault) {
             localConfigs.insert(pair<string, string>(prop->getName(), value));
         }
     }
@@ -1655,7 +2117,7 @@ void ReadOperations::checkSource(const std::string &sourceName)
     try {
         // this can already throw exceptions when the config is invalid
         SyncSourceParams params(sourceName, config->getSyncSourceNodes(sourceName));
-        auto_ptr<SyncSource> syncSource(SyncSource::createSource(params, false));
+        auto_ptr<SyncSource> syncSource(SyncSource::createSource(params, false, config.get()));
 
         if (syncSource.get()) {
             syncSource->open();
@@ -1693,11 +2155,96 @@ void ReadOperations::getDatabases(const string &sourceName, SourceDatabases_t &d
     SE_THROW_EXCEPTION(NoSuchSource, "'" + m_configName + "' has no '" + sourceName + "' source");
 }
 
+/***************** 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
+}
+
+
 /***************** DBusSync implementation **********************/
 
 DBusSync::DBusSync(const std::string &config,
                    Session &session) :
-    SyncContext(config, true),
+    DBusUserInterface(config),
     m_session(session)
 {
 }
@@ -1712,9 +2259,7 @@ boost::shared_ptr<TransportAgent> DBusSync::createTransportAgent()
     } else {
         // no connection, use HTTP via libsoup/GMainLoop
         GMainLoop *loop = m_session.getServer().getLoop();
-        g_main_loop_ref(loop);
-        boost::shared_ptr<HTTPTransportAgent> agent(new SoupTransportAgent(loop));
-        agent->setConfig(*this);
+        boost::shared_ptr<TransportAgent> agent = SyncContext::createTransportAgent(loop);
         return agent;
     }
 }
@@ -1775,86 +2320,16 @@ int DBusSync::sleep(int intervals)
     }
 }
 
-inline const char *passwdStr(const std::string &str)
-{
-    return str.empty() ? NULL : str.c_str();
-}
-
 string DBusSync::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;
+    string password = DBusUserInterface::askPassword(passwordName, descr, key);
 
-    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;
+    if(password.empty()) {
+        password = m_session.askPassword(passwordName, descr, key);
     }
-    //if not found, then ask user to interactively input password
-#endif
-    /** 
-     * if not built with gnome_keyring support, directly send password request 
-     * to dbus clients
-     */
-    return m_session.askPassword(passwordName, descr, key);
-}
-
-bool DBusSync::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
+    return password;
 }
 
 /***************** Session implementation ***********************/
@@ -1902,6 +2377,7 @@ void Session::setConfig(bool update, bool temporary,
         throw std::runtime_error("Clearing existing configuration and temporary configuration changes which only affects the duration of the session are mutually exclusive");
     }
 
+    m_server.getPresenceStatus().updateConfigPeers (m_configName, config);
     /** check whether we need remove the entire configuration */
     if(!update && config.empty()) {
         boost::shared_ptr<SyncConfig> syncConfig(new SyncConfig(getConfigName()));
@@ -2129,7 +2605,7 @@ Session::Session(DBusServer &server,
                      std::string("/org/syncevolution/Session/") + session,
                      "org.syncevolution.Session",
                      boost::bind(&DBusServer::autoTermCallback, &server)),
-    ReadOperations(config_name),
+    ReadOperations(config_name, server),
     m_server(server),
     m_sessionID(session),
     m_peerDeviceID(peerDeviceID),
@@ -2153,7 +2629,7 @@ Session::Session(DBusServer &server,
     emitProgress(*this, "ProgressChanged")
 {
     add(this, &Session::detach, "Detach");
-    add(&ReadOperations::getConfigs, "GetConfigs");
+    add(static_cast<ReadOperations *>(this), &ReadOperations::getConfigs, "GetConfigs");
     add(static_cast<ReadOperations *>(this), &ReadOperations::getConfig, "GetConfig");
     add(this, &Session::setConfig, "SetConfig");
     add(static_cast<ReadOperations *>(this), &ReadOperations::getReports, "GetReports");
@@ -2165,6 +2641,7 @@ Session::Session(DBusServer &server,
     add(this, &Session::getStatus, "GetStatus");
     add(this, &Session::getProgress, "GetProgress");
     add(this, &Session::restore, "Restore");
+    add(this, &Session::checkPresence, "checkPresence");
     add(emitStatus);
     add(emitProgress);
 }
@@ -2476,6 +2953,13 @@ string Session::askPassword(const string &passwordName,
     return "";
 }
 
+/*Implementation of Session.CheckPresence */
+void Session::checkPresence (string &status)
+{
+    vector<string> transport;
+    m_server.m_presence.checkPresence (m_configName, status, transport);
+}
+
 /************************ ProgressData implementation *****************/
 ProgressData::ProgressData(int32_t &progress) 
     : m_progress(progress),
@@ -2779,8 +3263,14 @@ void Connection::process(const Caller_t &caller,
                     BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server,
                             servers) {
                         SyncConfig conf(server.first);
-                        if (conf.getSyncURL() == serverID) {
-                            config = server.first;
+                        vector<string> urls = conf.getSyncURL();
+                        BOOST_FOREACH (const string &url, urls) {
+                            if (url == serverID) {
+                                config = server.first;
+                                break;
+                            }
+                        }
+                        if (!config.empty()) {
                             break;
                         }
                     }
@@ -2794,14 +3284,18 @@ void Connection::process(const Caller_t &caller,
                             BOOST_FOREACH(const SyncConfig::ConfigList::value_type &server,
                                     servers) {
                                 SyncConfig conf(server.first);
-                                string url = conf.getSyncURL();
-                                url = url.substr (0, url.find("+"));
-                                SE_LOG_DEBUG (NULL, NULL, "matching against %s",url.c_str());
-                                //TODO working with multiple SyncURLs
-                                if (url.find ("obex-bt://") ==0 && url.substr(strlen("obex-bt://"), url.npos) == btAddr) {
-                                    config = server.first;
+                                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());
+                                    if (url.find ("obex-bt://") ==0 && url.substr(strlen("obex-bt://"), url.npos) == btAddr) {
+                                        config = server.first;
+                                        break;
+                                    } 
+                                }
+                                if (!config.empty()){
                                     break;
-                                } 
+                                }
                             }
                         }
                     }
@@ -3398,7 +3892,7 @@ void DBusServer::checkPresence(const std::string &server,
                                std::string &status,
                                std::vector<std::string> &transports)
 {
-    // TODO: implement this, right now always return status = "" = available
+    return m_presence.checkPresence(server, status, transports);
 }
 
 void DBusServer::getSessions(std::vector<std::string> &sessions)
@@ -3425,9 +3919,12 @@ DBusServer::DBusServer(GMainLoop *loop, const DBusConnectionPtr &conn, int durat
     m_activeSession(NULL),
     m_lastInfoReq(0),
     m_autoTerm(duration),
+    m_bluezManager(*this),
     sessionChanged(*this, "SessionChanged"),
     presence(*this, "Presence"),
-    infoRequest(*this, "InfoRequest")
+    templatesChanged(*this, "TemplatesChanged"),
+    infoRequest(*this, "InfoRequest"),
+    m_presence(*this)
 {
     struct timeval tv;
     gettimeofday(&tv, NULL);
@@ -3436,7 +3933,7 @@ DBusServer::DBusServer(GMainLoop *loop, const DBusConnectionPtr &conn, int durat
     add(this, &DBusServer::detachClient, "Detach");
     add(this, &DBusServer::connect, "Connect");
     add(this, &DBusServer::startSession, "StartSession");
-    add(&ReadOperations::getConfigs, "GetConfigs");
+    add(this, &DBusServer::getConfigs, "GetConfigs");
     add(this, &DBusServer::getConfig, "GetConfig");
     add(this, &DBusServer::getReports, "GetReports");
     add(this, &DBusServer::checkSource, "CheckSource");
@@ -3445,8 +3942,16 @@ DBusServer::DBusServer(GMainLoop *loop, const DBusConnectionPtr &conn, int durat
     add(this, &DBusServer::getSessions, "GetSessions");
     add(this, &DBusServer::infoResponse, "InfoResponse");
     add(sessionChanged);
+    add(templatesChanged);
     add(presence);
     add(infoRequest);
+
+    const char *connmanTest = getenv ("DBUS_TEST_CONNMAN");
+    m_connmanConn = g_dbus_setup_bus (connmanTest ? DBUS_BUS_SESSION: DBUS_BUS_SYSTEM, NULL, true, NULL);
+    if (m_connmanConn) {
+        m_pollConnman =  g_timeout_add_seconds (10, connmanPoll, static_cast <gpointer> (this));
+    }
+
 }
 
 DBusServer::~DBusServer()
@@ -3686,6 +4191,155 @@ void DBusServer::removeInfoReq(const InfoReq &req)
     }
 }
 
+gboolean DBusServer::connmanPoll (gpointer dbusserver)
+{
+    DBusServer *me = static_cast<DBusServer *>(dbusserver);
+    DBusConnectionPtr conn = me->getConnmanConnection();
+    struct ConnmanClient : public DBusCallObject
+    {
+        DBusConnectionPtr m_connection;
+        ConnmanClient (DBusConnectionPtr conn ) : m_connection (conn) {}
+        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 const char *getMethod() const {return "GetProperties"; }
+        virtual DBusConnection *getConnection() const {return m_connection.get();}
+    }connman (conn);
+
+    typedef std::map <std::string, boost::variant <std::vector <std::string> > > PropDict;
+    DBusClientCall0<PropDict>  getProp(connman);
+    getProp (boost::bind(&DBusServer::connmanCallback, me, _1, _2));
+    return TRUE;
+}
+
+
+void DBusServer::connmanCallback (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") {
+            // no connman available, remove connmanPoll.
+            m_pollConnman.set(0);
+            // ensure there is still first set of singal set in case of no
+            // connman available
+            m_presence.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 == "EnabledTechnologies") {
+            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_presence.updatePresenceStatus (httpPresence, btPresence);
+}
+
+void DBusServer::getDeviceList(SyncConfig::DeviceList &devices)
+{
+    //wait bluez or other device managers
+    while(!m_bluezManager.isDone()) {
+        g_main_loop_run(m_loop);
+    }
+
+    devices.clear();
+    devices = m_syncDevices;
+}
+
+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>();
+    }
+}
+
+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);
+        templatesChanged();
+    }
+}
+
+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);
+            templatesChanged();
+            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; 
+            templatesChanged();
+            break;
+        }
+    }
+}
+
 /********************** InfoReq implementation ******************/
 InfoReq::InfoReq(DBusServer &server,
                  const string &type,
@@ -3838,6 +4492,178 @@ void InfoReq::done()
     }
 }
 
+/********************** BluezManager implementation ******************/
+BluezManager::BluezManager(DBusServer &server) :
+    m_server(server)
+{
+    m_bluezConn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, true, NULL);
+    if(m_bluezConn) {
+        m_done = false;
+        DBusClientCall0<DBusObject_t> getAdapter(*this, "DefaultAdapter");
+        getAdapter(boost::bind(&BluezManager::defaultAdapterCb, this, _1, _2 ));
+        m_adapterChanged.reset(new SignalWatch1<DBusObject_t>(*this, "DefaultAdapterChanged"));
+        (*m_adapterChanged)(boost::bind(&BluezManager::defaultAdapterChanged, this, _1));
+    } else {
+        m_done = true;
+    }
+}
+
+void BluezManager::defaultAdapterChanged(const DBusObject_t &adapter)
+{
+    m_done = false;
+    //remove devices that belong to this original adapter
+    if(m_adapter) {
+        BOOST_FOREACH(boost::shared_ptr<BluezDevice> &device, m_adapter->getDevices()) {
+            m_server.removeDevice(device->getMac());
+        }
+    }
+    string error;
+    defaultAdapterCb(adapter, error);
+}
+
+void BluezManager::defaultAdapterCb(const DBusObject_t &adapter, const string &error)
+{
+    if(!error.empty()) {
+        SE_LOG_DEBUG (NULL, NULL, "Error in calling DefaultAdapter of Interface org.bluez.Manager: %s", error.c_str());
+        m_done = true;
+        return;
+    }
+    m_adapter.reset(new BluezAdapter(*this, adapter)); 
+}
+
+BluezManager::BluezAdapter::BluezAdapter(BluezManager &manager, const string &path)
+    : m_manager(manager), m_path(path), m_devNo(0), m_devReplies(0),
+      m_deviceRemoved(*this,  "DeviceRemoved"), m_deviceAdded(*this, "DeviceCreated")
+{
+    DBusClientCall0<std::vector<DBusObject_t> > listDevices(*this, "ListDevices");
+    listDevices(boost::bind(&BluezAdapter::listDevicesCb, this, _1, _2));
+    //m_deviceRemoved.reset(new SignalWatch1<DBusObject_t>(*this, "DeviceRemoved"));
+    m_deviceRemoved(boost::bind(&BluezAdapter::deviceRemoved, this, _1));
+
+    //m_deviceAdded.reset(new SignalWatch1<DBusObject_t>(*this, "DeviceCreated"));
+    m_deviceAdded(boost::bind(&BluezAdapter::deviceCreated, this, _1));
+}
+
+void BluezManager::BluezAdapter::listDevicesCb(const std::vector<DBusObject_t> &devices, const string &error)
+{
+    if(!error.empty()) {
+        SE_LOG_DEBUG (NULL, NULL, "Error in calling ListDevices of Interface org.bluez.Adapter: %s", error.c_str());
+        checkDone(true);
+        return;
+    }
+    m_devNo = devices.size();
+    BOOST_FOREACH(const DBusObject_t &device, devices) {
+        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;
+        }
+    }
+    m_manager.m_server.removeDevice(address);
+}
+
+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)
+    : m_adapter(adapter), m_path(path), m_reply(false), m_propertyChanged(*this, "PropertyChanged")
+{
+    DBusClientCall0<PropDict> getProperties(*this, "GetProperties");
+    getProperties(boost::bind(&BluezDevice::getPropertiesCb, this, _1, _2));
+
+    m_propertyChanged(boost::bind(&BluezDevice::propertyChanged, this, _1, _2));
+}
+
+void BluezManager::BluezDevice::checkSyncService(const std::vector<std::string> &uuids)
+{
+    static const char * SYNCML_CLIENT_UUID = "00000002-0000-1000-8000-0002ee000002";
+    bool hasSyncService = false;
+    DBusServer &server = m_adapter.m_manager.m_server;
+    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()) {
+                server.addDevice(SyncConfig::DeviceDescription(m_mac, m_name, SyncConfig::MATCH_FOR_SERVER_MODE));
+            }
+            break;
+        }
+    }
+    // if sync service is not available now, possible to remove device
+    if(!hasSyncService && !m_mac.empty()) {
+        server.removeDevice(m_mac);
+    }
+}
+
+void BluezManager::BluezDevice::getPropertiesCb(const PropDict &props, const string &error)
+{
+    m_adapter.m_devReplies++;
+    m_reply = true;
+    if(!error.empty()) {
+        SE_LOG_DEBUG (NULL, NULL, "Error in calling GetProperties of Interface org.bluez.Device: %s", error.c_str());
+    } 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);
+        }
+    }
+    m_adapter.checkDone();
+}
+
+void BluezManager::BluezDevice::propertyChanged(const string &name,
+                                                const boost::variant<vector<string>, string> &prop)
+{
+    DBusServer &server = m_adapter.m_manager.m_server;
+    if(boost::iequals(name, "Name")) {
+        m_name = boost::get<std::string>(prop);
+        SyncConfig::DeviceDescription device;
+        if(server.getDevice(m_mac, device)) {
+            device.m_fingerprint = m_name;
+            server.updateDevice(m_mac, device);
+        }
+    } 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);
+        SyncConfig::DeviceDescription device;
+        if(server.getDevice(m_mac, device)) {
+            device.m_deviceId = mac;
+            server.updateDevice(m_mac, device);
+        }
+        m_mac = mac;
+    }
+}
+
 /**************************** main *************************/
 
 void niam(int sig)
index 0d3845d..fd3d03f 100644 (file)
@@ -197,7 +197,7 @@ bool Cmdline::run() {
                     SyncConfig::getPeerTemplates(devices), false);
         } else {
             //limiting at templates for syncml clients only.
-            devices.push_back (SyncConfig::DeviceList::value_type (m_template, SyncConfig::MATCH_FOR_SERVER_MODE));
+            devices.push_back (SyncConfig::DeviceDescription("", m_template, SyncConfig::MATCH_FOR_SERVER_MODE));
             dumpConfigTemplates("Available configuration templates:",
                     SyncConfig::matchPeerTemplates(devices), true);
         }
@@ -1370,6 +1370,7 @@ public:
                               "peers/scheduleworld/config.ini:ConsumerReady = 1\n"
 
                               "peers/scheduleworld/sources/addressbook/.internal.ini:# adminData = \n"
+                              "peers/scheduleworld/sources/addressbook/.internal.ini:# synthesisID = 0\n"
                               "peers/scheduleworld/sources/addressbook/config.ini:sync = two-way\n"
                               "sources/addressbook/config.ini:type = addressbook:text/vcard\n"
                               "peers/scheduleworld/sources/addressbook/config.ini:type = addressbook:text/vcard\n"
@@ -1379,6 +1380,7 @@ public:
                               "sources/addressbook/config.ini:# evolutionpassword = \n"
 
                               "peers/scheduleworld/sources/calendar/.internal.ini:# adminData = \n"
+                              "peers/scheduleworld/sources/calendar/.internal.ini:# synthesisID = 0\n"
                               "peers/scheduleworld/sources/calendar/config.ini:sync = two-way\n"
                               "sources/calendar/config.ini:type = calendar\n"
                               "peers/scheduleworld/sources/calendar/config.ini:type = calendar\n"
@@ -1388,6 +1390,7 @@ public:
                               "sources/calendar/config.ini:# evolutionpassword = \n"
 
                               "peers/scheduleworld/sources/memo/.internal.ini:# adminData = \n"
+                              "peers/scheduleworld/sources/memo/.internal.ini:# synthesisID = 0\n"
                               "peers/scheduleworld/sources/memo/config.ini:sync = two-way\n"
                               "sources/memo/config.ini:type = memo\n"
                               "peers/scheduleworld/sources/memo/config.ini:type = memo\n"
@@ -1397,6 +1400,7 @@ public:
                               "sources/memo/config.ini:# evolutionpassword = \n"
 
                               "peers/scheduleworld/sources/todo/.internal.ini:# adminData = \n"
+                              "peers/scheduleworld/sources/todo/.internal.ini:# synthesisID = 0\n"
                               "peers/scheduleworld/sources/todo/config.ini:sync = two-way\n"
                               "sources/todo/config.ini:type = todo\n"
                               "peers/scheduleworld/sources/todo/config.ini:type = todo\n"
@@ -1884,7 +1888,8 @@ protected:
             string expected = ScheduleWorldConfig();
             expected += "\n"
                 "peers/scheduleworld/sources/xyz/.internal.ini:# adminData = \n"
-                "peers/scheduleworld/sources/xyz/config.ini:# sync = two-way\n"
+                "peers/scheduleworld/sources/xyz/.internal.ini:# synthesisID = 0\n"
+                "peers/scheduleworld/sources/xyz/config.ini:# sync = disabled\n"
                 "peers/scheduleworld/sources/xyz/config.ini:# type = select backend\n"
                 "peers/scheduleworld/sources/xyz/config.ini:uri = dummy\n"
                 "sources/xyz/config.ini:# type = select backend\n"
@@ -2074,6 +2079,7 @@ protected:
             "ConfigDate" +
             "deviceData" +
             "adminData" +
+            "synthesisID" +
             "lastNonce" +
             "last";
         BOOST_FOREACH(string &prop, props) {
@@ -2532,36 +2538,7 @@ private:
                              "todo/config.ini:sync = disabled");
 
         return config;
-    }
-
-    /** temporarily set env variable, restore old value on destruction */
-    class ScopedEnvChange {
-    public:
-        ScopedEnvChange(const string &var, const string &value) :
-            m_var(var)
-        {
-            const char *oldval = getenv(var.c_str());
-            if (oldval) {
-                m_oldvalset = true;
-                m_oldval = oldval;
-            } else {
-                m_oldvalset = false;
-            }
-            setenv(var.c_str(), value.c_str(), 1);
-        }
-        ~ScopedEnvChange()
-        {
-            if (m_oldvalset) {
-                setenv(m_var.c_str(), m_oldval.c_str(), 1);
-            } else {
-                unsetenv(m_var.c_str());
-            } 
-        }
-    private:
-        string m_var, m_oldval;
-        bool m_oldvalset;
-    };
-            
+    }          
 
     /** create directory hierarchy, overwriting previous content */
     void createFiles(const string &root, const string &content, bool append = false) {
index aa428b8..7d84aec 100644 (file)
@@ -107,13 +107,13 @@ class ConfigNode {
     }
 
     bool getProperty(const string &property,
-                     string &value) {
+                     string &value) const {
         value = readProperty(property);
         return !value.empty();
     }
 
     bool getProperty(const string &property,
-                     bool &value) {
+                     bool &value) const {
         std::string str = readProperty(property);
         if (str.empty()) {
             return false;
@@ -144,7 +144,7 @@ class ConfigNode {
     }
 
     template <class T> bool getProperty(const string &property,
-                                        T &value) {
+                                        T &value) const {
         std::string str = readProperty(property);
         if (str.empty()) {
             return false;
index 1a54e49..a8a7880 100644 (file)
@@ -1,5 +1,7 @@
 AM_CPPFLAGS = @BACKEND_CPPFLAGS@ @GLIB_CFLAGS@ -I$(top_srcdir)/test -I$(top_srcdir)/src -DSYNCEVO_BACKEND=\"$(backendsearchdir)\"
 
+SUBDIRS = configs
+
 # applies to sources in SyncEvolution repository, but not
 # the Funambol C++ client library
 SYNCEVOLUTION_CXXFLAGS = @SYNCEVOLUTION_CXXFLAGS@
@@ -59,7 +61,6 @@ SYNCEVOLUTION_SOURCES  = \
        \
        SynthesisDBPlugin.cpp \
        \
-       SyncEvolutionXML.c \
         SyncContext.h \
        SyncContext.cpp \
        \
@@ -122,9 +123,14 @@ DISTCLEANFILES =syncevolution.pc
 EXTRA_DIST =syncevolution.pc.in
 
 libsyncevolution_la_SOURCES = $(SYNCEVOLUTION_SOURCES)
-libsyncevolution_la_LIBADD = @EPACKAGE_LIBS@ @GLIB_LIBS@ $(SYNTHESIS_LIBS) $(TRANSPORT_LIBS) @LIBS@ $(SYNCEVOLUTION_LDADD)
-libsyncevolution_la_CXXFLAGS = $(TRANSPORT_CFLAGS) $(SYNCEVOLUTION_CXXFLAGS) $(SYNTHESIS_CFLAGS)
-libsyncevolution_la_CPPFLAGS = $(AM_CPPFLAGS) -DTEMPLATE_DIR=\""$(datadir)/syncevolution/templates"\" -DLIBDIR=\""$(libdir)"\"
+nodist_libsyncevolution_la_SOURCES = SyncEvolutionXML.c
+CLEANFILES = SyncEvolutionXML.c
+libsyncevolution_la_LIBADD = @EPACKAGE_LIBS@ @GLIB_LIBS@ $(SYNTHESIS_LIBS) $(TRANSPORT_LIBS) @LIBS@ $(SYNCEVOLUTION_LDADD) $(NSS_LIBS)
+libsyncevolution_la_CXXFLAGS = $(TRANSPORT_CFLAGS) $(SYNCEVOLUTION_CXXFLAGS) $(SYNTHESIS_CFLAGS) $(NSS_CFLAGS)
+libsyncevolution_la_CPPFLAGS = $(AM_CPPFLAGS) \
+       -DXML_CONFIG_DIR=\""$(datadir)/syncevolution/xml"\" \
+       -DTEMPLATE_DIR=\""$(datadir)/syncevolution/templates"\" \
+       -DLIBDIR=\""$(libdir)"\"
 libsyncevolution_la_DEPENDENCIES = $(SYNTHESIS_DEP)
 
 if ENABLE_MODULES
@@ -133,10 +139,25 @@ else
 libsyncevolution_la_LDFLAGS = -static
 endif
 
-SyncEvolutionXML.c: $(srcdir)/../syncclient_sample_config.xml
-       echo "const char *SyncEvolutionXML =" > $@
-       sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/' $< >>$@
-       echo ";" >>$@
+# command which embeds its input lines into a C-style string that runs across multiple lines
+TO_C_STRING = sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/'
+
+# Don't depend on specific XML files. Instead recreate
+# SyncEvolutionXML.c each time make is invoked (allows including new
+# fragments in the binary without rerunning configure).
+SyncEvolutionXML.c: GenSyncEvolutionXML
+.PHONY: GenSyncEvolutionXML
+GenSyncEvolutionXML:
+       echo "const char *SyncEvolutionXMLClient =" > SyncEvolutionXML.c.new
+       (cd $(srcdir)/configs && perl update-samples.pl syncevolution.xml client ) | \
+          perl -p -e 's;</datatypes>;  <fieldlists/>\n    <profiles/>\n    <datatypedefs/>\n  </datatypes>;' | \
+          $(TO_C_STRING) >>SyncEvolutionXML.c.new
+       echo ";" >>SyncEvolutionXML.c.new
+       echo "const char *SyncEvolutionXMLClientRules =" >> SyncEvolutionXML.c.new
+       (cd $(srcdir)/configs && cat remoterules/*.xml remoterules/client/*.xml) | $(TO_C_STRING) >>SyncEvolutionXML.c.new
+       echo ";" >>SyncEvolutionXML.c.new
+       if cmp SyncEvolutionXML.c SyncEvolutionXML.c.new >/dev/null; then rm SyncEvolutionXML.c.new; else mv SyncEvolutionXML.c.new SyncEvolutionXML.c; fi
+
 
 # include boost in distribution
 #dist-hook:
index bd7a51a..6ce69da 100644 (file)
@@ -78,8 +78,8 @@ am__libsyncevolution_la_SOURCES_DIST = ConfigTree.h ConfigNode.h \
        CurlTransportAgent.h CurlTransportAgent.cpp \
        SoupTransportAgent.h SoupTransportAgent.cpp util.cpp util.h \
        lcs.h lcs.cpp Cmdline.cpp Cmdline.h SyncSource.h \
-       SyncSource.cpp SynthesisDBPlugin.cpp SyncEvolutionXML.c \
-       SyncContext.h SyncContext.cpp SyncConfig.h SyncConfig.cpp \
+       SyncSource.cpp SynthesisDBPlugin.cpp SyncContext.h \
+       SyncContext.cpp SyncConfig.h SyncConfig.cpp \
        DevNullConfigNode.h MultiplexConfigNode.h \
        MultiplexConfigNode.cpp FilterConfigNode.h \
        FilterConfigNode.cpp SafeConfigNode.h SafeConfigNode.cpp \
@@ -102,7 +102,6 @@ am__objects_2 = libsyncevolution_la-eds_abi_wrapper.lo \
        libsyncevolution_la-Cmdline.lo \
        libsyncevolution_la-SyncSource.lo \
        libsyncevolution_la-SynthesisDBPlugin.lo \
-       libsyncevolution_la-SyncEvolutionXML.lo \
        libsyncevolution_la-SyncContext.lo \
        libsyncevolution_la-SyncConfig.lo \
        libsyncevolution_la-MultiplexConfigNode.lo \
@@ -113,7 +112,10 @@ am__objects_2 = libsyncevolution_la-eds_abi_wrapper.lo \
        libsyncevolution_la-FileConfigTree.lo \
        libsyncevolution_la-TrackingSyncSource.lo $(am__objects_1)
 am_libsyncevolution_la_OBJECTS = $(am__objects_2)
-libsyncevolution_la_OBJECTS = $(am_libsyncevolution_la_OBJECTS)
+nodist_libsyncevolution_la_OBJECTS =  \
+       libsyncevolution_la-SyncEvolutionXML.lo
+libsyncevolution_la_OBJECTS = $(am_libsyncevolution_la_OBJECTS) \
+       $(nodist_libsyncevolution_la_OBJECTS)
 libsyncevolution_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
        $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
        $(libsyncevolution_la_CXXFLAGS) $(CXXFLAGS) \
@@ -139,14 +141,25 @@ CXXLD = $(CXX)
 CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
        --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
        $(LDFLAGS) -o $@
-SOURCES = $(libsyncevolution_la_SOURCES)
+SOURCES = $(libsyncevolution_la_SOURCES) \
+       $(nodist_libsyncevolution_la_SOURCES)
 DIST_SOURCES = $(am__libsyncevolution_la_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+       html-recursive info-recursive install-data-recursive \
+       install-dvi-recursive install-exec-recursive \
+       install-html-recursive install-info-recursive \
+       install-pdf-recursive install-ps-recursive install-recursive \
+       installcheck-recursive installdirs-recursive pdf-recursive \
+       ps-recursive uninstall-recursive
 pkgconfigDATA_INSTALL = $(INSTALL_DATA)
 DATA = $(pkgconfig_DATA)
 libsyncevolution_includeHEADERS_INSTALL = $(INSTALL_HEADER)
 HEADERS = $(libsyncevolution_include_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive        \
+  distclean-recursive maintainer-clean-recursive
 ETAGS = etags
 CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ADDRESSBOOK_CFLAGS = @ADDRESSBOOK_CFLAGS@
@@ -206,11 +219,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -240,6 +258,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -259,6 +279,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
@@ -362,6 +384,7 @@ target_alias = @target_alias@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 AM_CPPFLAGS = @BACKEND_CPPFLAGS@ @GLIB_CFLAGS@ -I$(top_srcdir)/test -I$(top_srcdir)/src -DSYNCEVO_BACKEND=\"$(backendsearchdir)\"
+SUBDIRS = configs
 MAINTAINERCLEANFILES = Makefile.in
 lib_LTLIBRARIES = libsyncevolution.la
 SYNCEVOLUTION_SOURCES = ConfigTree.h ConfigNode.h HashConfigNode.h \
@@ -373,14 +396,14 @@ SYNCEVOLUTION_SOURCES = ConfigTree.h ConfigNode.h HashConfigNode.h \
        CurlTransportAgent.cpp SoupTransportAgent.h \
        SoupTransportAgent.cpp util.cpp util.h lcs.h lcs.cpp \
        Cmdline.cpp Cmdline.h SyncSource.h SyncSource.cpp \
-       SynthesisDBPlugin.cpp SyncEvolutionXML.c SyncContext.h \
-       SyncContext.cpp SyncConfig.h SyncConfig.cpp \
-       DevNullConfigNode.h MultiplexConfigNode.h \
-       MultiplexConfigNode.cpp FilterConfigNode.h \
-       FilterConfigNode.cpp SafeConfigNode.h SafeConfigNode.cpp \
-       PrefixConfigNode.h PrefixConfigNode.cpp FileConfigNode.h \
-       FileConfigNode.cpp FileConfigTree.h FileConfigTree.cpp \
-       TrackingSyncSource.h TrackingSyncSource.cpp $(am__append_3)
+       SynthesisDBPlugin.cpp SyncContext.h SyncContext.cpp \
+       SyncConfig.h SyncConfig.cpp DevNullConfigNode.h \
+       MultiplexConfigNode.h MultiplexConfigNode.cpp \
+       FilterConfigNode.h FilterConfigNode.cpp SafeConfigNode.h \
+       SafeConfigNode.cpp PrefixConfigNode.h PrefixConfigNode.cpp \
+       FileConfigNode.h FileConfigNode.cpp FileConfigTree.h \
+       FileConfigTree.cpp TrackingSyncSource.h TrackingSyncSource.cpp \
+       $(am__append_3)
 libsyncevolution_includedir = $(includedir)/syncevo
 libsyncevolution_include_HEADERS = \
        declarations.h \
@@ -405,13 +428,22 @@ pkgconfig_DATA = syncevolution.pc
 DISTCLEANFILES = syncevolution.pc
 EXTRA_DIST = syncevolution.pc.in installcheck-local.sh
 libsyncevolution_la_SOURCES = $(SYNCEVOLUTION_SOURCES)
-libsyncevolution_la_LIBADD = @EPACKAGE_LIBS@ @GLIB_LIBS@ $(SYNTHESIS_LIBS) $(TRANSPORT_LIBS) @LIBS@ $(SYNCEVOLUTION_LDADD)
-libsyncevolution_la_CXXFLAGS = $(TRANSPORT_CFLAGS) $(SYNCEVOLUTION_CXXFLAGS) $(SYNTHESIS_CFLAGS)
-libsyncevolution_la_CPPFLAGS = $(AM_CPPFLAGS) -DTEMPLATE_DIR=\""$(datadir)/syncevolution/templates"\" -DLIBDIR=\""$(libdir)"\"
+nodist_libsyncevolution_la_SOURCES = SyncEvolutionXML.c
+CLEANFILES = SyncEvolutionXML.c
+libsyncevolution_la_LIBADD = @EPACKAGE_LIBS@ @GLIB_LIBS@ $(SYNTHESIS_LIBS) $(TRANSPORT_LIBS) @LIBS@ $(SYNCEVOLUTION_LDADD) $(NSS_LIBS)
+libsyncevolution_la_CXXFLAGS = $(TRANSPORT_CFLAGS) $(SYNCEVOLUTION_CXXFLAGS) $(SYNTHESIS_CFLAGS) $(NSS_CFLAGS)
+libsyncevolution_la_CPPFLAGS = $(AM_CPPFLAGS) \
+       -DXML_CONFIG_DIR=\""$(datadir)/syncevolution/xml"\" \
+       -DTEMPLATE_DIR=\""$(datadir)/syncevolution/templates"\" \
+       -DLIBDIR=\""$(libdir)"\"
+
 libsyncevolution_la_DEPENDENCIES = $(SYNTHESIS_DEP)
 @ENABLE_MODULES_FALSE@libsyncevolution_la_LDFLAGS = -static
 @ENABLE_MODULES_TRUE@libsyncevolution_la_LDFLAGS = 
-all: all-am
+
+# command which embeds its input lines into a C-style string that runs across multiple lines
+TO_C_STRING = sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/'
+all: all-recursive
 
 .SUFFIXES:
 .SUFFIXES: .c .cpp .lo .o .obj
@@ -765,6 +797,76 @@ uninstall-libsyncevolution_includeHEADERS:
          rm -f "$(DESTDIR)$(libsyncevolution_includedir)/$$f"; \
        done
 
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+#     (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+       @failcom='exit 1'; \
+       for f in x $$MAKEFLAGS; do \
+         case $$f in \
+           *=* | --[!k]*);; \
+           *k*) failcom='fail=yes';; \
+         esac; \
+       done; \
+       dot_seen=no; \
+       target=`echo $@ | sed s/-recursive//`; \
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+         echo "Making $$target in $$subdir"; \
+         if test "$$subdir" = "."; then \
+           dot_seen=yes; \
+           local_target="$$target-am"; \
+         else \
+           local_target="$$target"; \
+         fi; \
+         (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+         || eval $$failcom; \
+       done; \
+       if test "$$dot_seen" = "no"; then \
+         $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+       fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+       @failcom='exit 1'; \
+       for f in x $$MAKEFLAGS; do \
+         case $$f in \
+           *=* | --[!k]*);; \
+           *k*) failcom='fail=yes';; \
+         esac; \
+       done; \
+       dot_seen=no; \
+       case "$@" in \
+         distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+         *) list='$(SUBDIRS)' ;; \
+       esac; \
+       rev=''; for subdir in $$list; do \
+         if test "$$subdir" = "."; then :; else \
+           rev="$$subdir $$rev"; \
+         fi; \
+       done; \
+       rev="$$rev ."; \
+       target=`echo $@ | sed s/-recursive//`; \
+       for subdir in $$rev; do \
+         echo "Making $$target in $$subdir"; \
+         if test "$$subdir" = "."; then \
+           local_target="$$target-am"; \
+         else \
+           local_target="$$target"; \
+         fi; \
+         (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+         || eval $$failcom; \
+       done && test -z "$$fail"
+tags-recursive:
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+         test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+       done
+ctags-recursive:
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+         test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+       done
+
 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
        list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
        unique=`for i in $$list; do \
@@ -775,10 +877,23 @@ ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
        mkid -fID $$unique
 tags: TAGS
 
-TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
                $(TAGS_FILES) $(LISP)
        tags=; \
        here=`pwd`; \
+       if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+         include_option=--etags-include; \
+         empty_fix=.; \
+       else \
+         include_option=--include; \
+         empty_fix=; \
+       fi; \
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+         if test "$$subdir" = .; then :; else \
+           test ! -f $$subdir/TAGS || \
+             tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+         fi; \
+       done; \
        list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
        unique=`for i in $$list; do \
            if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
@@ -791,7 +906,7 @@ TAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
            $$tags $$unique; \
        fi
 ctags: CTAGS
-CTAGS:  $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) \
                $(TAGS_FILES) $(LISP)
        tags=; \
        list='$(SOURCES) $(HEADERS)  $(LISP) $(TAGS_FILES)'; \
@@ -838,22 +953,40 @@ distdir: $(DISTFILES)
            || exit 1; \
          fi; \
        done
+       list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+         if test "$$subdir" = .; then :; else \
+           test -d "$(distdir)/$$subdir" \
+           || $(MKDIR_P) "$(distdir)/$$subdir" \
+           || exit 1; \
+           distdir=`$(am__cd) $(distdir) && pwd`; \
+           top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+           (cd $$subdir && \
+             $(MAKE) $(AM_MAKEFLAGS) \
+               top_distdir="$$top_distdir" \
+               distdir="$$distdir/$$subdir" \
+               am__remove_distdir=: \
+               am__skip_length_check=: \
+               distdir) \
+             || exit 1; \
+         fi; \
+       done
 check-am: all-am
-check: check-am
+check: check-recursive
 all-am: Makefile $(LTLIBRARIES) $(DATA) $(HEADERS)
-installdirs:
+installdirs: installdirs-recursive
+installdirs-am:
        for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(libsyncevolution_includedir)"; do \
          test -z "$$dir" || $(MKDIR_P) "$$dir"; \
        done
-install: install-am
-install-exec: install-exec-am
-install-data: install-data-am
-uninstall: uninstall-am
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
 
 install-am: all-am
        @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
 
-installcheck: installcheck-am
+installcheck: installcheck-recursive
 install-strip:
        $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
          install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
@@ -862,6 +995,7 @@ install-strip:
 mostlyclean-generic:
 
 clean-generic:
+       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
 
 distclean-generic:
        -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
@@ -871,61 +1005,61 @@ maintainer-clean-generic:
        @echo "This command is intended for maintainers to use"
        @echo "it deletes files that may require special tools to rebuild."
        -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
-clean: clean-am
+clean: clean-recursive
 
 clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
        mostlyclean-am
 
-distclean: distclean-am
+distclean: distclean-recursive
        -rm -rf ./$(DEPDIR)
        -rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
        distclean-tags
 
-dvi: dvi-am
+dvi: dvi-recursive
 
 dvi-am:
 
-html: html-am
+html: html-recursive
 
-info: info-am
+info: info-recursive
 
 info-am:
 
 install-data-am: install-libsyncevolution_includeHEADERS \
        install-pkgconfigDATA
 
-install-dvi: install-dvi-am
+install-dvi: install-dvi-recursive
 
 install-exec-am: install-libLTLIBRARIES
 
-install-html: install-html-am
+install-html: install-html-recursive
 
-install-info: install-info-am
+install-info: install-info-recursive
 
 install-man:
 
-install-pdf: install-pdf-am
+install-pdf: install-pdf-recursive
 
-install-ps: install-ps-am
+install-ps: install-ps-recursive
 
 installcheck-am: installcheck-local
 
-maintainer-clean: maintainer-clean-am
+maintainer-clean: maintainer-clean-recursive
        -rm -rf ./$(DEPDIR)
        -rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
-mostlyclean: mostlyclean-am
+mostlyclean: mostlyclean-recursive
 
 mostlyclean-am: mostlyclean-compile mostlyclean-generic \
        mostlyclean-libtool
 
-pdf: pdf-am
+pdf: pdf-recursive
 
 pdf-am:
 
-ps: ps-am
+ps: ps-recursive
 
 ps-am:
 
@@ -933,31 +1067,45 @@ uninstall-am: uninstall-libLTLIBRARIES \
        uninstall-libsyncevolution_includeHEADERS \
        uninstall-pkgconfigDATA
 
-.MAKE: install-am install-strip
-
-.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
-       clean-libLTLIBRARIES clean-libtool ctags distclean \
-       distclean-compile distclean-generic distclean-libtool \
-       distclean-tags distdir dvi dvi-am html html-am info info-am \
-       install install-am install-data install-data-am install-dvi \
-       install-dvi-am install-exec install-exec-am install-html \
-       install-html-am install-info install-info-am \
-       install-libLTLIBRARIES install-libsyncevolution_includeHEADERS \
-       install-man install-pdf install-pdf-am install-pkgconfigDATA \
-       install-ps install-ps-am install-strip installcheck \
-       installcheck-am installcheck-local installdirs \
-       maintainer-clean maintainer-clean-generic mostlyclean \
-       mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
-       pdf pdf-am ps ps-am tags uninstall uninstall-am \
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
+       install-strip
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+       all all-am check check-am clean clean-generic \
+       clean-libLTLIBRARIES clean-libtool ctags ctags-recursive \
+       distclean distclean-compile distclean-generic \
+       distclean-libtool distclean-tags distdir dvi dvi-am html \
+       html-am info info-am install install-am install-data \
+       install-data-am install-dvi install-dvi-am install-exec \
+       install-exec-am install-html install-html-am install-info \
+       install-info-am install-libLTLIBRARIES \
+       install-libsyncevolution_includeHEADERS install-man \
+       install-pdf install-pdf-am install-pkgconfigDATA install-ps \
+       install-ps-am install-strip installcheck installcheck-am \
+       installcheck-local installdirs installdirs-am maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+       tags tags-recursive uninstall uninstall-am \
        uninstall-libLTLIBRARIES \
        uninstall-libsyncevolution_includeHEADERS \
        uninstall-pkgconfigDATA
 
 
-SyncEvolutionXML.c: $(srcdir)/../syncclient_sample_config.xml
-       echo "const char *SyncEvolutionXML =" > $@
-       sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/' $< >>$@
-       echo ";" >>$@
+# Don't depend on specific XML files. Instead recreate
+# SyncEvolutionXML.c each time make is invoked (allows including new
+# fragments in the binary without rerunning configure).
+SyncEvolutionXML.c: GenSyncEvolutionXML
+.PHONY: GenSyncEvolutionXML
+GenSyncEvolutionXML:
+       echo "const char *SyncEvolutionXMLClient =" > SyncEvolutionXML.c.new
+       (cd $(srcdir)/configs && perl update-samples.pl syncevolution.xml client ) | \
+          perl -p -e 's;</datatypes>;  <fieldlists/>\n    <profiles/>\n    <datatypedefs/>\n  </datatypes>;' | \
+          $(TO_C_STRING) >>SyncEvolutionXML.c.new
+       echo ";" >>SyncEvolutionXML.c.new
+       echo "const char *SyncEvolutionXMLClientRules =" >> SyncEvolutionXML.c.new
+       (cd $(srcdir)/configs && cat remoterules/*.xml remoterules/client/*.xml) | $(TO_C_STRING) >>SyncEvolutionXML.c.new
+       echo ";" >>SyncEvolutionXML.c.new
+       if cmp SyncEvolutionXML.c SyncEvolutionXML.c.new >/dev/null; then rm SyncEvolutionXML.c.new; else mv SyncEvolutionXML.c.new SyncEvolutionXML.c; fi
 
 # include boost in distribution
 #dist-hook:
index 0865293..d264d82 100644 (file)
 
 SE_BEGIN_CXX
 
-ObexTransportAgent::ObexTransportAgent (OBEX_TRANS_TYPE type) :
+ObexTransportAgent::ObexTransportAgent (OBEX_TRANS_TYPE type, GMainLoop *loop) :
     m_status(INACTIVE),
     m_transType(type),
+    m_context(g_main_context_ref(loop ?
+                                 g_main_loop_get_context(loop) :
+                                 g_main_context_default())),
     m_address(""),
     m_port(-1),
     m_buffer(NULL),
@@ -103,6 +106,14 @@ void ObexTransportAgent::connect() {
     m_obexReady = false;
     if(m_transType == OBEX_BLUETOOTH) {
         if(m_port == -1) {
+            EDSAbiWrapperInit();
+            // sdp_connect may be a pointer when EVOLUTION_COMPATIBILITY is enabled.
+            // Must check whether we really have an implementation of the sdp_ calls
+            // before using them.
+            if (!SyncEvoHaveLibbluetooth) {
+                SE_THROW_EXCEPTION (TransportException, "no suitable libbluetooth found, try setting Bluetooth channel manually (obex-bt://<mac>+<channel>)");
+            }
+            
             //use sdp to detect the appropriate channel
             //Do not use BDADDR_ANY to avoid a warning
             bdaddr_t bdaddr, anyaddr ={{0,0,0,0,0,0}};
@@ -351,7 +362,7 @@ TransportAgent::Status ObexTransportAgent::wait(bool noReply) {
     cxxptr<Channel> channel;
 
     while (!m_obexReady) {
-        g_main_context_iteration (NULL, TRUE);
+        g_main_context_iteration (m_context, TRUE);
         if (m_status == FAILED) {
             if (m_obexEvent) {
                 obexEvent = m_obexEvent;
@@ -391,7 +402,7 @@ TransportAgent::Status ObexTransportAgent::wait(bool noReply) {
         }
 
         while (!m_obexReady) {
-            g_main_context_iteration (NULL, FALSE);
+            g_main_context_iteration (m_context, TRUE);
             if (m_status == FAILED) {
                 SE_THROW_EXCEPTION (TransportException, 
                         "ObexTransprotAgent: Underlying transport error");
@@ -502,10 +513,10 @@ void ObexTransportAgent::sdp_callback_impl (uint8_t type, uint16_t status, uint8
 
            int seqSize = 0;
         uint8_t dtdp;
-#ifdef HAVE_BLUEZ_BUFSIZE
-        scanned = sdp_extract_seqtype(rsp, bufSize, &dtdp, &seqSize);
-#elif defined(HAVE_BLUEZ_SAFE)
+#if defined(HAVE_BLUEZ_SAFE) || defined(EVOLUTION_COMPATIBILITY)
         scanned = sdp_extract_seqtype_safe(rsp, bufSize, &dtdp, &seqSize);
+#elif defined(HAVE_BLUEZ_BUFSIZE)
+        scanned = sdp_extract_seqtype(rsp, bufSize, &dtdp, &seqSize);
 #else
         scanned = sdp_extract_seqtype(rsp, &dtdp, &seqSize);
 #endif
@@ -521,10 +532,10 @@ void ObexTransportAgent::sdp_callback_impl (uint8_t type, uint16_t status, uint8
             int recSize;
 
             recSize = 0;
-#ifdef HAVE_BLUEZ_BUFSIZE
-            rec = sdp_extract_pdu(rsp, bufSize, &recSize);
-#elif defined(HAVE_BLUEZ_SAFE)
+#if defined(HAVE_BLUEZ_SAFE) || defined(EVOLUTION_COMPATIBILITY) 
             rec = sdp_extract_pdu_safe(rsp, bufSize, &recSize);
+#elif defined(HAVE_BLUEZ_BUFSIZE)
+            rec = sdp_extract_pdu(rsp, bufSize, &recSize);
 #else
             rec = sdp_extract_pdu(rsp, &recSize);
 #endif
@@ -770,7 +781,7 @@ void ObexTransportAgent::obex_callback (obex_object_t *object, int mode, int eve
                         m_connectStatus = END;
                         OBEX_TransportDisconnect (m_handle->get());
                         m_status = CLOSED;
-                    } else {
+                    } else if (obex_rsp !=0) {
                         SE_LOG_ERROR (NULL, NULL, "ObexTransport Error %d", obex_rsp);
                         m_status = FAILED;
                         return;
index 9709f1e..78c6369 100644 (file)
@@ -79,6 +79,11 @@ public:
  */
 class ObexTransportAgent : public TransportAgent 
 {
+    class ContextUnref {
+    public:
+        static void unref(GMainContext *context) { g_main_context_unref(context); }
+    };
+
     public:
         enum OBEX_TRANS_TYPE{
             OBEX_BLUETOOTH,
@@ -86,7 +91,12 @@ class ObexTransportAgent : public TransportAgent
             INVALID
         };
 
-        ObexTransportAgent(OBEX_TRANS_TYPE type);
+        /**
+         * @param loop     the glib loop to use when waiting for IO;
+         *                 transport will increase the reference count;
+         *                 if NULL a new loop in the default context is used
+         */
+        ObexTransportAgent(OBEX_TRANS_TYPE type, GMainLoop *loop);
         ~ObexTransportAgent();
 
         virtual void setURL (const std::string &url);
@@ -139,6 +149,9 @@ class ObexTransportAgent : public TransportAgent
          */
         OBEX_TRANS_TYPE m_transType;
 
+        /** context that needs to be kept alive while waiting for OBEX */
+        eptr<GMainContext, GMainContext, ContextUnref> m_context;
+
         /* The address of the remote device  
          * macadd for Bluetooth; device name for usb; host name for
          * tcp/ip
index 10ff0f5..1a3120e 100644 (file)
@@ -83,6 +83,10 @@ class UnrefGLibEvent {
  public:
     static void unref(guint event) { g_source_remove(event); }
 };
+class UnrefGString {
+ public:
+    static void unref(gchar *ptr) { g_free(ptr); }
+};
 #endif // HAVE_GLIB
 
 /**
@@ -207,6 +211,7 @@ template <class T> class arrayptr : public eptr<T, T, ArrayUnref<T> > {
 #ifdef HAVE_GLIB
 /** eptr for glib event handle */
 typedef SmartPtr<guint, guint, UnrefGLibEvent> GLibEvent;
+typedef SmartPtr<gchar *, gchar *, UnrefGString> GString;
 #endif
 
 SE_END_CXX
index e7049b9..948bb84 100644 (file)
@@ -36,7 +36,7 @@ SE_BEGIN_CXX
 SoupTransportAgent::SoupTransportAgent(GMainLoop *loop) :
     m_session(soup_session_async_new()),
     m_loop(loop ?
-           loop :
+           g_main_loop_ref(loop) :
            g_main_loop_new(NULL, TRUE),
            "Soup main loop"),
     m_status(INACTIVE),
index 6723d68..89ba1f8 100644 (file)
@@ -52,7 +52,7 @@ class SoupTransportAgent : public HTTPTransportAgent
  public:
     /**
      *  @param loop     the glib loop to use when waiting for IO;
-     *                  will be owned and unref'ed by the new instance;
+     *                  transport will increase the reference count;
      *                  if NULL a new loop in the default context is used
      */
     SoupTransportAgent(GMainLoop *loop = NULL);
index d6f90f5..7506069 100644 (file)
@@ -73,15 +73,7 @@ void ConfigProperty::throwValueError(const ConfigNode &node, const string &name,
 
 string SyncConfig::normalizeConfigString(const string &config)
 {
-    string normal, context;
-    normalizeConfigString(config, normal, context);
-    return normal;
-}
-
-void SyncConfig::normalizeConfigString(const string &config, string &normal, string &context)
-{
-    context = "";
-    normal = config;
+    string normal = config;
     boost::to_lower(normal);
     BOOST_FOREACH(char &character, normal) {
         if (!isprint(character) ||
@@ -92,21 +84,35 @@ void SyncConfig::normalizeConfigString(const string &config, string &normal, str
         }
     }
     if (boost::ends_with(normal, "@default")) {
-        context = "default";
         normal.resize(normal.size() - strlen("@default"));
     } else if (boost::ends_with(normal, "@")) {
         normal.resize(normal.size() - 1);
     } else {
-        // context specified?
         size_t at = normal.rfind('@');
-        if (at != normal.npos) {
-            context = normal.substr(at + 1);
+        if (at == normal.npos) {
+            // No explicit context. Pick the first server which matches
+            // when ignoring their context. Peer list is sorted by name,
+            // therefore shorter config names (= without context) are
+            // found first, as intended.
+            BOOST_FOREACH(const StringPair &entry, getConfigs()) {
+                string entry_peer, entry_context;
+                splitConfigString(entry.first, entry_peer, entry_context);
+                if (normal == entry_peer) {
+                    // found a matching, existing config, use it
+                    normal = entry.first;
+                    break;
+                }
+            }
         }
     }
+
     if (normal.empty()) {
-        // leave context empty, it wasn't set explicitly
+        // default context is meant with the empty string,
+        // better make that explicit
         normal = "@default";
     }
+
+    return normal;
 }
 
 void SyncConfig::splitConfigString(const string &config, string &peer, string &context)
@@ -155,23 +161,7 @@ SyncConfig::SyncConfig(const string &peer,
 
     string root;
 
-    string context;
-    normalizeConfigString(peer, m_peer, context);
-    if (context.empty()) {
-        // No explicit context. Pick the first server which matches
-        // when ignoring their context. Peer list is sorted by name,
-        // therefore shorter config names (= without context) are
-        // found first, as intended.
-        BOOST_FOREACH(const StringPair &entry, getConfigs()) {
-            string entry_peer, entry_context;
-            splitConfigString(entry.first, entry_peer, entry_context);
-            if (m_peer == entry_peer) {
-                // found a matching, existing config, use it
-                m_peer = entry.first;
-                break;
-            }
-        }
-    }
+    m_peer = normalizeConfigString(peer);
 
     // except for SHARED_LAYOUT (set below),
     // everything is below the directory called like
@@ -438,17 +428,17 @@ SyncConfig::TemplateList SyncConfig::matchPeerTemplates(const DeviceList &peers,
         } else {
             TemplateConfig templateConf (sDir);
             BOOST_FOREACH (const DeviceList::value_type &entry, peers){
-                int rank = templateConf.metaMatch (entry.first, entry.second);
+                int rank = templateConf.metaMatch (entry.m_fingerprint, entry.m_matchMode);
                 if (fuzzyMatch){
                     if (rank > TemplateConfig::NO_MATCH) {
                         result.push_back (boost::shared_ptr<TemplateDescription>(
                                     new TemplateDescription(templateConf.getName(),
-                                        templateConf.getDescription(), rank, entry.first, sDir, templateConf.getFingerprint())));
+                                        templateConf.getDescription(), rank, entry.m_deviceId, entry.m_fingerprint, sDir, templateConf.getFingerprint())));
                     }
                 } else if (rank == TemplateConfig::BEST_MATCH){
                     result.push_back (boost::shared_ptr<TemplateDescription>(
                                 new TemplateDescription(templateConf.getName(),
-                                    templateConf.getDescription(), rank, entry.first, sDir, templateConf.getFingerprint())));
+                                    templateConf.getDescription(), rank, entry.m_deviceId, entry.m_fingerprint, sDir, templateConf.getFingerprint())));
                     break;
                 }
             }
@@ -479,7 +469,7 @@ boost::shared_ptr<SyncConfig> SyncConfig::createPeerTemplate(const string &serve
         templateConfig = server;
     } else {
         SyncConfig::DeviceList devices;
-        devices.push_back (std::make_pair(server, MATCH_ALL));
+        devices.push_back (DeviceDescription("", server, MATCH_ALL));
         templateConfig = "";
         TemplateList templates = matchPeerTemplates (devices, false);
         if (!templates.empty()) {
@@ -497,11 +487,7 @@ boost::shared_ptr<SyncConfig> SyncConfig::createPeerTemplate(const string &serve
     boost::shared_ptr<PersistentSyncSourceConfig> source;
 
     config->setDefaults(false);
-    // The prefix is important: without it, myFUNAMBOL 6.x and 7.0 map
-    // all SyncEvolution instances to the single phone that they support,
-    // which leads to unwanted slow syncs when switching between multiple
-    // instances.
-    config->setDevID(string("sc-pim-") + UUID());
+    config->setDevID(string("syncevolution-") + UUID());
 
     // create sync source configs and set non-default values
     config->setSourceDefaults("addressbook", false);
@@ -894,7 +880,7 @@ ConstSyncSourceNodes SyncConfig::getSyncSourceNodes(const string &name,
 
 static ConfigProperty syncPropSyncURL("syncURL",
                                       "Identifies how to contact the peer,\n"
-                                      "best explained with some examples.\n"
+                                      "best explained with some examples:\n"
                                       "HTTP(S) SyncML servers:\n"
                                       "  http://my.funambol.com/sync\n"
                                       "  http://sync.scheduleworld.com/funambol/ds\n"
@@ -903,7 +889,15 @@ static ConfigProperty syncPropSyncURL("syncURL",
                                       "the channel chosen automatically:\n"
                                       "  obex-bt://00:0A:94:03:F3:7E\n"
                                       "If the automatism fails, the channel can also be specified:\n"
-                                      "  obex-bt://00:0A:94:03:F3:7E+16\n");
+                                      "  obex-bt://00:0A:94:03:F3:7E+16\n"
+                                      "For peers contacting us via Bluetooth, the MAC address is\n"
+                                      "used to identify it before the sync starts. Multiple\n"
+                                      "urls can be specified in one syncURL property:\n"
+                                      "  obex-bt://00:0A:94:03:F3:7E obex-bt://00:01:02:03:04:05\n"
+                                      "In the future this might be used to contact the peer\n"
+                                      "via one of several transports; right now, only the first\n"
+                                      "one is tried." // MB #9446
+                                      );
 
 static ConfigProperty syncPropDevID("deviceId",
                                     "The SyncML server gets this string and will use it to keep track of\n"
@@ -949,12 +943,8 @@ static BoolConfigProperty syncPropPreventSlowSync("preventSlowSync",
                                                   "  this as 'user wants to start from scratch') => the sync would\n"
                                                   "  recreate all the client's data, even if the user really wanted\n"
                                                   "  to have it deleted, therefore slow sync is prevented\n"
-                                                  "Slow syncs are not yet detected when running as server and in the\n"
-                                                  "client when the server's anchor is wrong.\n"
-                                                  "This option is not enabled by default because it forces users\n"
-                                                  "to deal with slow syncs, which is a deviation from previous\n"
-                                                  "behavior.",
-                                                  "0");
+                                                  "Slow syncs are not yet detected when running as server.\n",
+                                                  "1");
 static BoolConfigProperty syncPropUseProxy("useProxy",
                                            "set to T to choose an HTTP proxy explicitly; otherwise the default\n"
                                            "proxy settings of the underlying HTTP transport mechanism are used;\n"
@@ -988,7 +978,7 @@ static ULongConfigProperty syncPropMaxMsgSize("maxMsgSize",
                                               "peer can be told to never sent items larger than a certain\n"
                                               "threshold (maxObjSize). Presumably the peer has to truncate or\n"
                                               "skip larger items. Sizes are specified as number of bytes.",
-                                              "20000");
+                                              "150000");
 static UIntConfigProperty syncPropMaxObjSize("maxObjSize", "", "4000000");
 
 static BoolConfigProperty syncPropCompression("enableCompression", "enable compression of network traffic (not currently supported)");
@@ -1395,8 +1385,23 @@ void SyncConfig::saveProxyPassword(ConfigUserInterface &ui) {
     syncPropProxyPassword.savePassword(ui, m_peer, *getNode(syncPropProxyPassword), "", boost::shared_ptr<FilterConfigNode>());
 }
 void SyncConfig::setProxyPassword(const string &value, bool temporarily) { m_cachedProxyPassword = ""; syncPropProxyPassword.setProperty(*getNode(syncPropProxyPassword), value, temporarily); }
-const char *SyncConfig::getSyncURL() const { return m_stringCache.getProperty(*getNode(syncPropSyncURL), syncPropSyncURL); }
+vector<string> SyncConfig::getSyncURL() const { 
+    string s = m_stringCache.getProperty(*getNode(syncPropSyncURL), syncPropSyncURL);
+    vector<string> urls;
+    // workaround for g++ 4.3/4.4:
+    // http://stackoverflow.com/questions/1168525/c-gcc4-4-warning-array-subscript-is-above-array-bounds
+    static const string sep(" \t");
+    boost::split(urls, s, boost::is_any_of(sep));
+    return urls;
+}
 void SyncConfig::setSyncURL(const string &value, bool temporarily) { syncPropSyncURL.setProperty(*getNode(syncPropSyncURL), value, temporarily); }
+void SyncConfig::setSyncURL(const vector<string> &value, bool temporarily) { 
+    stringstream urls;
+    BOOST_FOREACH (string url, value) {
+        urls<<url<<" ";
+    }
+    return setSyncURL (urls.str(), temporarily);
+}
 const char *SyncConfig::getClientAuthType() const { return m_stringCache.getProperty(*getNode(syncPropClientAuthType), syncPropClientAuthType); }
 void SyncConfig::setClientAuthType(const string &value, bool temporarily) { syncPropClientAuthType.setProperty(*getNode(syncPropClientAuthType), value, temporarily); }
 unsigned long  SyncConfig::getMaxMsgSize() const { return syncPropMaxMsgSize.getPropertyValue(*getNode(syncPropMaxMsgSize)); }
@@ -1699,7 +1704,7 @@ StringConfigProperty SyncSourceConfig::m_sourcePropSync("sync",
                                            "  one-way-from-client = transmit changes from client\n"
                                            "  one-way-from-server = transmit changes from server\n"
                                            "  none (or disabled)  = synchronization disabled",
-                                           "two-way",
+                                           "disabled",
                                            "",
                                            Values() +
                                            (Aliases("two-way")) +
@@ -1884,6 +1889,8 @@ static EvolutionPasswordConfigProperty sourcePropPassword("evolutionpassword", "
 static ConfigProperty sourcePropAdminData(SourceAdminDataName,
                                           "used by the Synthesis library internally; do not modify");
 
+static IntConfigProperty sourcePropSynthesisID("synthesisID", "unique integer ID, necessary for libsynthesis", "0");
+
 ConfigPropertyRegistry &SyncSourceConfig::getRegistry()
 {
     static ConfigPropertyRegistry registry;
@@ -1897,6 +1904,7 @@ ConfigPropertyRegistry &SyncSourceConfig::getRegistry()
         registry.push_back(&sourcePropUser);
         registry.push_back(&sourcePropPassword);
         registry.push_back(&sourcePropAdminData);
+        registry.push_back(&sourcePropSynthesisID);
 
         // obligatory source properties
         SyncSourceConfig::m_sourcePropSync.setObligatory(true);
@@ -1905,6 +1913,7 @@ ConfigPropertyRegistry &SyncSourceConfig::getRegistry()
         // non-shared properties (other hidden nodes don't
         // exist at the moment)
         sourcePropAdminData.setHidden(true);
+        sourcePropSynthesisID.setHidden(true);
 
         // No global source properties. Does not make sense
         // conceptually.
@@ -2021,6 +2030,9 @@ SourceType SyncSourceConfig::getSourceType(const SyncSourceNodes &nodes) {
 SourceType SyncSourceConfig::getSourceType() const { return getSourceType(m_nodes); }
 void SyncSourceConfig::setSourceType(const string &value, bool temporarily) { sourcePropSourceType.setProperty(*getNode(sourcePropSourceType), value, temporarily); }
 
+const int SyncSourceConfig::getSynthesisID() const { return sourcePropSynthesisID.getPropertyValue(*getNode(sourcePropSynthesisID)); }
+void SyncSourceConfig::setSynthesisID(int value, bool temporarily) { sourcePropSynthesisID.setProperty(*getNode(sourcePropSynthesisID), value, temporarily); }
+
 ConfigPasswordKey EvolutionPasswordConfigProperty::getPasswordKey(const string &descr,
                                                                   const string &serverName,
                                                                   FilterConfigNode &globalConfigNode,
@@ -2164,6 +2176,9 @@ class SyncConfigTest : public CppUnit::TestFixture {
 private:
     void normalize()
     {
+        ScopedEnvChange xdg("XDG_CONFIG_HOME", "/dev/null");
+        ScopedEnvChange home("HOME", "/dev/null");
+
         CPPUNIT_ASSERT_EQUAL(std::string("@default"),
                              SyncConfig::normalizeConfigString(""));
         CPPUNIT_ASSERT_EQUAL(std::string("@default"),
index a891415..4459816 100644 (file)
@@ -851,6 +851,9 @@ class SyncConfig {
         // The matched percentage of the template, larger the better.
         int m_rank;
 
+        //a unique identity of the device that the template is for, used by caller
+        std::string m_id;
+
         // A string identify which fingerprint the template is matched with.
         std::string m_fingerprint;
 
@@ -863,10 +866,11 @@ class SyncConfig {
         std::string m_matchedModel;
 
         TemplateDescription (const std::string &name, const std::string &description, 
-                const int rank, const std::string &fingerprint, const std::string &path, const std::string &model)
+                const int rank, const std::string id, const std::string &fingerprint, const std::string &path, const std::string &model)
             :   m_name (name),
                 m_description (description),
                 m_rank (rank),
+                m_id (id),
                 m_fingerprint (fingerprint),
                 m_path (path),
                 m_matchedModel(model)
@@ -889,7 +893,24 @@ class SyncConfig {
     };
 
     typedef list<boost::shared_ptr <TemplateDescription> > TemplateList;
-    typedef list<std::pair <std::string, SyncConfig::MatchMode> > DeviceList;
+
+    struct DeviceDescription {
+        /** the id of the device */
+        std::string m_deviceId;
+        /** the finger print of the device used for matching templates */
+        std::string m_fingerprint;
+        /** match mode used for matching templates */
+        MatchMode m_matchMode;
+        DeviceDescription(const std::string &deviceId,
+                          const std::string &fingerprint,
+                          MatchMode mode)
+            :m_deviceId(deviceId), m_fingerprint(fingerprint), m_matchMode(mode)
+        {}
+        DeviceDescription() : m_matchMode(INVALID)
+        {}
+    };
+
+    typedef list<DeviceDescription> DeviceList;
 
     /**
      * returns list of servers in either the old (.sync4j) or
@@ -975,22 +996,14 @@ class SyncConfig {
      * - lower case
      * - non-printable and unsafe characters (colon, slash, backslash)
      *   replaced by underscore
+     * - when no context specified: search for peer config first in @default,
+     *   then also in other contexts in alphabetical order
      * - @default stripped
      * - empty string replaced with "@default"
      */
     static string normalizeConfigString(const string &config);
 
     /**
-     * Normalize and in addition, set original context.
-     *
-     * @param config      one of the various ways of selecting a configuration
-     *                    (@default, scheduleworld, scheduleworld@foo, ...)
-     * @retval normal     normalized form of config string       
-     * @retval context    empty if no specified, otherwise normalized value
-     */
-    static void normalizeConfigString(const string &config, string &normal, string &context);
-
-    /**
      * Split a config string (normalized or not) into the peer part
      * (before final @) and the context (after that @, not including
      * it), return "default" as context if not specified otherwise.
@@ -1208,8 +1221,9 @@ class SyncConfig {
     virtual void checkProxyPassword(ConfigUserInterface &ui);
     virtual void saveProxyPassword(ConfigUserInterface &ui);
     virtual void setProxyPassword(const string &value, bool temporarily = false);
-    virtual const char*  getSyncURL() const;
+    virtual vector<string>  getSyncURL() const;
     virtual void setSyncURL(const string &value, bool temporarily = false);
+    virtual void setSyncURL(const vector<string> &value, bool temporarily = false);
     virtual const char*  getClientAuthType() const;
     virtual void setClientAuthType(const string &value, bool temporarily = false);
     virtual unsigned long getMaxMsgSize() const;
@@ -1543,10 +1557,18 @@ class SyncSourceConfig {
     /** same as SyncConfig::savePassword() */
     virtual void savePassword(ConfigUserInterface &ui, const string &serverName, FilterConfigNode& globalConfigNode);
 
+    /** selects the backend database to use */
     virtual const char *getDatabaseID() const;
     virtual void setDatabaseID(const string &value, bool temporarily = false);
 
     /**
+     * internal property: unique integer ID for the source, needed by Synthesis XML <dbtypeid>,
+     * zero if unset
+     */
+    virtual const int getSynthesisID() const;
+    virtual void setSynthesisID(int value, bool temporarily = false);
+
+    /**
      * Returns the data source type configured as part of the given
      * configuration; different SyncSources then check whether
      * they support that type. This call has to work before instantiating
index a8c15c7..f431f99 100644 (file)
@@ -52,6 +52,7 @@ using namespace std;
 #include <boost/algorithm/string/join.hpp>
 #include <boost/foreach.hpp>
 #include <boost/algorithm/string/split.hpp>
+#include <boost/bind.hpp>
 
 #include <sys/stat.h>
 #include <sys/wait.h>
@@ -67,6 +68,8 @@ using namespace std;
 #include <synthesis/SDK_util.h>
 #include <synthesis/san.h>
 
+#include "test.h"
+
 #include <syncevo/declarations.h>
 SE_BEGIN_CXX
 
@@ -182,26 +185,6 @@ class LogDir : public LoggerBase {
     // internal prefix for backup directory name: "SyncEvolution-"
     static const char* const DIR_PREFIX;
 
-    /** set m_logdir and adapt m_prefix accordingly */
-    void setLogdir(const string &logdir) {
-        m_logdir = boost::trim_right_copy_if(logdir, boost::is_any_of("/"));
-
-        // the config name has been normalized
-        string peer = m_client.getConfigName();
-
-        // escape "_" and "-" the peer name
-        peer = escapePeer(peer);
-
-        if (boost::iends_with(m_logdir, "syncevolution")) {
-            // use just the server name as prefix
-            m_prefix = peer;
-        } else {
-            // SyncEvolution-<server>-<yyyy>-<mm>-<dd>-<hh>-<mm>
-            m_prefix = DIR_PREFIX;
-            m_prefix += peer;
-        }
-    }
-
 public:
     LogDir(SyncContext &client) : m_client(client), m_parentLogger(LoggerBase::instance()), m_info(NULL), m_readonly(false), m_report(NULL)
     {
@@ -218,27 +201,20 @@ public:
         rename(SubstEnvironment("${XDG_DATA_HOME}/applications/syncevolution").c_str(),
                SubstEnvironment("${XDG_CACHE_HOME}/syncevolution").c_str());
 
-        setLogdir(SubstEnvironment("${XDG_CACHE_HOME}/syncevolution"));
+        const char *path = m_client.getLogDir();
+        setLogdir(!path || !path[0] ?
+                  "${XDG_CACHE_HOME}/syncevolution" :
+                  path);
     }
 
     /**
-     * Finds previous log directories. Reports errors via exceptions.
+     * Finds previous log directories for context. Reports errors via exceptions.
      *
-     * @param path        path to configured backup directy, NULL if defaulting to /tmp, "none" if not creating log file
      * @retval dirs       vector of full path names, oldest first
      */
-    void previousLogdirs(const char *path, vector<string> &dirs) {
-        string logdir;
-
+    void previousLogdirs(vector<string> &dirs) {
         dirs.clear();
-        if (path && !strcasecmp(path, "none")) {
-            return;
-        } else {
-            if (path && path[0]) {
-                setLogdir(SubstEnvironment(path));
-            }
-            getLogdirs(dirs);
-        }
+        getLogdirs(dirs);
     }
 
     /**
@@ -247,10 +223,10 @@ public:
      * @param path        path to configured backup directy, NULL if defaulting to /tmp, "none" if not creating log file
      * @return full path of previous log directory, empty string if not found
      */
-    string previousLogdir(const char *path) throw() {
+    string previousLogdir() throw() {
         try {
             vector<string> dirs;
-            previousLogdirs(path, dirs);
+            previousLogdirs(dirs);
             return dirs.empty() ? "" : dirs.back();
         } catch (...) {
             Exception::handle();
@@ -259,6 +235,39 @@ public:
     }
 
     /**
+     * Set log dir and base name used for searching and creating sessions.
+     * Default if not called is the getLogDir() value of the context.
+     *
+     * @param logdir     "none" to disable sessions, NULL/"" for default, may contain ${}
+     *                   for environment variables
+     */
+    void setLogdir(const char *logdir) {
+        if (!logdir || !logdir[0]) {
+            return;
+        }
+        m_logdir = SubstEnvironment(logdir);
+        m_logdir = boost::trim_right_copy_if(m_logdir, boost::is_any_of("/"));
+        if (m_logdir == "none") {
+            return;
+        }
+
+        // the config name has been normalized
+        string peer = m_client.getConfigName();
+
+        // escape "_" and "-" the peer name
+        peer = escapePeer(peer);
+
+        if (boost::iends_with(m_logdir, "syncevolution")) {
+            // use just the server name as prefix
+            m_prefix = peer;
+        } else {
+            // SyncEvolution-<server>-<yyyy>-<mm>-<dd>-<hh>-<mm>
+            m_prefix = DIR_PREFIX;
+            m_prefix += peer;
+        }
+    }
+
+    /**
      * access existing log directory to extract status information
      */
     void openLogdir(const string &dir) {
@@ -314,18 +323,14 @@ public:
     // @param logLevel    0 = default, 1 = ERROR, 2 = INFO, 3 = DEBUG
     // @param usePath     write directly into path, don't create and manage subdirectories
     // @param report      record information about session here (may be NULL)
-    // @param logname     the basename to be used for logs, traditionally "client" for syncs
-    void startSession(const char *path, int maxlogdirs, int logLevel, bool usePath, SyncReport *report, const string &logname) {
+    void startSession(const char *path, int maxlogdirs, int logLevel, bool usePath, SyncReport *report) {
         m_maxlogdirs = maxlogdirs;
         m_report = report;
         m_logfile = "";
         if (path && !strcasecmp(path, "none")) {
             m_path = "";
         } else {
-            if (path && path[0]) {
-                setLogdir(SubstEnvironment(path));
-            }
-
+            setLogdir(path);
             if (!usePath) {
                 // create unique directory name in the given directory
                 time_t ts = time(NULL);
@@ -338,36 +343,48 @@ public:
                      << setw(2) << tm->tm_mday << "-"
                      << setw(2) << tm->tm_hour << "-"
                      << setw(2) << tm->tm_min;
-                // make sure no directory name has the same date time with others 
-                // even for different peers
-                std::vector<string> dateTimes;
-                if(isDir(m_logdir)) {
+                // If other sessions, regardless of which peer, have
+                // the same date and time, then append a sequence
+                // number to ensure correct sorting. Solve this by
+                // finding the maximum sequence number for any kind of
+                // date time. Backwards running clocks or changing the
+                // local time will still screw our ordering, though.
+                typedef std::map<string, int> SeqMap_t;
+                SeqMap_t dateTimes2Seq;
+                if (isDir(m_logdir)) {
                     ReadDir dir(m_logdir);
                     BOOST_FOREACH(const string &entry, dir) {
                         string dirPrefix, peerName, dateTime;
-                        if(parseDirName(entry, dirPrefix, peerName, dateTime)) {
-                            dateTimes.push_back(dateTime);
+                        if (parseDirName(entry, dirPrefix, peerName, dateTime)) {
+                            // dateTime = -2010-01-31-12-00[-rev]
+                            size_t off = 0;
+                            for (int i = 0; off != dateTime.npos && i < 5; i++) {
+                                off = dateTime.find('-', off + 1);
+                            }
+                            int sequence;
+                            if (off != dateTime.npos) {
+                                sequence = atoi(dateTime.substr(off + 1).c_str());
+                                dateTime.resize(off);
+                            } else {
+                                sequence = 0;
+                            }
+                            pair <SeqMap_t::iterator, bool> entry = dateTimes2Seq.insert(make_pair(dateTime, sequence));
+                            if (sequence > entry.first->second) {
+                                entry.first->second = sequence;
+                            }
                         }
                     }
-                    sort(dateTimes.begin(), dateTimes.end());
                 }
-                int seq = 0;
-                while (true) {
-                    stringstream path;
-                    path << base.str();
-                    if (seq) {
-                        path << "-" << seq;
-                    }
-                    if (!binary_search(dateTimes.begin(), dateTimes.end(), path.str())) {
-                        m_path = m_logdir + "/";
-                        m_path += m_prefix;
-                        m_path += path.str();
-                        mkdir_p(m_path);
-                        break;
-                    } else {
-                        seq++;
-                    }
+                stringstream path;
+                path << base.str();
+                SeqMap_t::iterator it = dateTimes2Seq.find(path.str());
+                if (it != dateTimes2Seq.end()) {
+                    path << "-" << it->second + 1;
                 }
+                m_path = m_logdir + "/";
+                m_path += m_prefix;
+                m_path += path.str();
+                mkdir_p(m_path);
             } else {
                 m_path = m_logdir;
                 if (mkdir(m_path.c_str(), S_IRWXU) &&
@@ -419,7 +436,7 @@ public:
             m_info->setMode(false);
             // Create a status.ini which contains an error.
             // Will be overwritten later on, unless we crash.
-            m_info->setProperty("status", "500");
+            m_info->setProperty("status", STATUS_DIED_PREMATURELY);
             m_info->setProperty("error", "synchronization process died prematurely");
             writeTimestamp("start", start);
         }
@@ -438,20 +455,127 @@ public:
         return m_logfile;
     }
 
-    // remove oldest backup dirs if exceeding limit
+    /**
+     * remove backup dir(s) if exceeding limit
+     *
+     * Assign a priority to each session dir, with lower
+     * meaning "less important". Then sort by priority and (if
+     * equal) creation time (aka index) in ascending
+     * order. The sessions at the beginning of the sorted
+     * vector are then removed first.
+     *
+     * DUMPS = any kind of database dump was made
+     * ERROR = session failed
+     * CHANGES = local data modified since previous dump (based on dumps
+     *           of the current peer, for simplicity reasons),
+     *           dump created for the first time,
+     *           changes made during sync (detected with dumps and statistics)
+     *
+     * The goal is to preserve as many database dumps as possible
+     * and ideally those where something happened.
+     *
+     * Some criteria veto the removal of a session:
+     * - it is the only one holding a dump of a specific source
+     * - it is the last session
+     */
     void expire() {
         if (m_logdir.size() && m_maxlogdirs > 0 ) {
             vector<string> dirs;
             getLogdirs(dirs);
 
+            /** stores priority and index in "dirs"; after sorting, delete from the start */
+            vector< pair<Priority, size_t> > victims;
+            /** maps from source name to list of information about dump, oldest first */
+            typedef map< string, list<DumpInfo> > Dumps_t;
+            Dumps_t dumps;
+            for (size_t i = 0;
+                 i < dirs.size();
+                 i++) {
+                bool changes = false;
+                bool havedumps = false;
+                bool errors = false;
+
+                LogDir logdir(m_client);
+                logdir.openLogdir(dirs[i]);
+                SyncReport report;
+                logdir.readReport(report);
+                SyncMLStatus status = report.getStatus();
+                if (status != STATUS_OK && status != STATUS_HTTP_OK) {
+                    errors = true;
+                }
+                BOOST_FOREACH(SyncReport::SourceReport_t source, report) {
+                    string &sourcename = source.first;
+                    SyncSourceReport &sourcereport = source.second;
+                    list<DumpInfo> &dumplist = dumps[sourcename];
+                    if (sourcereport.m_backupBefore.isAvailable() ||
+                        sourcereport.m_backupAfter.isAvailable()) {
+                        // yes, we have backup dumps
+                        havedumps = true;
+
+                        DumpInfo info(i,
+                                      sourcereport.m_backupBefore.getNumItems(),
+                                      sourcereport.m_backupAfter.getNumItems());
+
+                        // now check for changes, if none found yet
+                        if (!changes) {
+                            if (dumplist.empty()) {
+                                // new backup dump
+                                changes = true;
+                            } else {
+                                DumpInfo &previous = dumplist.back();
+                                changes =
+                                    // item count changed -> items changed
+                                    previous.m_itemsDumpedAfter != info.m_itemsDumpedBefore ||
+                                    sourcereport.wasChanged(SyncSourceReport::ITEM_LOCAL) ||
+                                    sourcereport.wasChanged(SyncSourceReport::ITEM_REMOTE) ||
+                                    haveDifferentContent(sourcename,
+                                                         dirs[previous.m_dirIndex], "after",
+                                                         dirs[i], "before");
+                            }
+                        }
+
+                        dumplist.push_back(info);
+                    }
+                }
+                Priority pri =
+                    havedumps ?
+                    (changes ?
+                     HAS_DUMPS_WITH_CHANGES :
+                     errors ?
+                     HAS_DUMPS_NO_CHANGES_WITH_ERRORS :
+                     HAS_DUMPS_NO_CHANGES) :
+                    (changes ?
+                     NO_DUMPS_WITH_CHANGES :
+                     errors ?
+                     NO_DUMPS_WITH_ERRORS :
+                     NO_DUMPS_NO_ERRORS);
+                victims.push_back(make_pair(pri, i));
+            }
+            sort(victims.begin(), victims.end());
+
             int deleted = 0;
-            for (vector<string>::iterator it = dirs.begin();
-                 it != dirs.end() && (int)dirs.size() - deleted > m_maxlogdirs;
-                 ++it, ++deleted) {
-                string &path = *it;
-                string msg = "removing " + path;
-                SE_LOG_INFO(NULL, NULL, "%s", msg.c_str());
-                rm_r(path);
+            for (size_t e = 0;
+                 e < victims.size() && (int)dirs.size() - deleted > m_maxlogdirs;
+                 ++e) {
+                size_t index = victims[e].second;
+                string &path = dirs[index];
+                // preserve latest session
+                if (index != dirs.size() - 1) {
+                    bool mustkeep = false;
+                    // also check whether it holds the only backup of a source
+                    BOOST_FOREACH(Dumps_t::value_type dump, dumps) {
+                        if (dump.second.size() == 1 &&
+                            dump.second.front().m_dirIndex == index) {
+                            mustkeep = true;
+                            break;
+                        }
+                    }
+                    if (!mustkeep) {
+                        SE_LOG_DEBUG(NULL, NULL, "removing %s", path.c_str());
+                        rm_r(path);
+                        ++deleted;
+                    }
+                }
             }
         }
     }
@@ -529,7 +653,74 @@ public:
         return dateTime1 < dateTime2;
     }
 
+    /**
+     * Compare two database dumps just based on their inodes.
+     * @return true    if inodes differ
+     */
+    static bool haveDifferentContent(const string &sourceName,
+                                     const string &firstDir,
+                                     const string &firstSuffix,
+                                     const string &secondDir,
+                                     const string &secondSuffix)
+    {
+        string first = firstDir + "/" + sourceName + "." + firstSuffix;
+        string second = secondDir + "/" + sourceName + "." + secondSuffix;
+        ReadDir firstContent(first);
+        ReadDir secondContent(second);
+        set<ino_t> firstInodes;
+        BOOST_FOREACH(const string &name, firstContent) {
+            struct stat buf;
+            string fullpath = first + "/" + name;
+            if (stat(fullpath.c_str(), &buf)) {
+                SyncContext::throwError(fullpath, errno);
+            }
+            firstInodes.insert(buf.st_ino);
+        }
+        BOOST_FOREACH(const string &name, secondContent) {
+            struct stat buf;
+            string fullpath = second + "/" + name;
+            if (stat(fullpath.c_str(), &buf)) {
+                SyncContext::throwError(fullpath, errno);
+            }
+            set<ino_t>::iterator it = firstInodes.find(buf.st_ino);
+            if (it == firstInodes.end()) {
+                // second dir has different file
+                return true;
+            } else {
+                firstInodes.erase(it);
+            }
+        }
+        if (!firstInodes.empty()) {
+            // first dir has different file
+            return true;
+        }
+        // exact match of inodes
+        return false;
+    }
+
 private:
+    enum Priority {
+        NO_DUMPS_NO_ERRORS,
+        NO_DUMPS_WITH_ERRORS,
+        NO_DUMPS_WITH_CHANGES,
+        HAS_DUMPS_NO_CHANGES,
+        HAS_DUMPS_NO_CHANGES_WITH_ERRORS,
+        HAS_DUMPS_WITH_CHANGES
+    };
+
+    struct DumpInfo {
+        size_t m_dirIndex;
+        int m_itemsDumpedBefore;
+        int m_itemsDumpedAfter;
+        DumpInfo(size_t dirIndex,
+                 int itemsDumpedBefore,
+                 int itemsDumpedAfter) :
+            m_dirIndex(dirIndex),
+            m_itemsDumpedBefore(itemsDumpedBefore),
+            m_itemsDumpedAfter(itemsDumpedAfter)
+        {}
+    };
+
     /**
      * extract backup directory name from a full backup path
      * for example, a full path "/home/xxx/.cache/syncevolution/default/funambol-2009-12-08-14-05"
@@ -565,6 +756,7 @@ private:
 
     /**
      * parse a directory name into dirPrefix(empty or DIR_PREFIX), peerName, dateTime.
+     * peerName must be unescaped by the caller to get the real string.
      * If directory name is in the format of '[DIR_PREFIX]-peer[@context]-year-month-day-hour-min'
      * then parsing is sucessful and these 3 strings are correctly set and true is returned. 
      * Otherwise, false is returned. 
@@ -594,7 +786,7 @@ private:
      * logdir are returned.
      */
     void getLogdirs(vector<string> &dirs) {
-        if (!isDir(m_logdir)) {
+        if (m_logdir != "none" && !isDir(m_logdir)) {
             return;
         }
         string peer = m_client.getConfigName();
@@ -612,7 +804,7 @@ private:
                 } else if(peerName.empty()) {
                     // if no peer name and only context, match for all logs under the given context
                     string tmpName, tmpContext;
-                    SyncConfig::splitConfigString(tmpPeer, tmpName, tmpContext);
+                    SyncConfig::splitConfigString(unescapePeer(tmpPeer), tmpName, tmpContext);
                     if( context == tmpContext && boost::starts_with(m_prefix, tmpDirPrefix)) {
                         dirs.push_back(m_logdir + "/" + entry);
                     }
@@ -689,12 +881,46 @@ private:
 
 const char* const LogDir::DIR_PREFIX = "SyncEvolution-";
 
-// this class owns the sync sources and (together with
-// a logdir) handles writing of per-sync files as well
-// as the final report 
-// It also handles the virtual syncsources that is a combination of several
-// real syncsources.
-class SourceList : public vector<SyncSource *> {
+/**
+ * This class owns the sync sources. For historic reasons (required
+ * by Funambol) SyncSource instances are stored as plain pointers
+ * deleted by this class. Virtual sync sources were added later
+ * and are stored as shared pointers which are freed automatically.
+ * It is possible to iterate over the two classes of sources
+ * separately.
+ *
+ * The SourceList ensures that all sources (normal and virtual) have
+ * a valid and unique integer ID as needed for Synthesis. Traditionally
+ * this used to be a simple hash of the source name (which is unique
+ * by design), without checking for hash collisions. Now the ID is assigned
+ * the first time a source is added here and doesn't have one yet.
+ * For backward compatibility (the ID is stored in the .synthesis dir),
+ * the same Hash() value is tested first. Assuming that there were no
+ * hash conflicts, the same IDs will be generated as before.
+ *
+ * Together with a logdir, the SourceList
+ * handles writing of per-sync files as well as the final report.
+ * It is not stateless. The expectation is that it is instantiated
+ * together with a SyncContext for one particular operation (sync
+ * session, status check, restore). In contrast to a SyncContext,
+ * this class has to be recreated for another operation.
+ *
+ * When running as client, only the active sources get added. They can
+ * be dumped one after the other before running a sync.
+ *
+ * As a server, all sources get added, regardless whether they are
+ * active. This implies that at least their "type" must be valid. Then
+ * later when a client really starts using them, they are opened() and
+ * database dumps are made.
+ *
+ * Virtual datastores are stored here when they get initialized
+ * together with the normal sources by the user of SourceList.
+ *
+ * 
+ */
+class SourceList : private vector<SyncSource *> {
+    typedef vector<SyncSource *> inherited;
+
 public:
     enum LogLevel {
         LOGGING_QUIET,    /**< avoid all extra output */
@@ -702,22 +928,30 @@ public:
         LOGGING_FULL      /**< everything */
     };
 
-    struct SourceConfigSpecials{
-        bool m_forceSlow;
-        string m_alias;
-        SourceConfigSpecials ():
-            m_forceSlow(false),
-            m_alias("")
-        {
-        }
-    };
+    typedef std::vector< boost::shared_ptr<VirtualSyncSource> > VirtualSyncSources_t;
+
+    /** reading our set of virtual sources is okay, modifying it is not */
+    const VirtualSyncSources_t &getVirtualSources() { return m_virtualSources; }
+    void addSource(const boost::shared_ptr<VirtualSyncSource> &source) { checkSource(source.get()); m_virtualSources.push_back(source); }
+
+    using inherited::iterator;
+    using inherited::const_iterator;
+    using inherited::empty;
+    using inherited::begin;
+    using inherited::end;
+    using inherited::rbegin;
+    using inherited::rend;
+
+    /** transfers ownership (historic reasons for storing plain pointer...) */
+    void addSource(cxxptr<SyncSource> &source) { checkSource(source); push_back(source.release()); }
 
-    std::vector<boost::shared_ptr<VirtualSyncSource> >m_virtualDS; /**All configured virtual datastores*/
-    std::map<std::string, SourceConfigSpecials > m_configSpecials; /*Indicating whether the corresponding sync source is forced slow*/
 private:
+    VirtualSyncSources_t m_virtualSources; /**< all configured virtual data sources (aka Synthesis <superdatastore>) */
     LogDir m_logdir;     /**< our logging directory */
     SyncContext &m_client; /**< the context in which we were instantiated */
-    bool m_prepared;     /**< remember whether syncPrepare() dumped databases successfully */
+    set<string> m_prepared;   /**< remember for which source we dumped databases successfully */
+    string m_intro;      /**< remembers the dumpLocalChanges() intro and only prints it again
+                            when different from last dumpLocalChanges() call */
     bool m_doLogging;    /**< true iff the normal logdir handling is enabled
                             (creating and expiring directoties, before/after comparison) */
     bool m_reportTodo;   /**< true if syncDone() shall print a final report */
@@ -733,27 +967,117 @@ private:
             source.getName() + "." + suffix;
     }
 
+    /** ensure that Synthesis ID is set and unique */
+    void checkSource(SyncSource *source) {
+        if (source->getSynthesisID()) {
+            return;
+        }
+        int id = Hash(source->getName()) % INT_MAX;
+        while (true) {
+            // avoid negative values
+            if (id < 0) {
+                id = -id;
+            }
+            // avoid zero, it means unset
+            if (!id) {
+                id = 1;
+            }
+            // check for collisions
+            bool collision = false;
+            BOOST_FOREACH(const string &other, m_client.getSyncSources()) {
+                boost::shared_ptr<PersistentSyncSourceConfig> sc(m_client.getSyncSourceConfig(other));
+                int other_id = sc->getSynthesisID();
+                if (other_id == id) {
+                    ++id;
+                    collision = true;
+                    break;
+                }
+            }
+            if (!collision) {
+                source->setSynthesisID(id);
+                return;
+            }
+        }
+    }
+
 public:
     LogLevel getLogLevel() const { return m_logLevel; }
     void setLogLevel(LogLevel logLevel) { m_logLevel = logLevel; }
 
     /**
-     * dump into files with a certain suffix,
-     * optionally store report in member of SyncSourceReport
+     * Dump into files with a certain suffix, optionally store report
+     * in member of SyncSourceReport. Remembers which sources were
+     * dumped before a sync and only dumps those again afterward.
+     *
+     * @param suffix        "before/after/current" - before sync, after sync, during status check
+     * @param excludeSource when not empty, only dump that source
      */
     void dumpDatabases(const string &suffix,
-                       BackupReport SyncSourceReport::*report) {
+                       BackupReport SyncSourceReport::*report,
+                       const string &excludeSource = "") {
+        // Identify all logdirs of current context, of any peer.  Used
+        // to search for previous backups of each source, if
+        // necessary.
+        string peer = m_client.getConfigName();
+        string peerName, contextName;
+        SyncConfig::splitConfigString(peer, peerName, contextName);
+        SyncContext context(string("@") + contextName);
+        LogDir logdir(context);
+        vector<string> dirs;
+        logdir.previousLogdirs(dirs);
+
         BOOST_FOREACH(SyncSource *source, *this) {
+            if ((!excludeSource.empty() && excludeSource != source->getName()) ||
+                (suffix == "after" && m_prepared.find(source->getName()) == m_prepared.end())) {
+                continue;
+            }
+
             string dir = databaseName(*source, suffix);
             boost::shared_ptr<ConfigNode> node = ConfigNode::createFileNode(dir + ".ini");
             SE_LOG_DEBUG(NULL, NULL, "creating %s", dir.c_str());
             rm_r(dir);
-            mkdir_p(dir);
             BackupReport dummy;
             if (source->getOperations().m_backupData) {
-                source->getOperations().m_backupData(dir, *node,
+                SyncSource::Operations::ConstBackupInfo oldBackup;
+                // Now look for a backup of the current source,
+                // starting with the most recent one.
+                for (vector<string>::const_reverse_iterator it = dirs.rbegin();
+                     it != dirs.rend();
+                     ++it) {
+                    const string &sessiondir = *it;
+                    string oldBackupDir;
+                    SyncSource::Operations::BackupInfo::Mode mode =
+                        SyncSource::Operations::BackupInfo::BACKUP_AFTER;
+                    oldBackupDir = databaseName(*source, "after", sessiondir);
+                    if (!isDir(oldBackupDir)) {
+                        mode = SyncSource::Operations::BackupInfo::BACKUP_BEFORE;
+                        oldBackupDir = databaseName(*source, "before", sessiondir);
+                        if (!isDir(oldBackupDir)) {
+                            // try next session
+                            continue;
+                        }
+                    }
+
+                    oldBackup.m_mode = mode;
+                    oldBackup.m_dirname = oldBackupDir;
+                    oldBackup.m_node = ConfigNode::createFileNode(oldBackupDir + ".ini");
+                    break;
+                }
+                mkdir_p(dir);
+                SyncSource::Operations::BackupInfo newBackup(suffix == "before" ?
+                                                             SyncSource::Operations::BackupInfo::BACKUP_BEFORE :
+                                                             suffix == "after" ?
+                                                             SyncSource::Operations::BackupInfo::BACKUP_AFTER :
+                                                             SyncSource::Operations::BackupInfo::BACKUP_OTHER,
+                                                             dir, node);
+                source->getOperations().m_backupData(oldBackup, newBackup,
                                                      report ? source->*report : dummy);
                 SE_LOG_DEBUG(NULL, NULL, "%s created", dir.c_str());
+
+                // remember that we have dumped at the beginning of a sync
+                if (suffix == "before") {
+                    m_prepared.insert(source->getName());
+                }
             }
         }
     }
@@ -766,14 +1090,14 @@ public:
             SyncContext::throwError(dir + ": no such database backup found");
         }
         if (source.getOperations().m_restoreData) {
-            source.getOperations().m_restoreData(dir, *node, dryrun, report);
+            source.getOperations().m_restoreData(SyncSource::Operations::ConstBackupInfo(SyncSource::Operations::BackupInfo::BACKUP_OTHER, dir, node),
+                                                 dryrun, report);
         }
     }
 
     SourceList(SyncContext &client, bool doLogging) :
         m_logdir(client),
         m_client(client),
-        m_prepared(false),
         m_doLogging(doLogging),
         m_reportTodo(true),
         m_logLevel(LOGGING_FULL)
@@ -781,17 +1105,17 @@ public:
     }
     
     // call as soon as logdir settings are known
-    void startSession(const char *logDirPath, int maxlogdirs, int logLevel, SyncReport *report,
-                      const string &logname) {
-        m_previousLogdir = m_logdir.previousLogdir(logDirPath);
+    void startSession(const char *logDirPath, int maxlogdirs, int logLevel, SyncReport *report) {
+        m_logdir.setLogdir(logDirPath);
+        m_previousLogdir = m_logdir.previousLogdir();
         if (m_doLogging) {
-            m_logdir.startSession(logDirPath, maxlogdirs, logLevel, false, report, logname);
+            m_logdir.startSession(logDirPath, maxlogdirs, logLevel, false, report);
         } else {
             // Run debug session without paying attention to
             // the normal logdir handling. The log level here
             // refers to stdout. The log file will be as complete
             // as possible.
-            m_logdir.startSession(logDirPath, 0, 1, true, report, logname);
+            m_logdir.startSession(logDirPath, 0, 1, true, report);
         }
     }
 
@@ -810,25 +1134,65 @@ public:
      * If possible (directory to compare against available) and enabled,
      * then dump changes applied locally.
      *
+     * @param oldSession     directory to compare against; "" searches in sessions of current peer
+     *                       as selected by context for the lastest one involving each source
      * @param oldSuffix      suffix of old database dump: usually "after"
      * @param currentSuffix  the current database dump suffix: "current"
      *                       when not doing a sync, otherwise "before"
+     * @param excludeSource  when not empty, only dump that source
      */
-    bool dumpLocalChanges(const string &oldDir,
+    bool dumpLocalChanges(const string &oldSession,
                           const string &oldSuffix, const string &newSuffix,
-                          const string &intro = "Local data changes to be applied to server during synchronization:\n",
+                          const string &excludeSource,
+                          const string &intro = "Local data changes to be applied remotely during synchronization:\n",
                           const string &config = "CLIENT_TEST_LEFT_NAME='after last sync' CLIENT_TEST_RIGHT_NAME='current data' CLIENT_TEST_REMOVED='removed since last sync' CLIENT_TEST_ADDED='added since last sync'") {
-        if (m_logLevel <= LOGGING_SUMMARY || oldDir.empty()) {
+        if (m_logLevel <= LOGGING_SUMMARY) {
             return false;
         }
 
-        cout << intro;
+        vector<string> dirs;
+        if (oldSession.empty()) {
+            m_logdir.previousLogdirs(dirs);
+        }
+
         BOOST_FOREACH(SyncSource *source, *this) {
-            string oldFile = databaseName(*source, oldSuffix, oldDir);
-            string newFile = databaseName(*source, newSuffix);
+            if ((!excludeSource.empty() && excludeSource != source->getName()) ||
+                (newSuffix == "after" && m_prepared.find(source->getName()) == m_prepared.end())) {
+                continue;
+            }
+
+            // dump only if not done before or changed
+            if (m_intro != intro) {
+                cout << intro;
+                m_intro = intro;
+            }
+
+            string oldDir;
+            if (oldSession.empty()) {
+                // Now look for the latest session involving the current source,
+                // starting with the most recent one.
+                for (vector<string>::const_reverse_iterator it = dirs.rbegin();
+                     it != dirs.rend();
+                     ++it) {
+                    const string &sessiondir = *it;
+                    LogDir oldsession(m_client);
+                    oldsession.openLogdir(sessiondir);
+                    SyncReport report;
+                    oldsession.readReport(report);
+                    if (report.find(source->getName()) != report.end())  {
+                        // source was active in that session, use dump
+                        // made there
+                        oldDir = databaseName(*source, oldSuffix, sessiondir);
+                        break;
+                    }
+                }
+            } else {
+                oldDir = databaseName(*source, oldSuffix, oldSession);
+            }
+            string newDir = databaseName(*source, newSuffix);
             cout << "*** " << source->getName() << " ***\n" << flush;
             string cmd = string("env CLIENT_TEST_COMPARISON_FAILED=10 " + config + " synccompare 2>/dev/null '" ) +
-                oldFile + "' '" + newFile + "'";
+                oldDir + "' '" + newDir + "'";
             int ret = system(cmd.c_str());
             switch (ret == -1 ? ret : WEXITSTATUS(ret)) {
             case 0:
@@ -847,15 +1211,14 @@ public:
 
     // call when all sync sources are ready to dump
     // pre-sync databases
-    void syncPrepare() {
+    // @param excludeSource   when non-empty, limit preparation to that source
+    void syncPrepare(const string &excludeSource = "") {
         if (m_logdir.getLogfile().size() &&
             m_doLogging) {
             // dump initial databases
-            dumpDatabases("before", &SyncSourceReport::m_backupBefore);
+            dumpDatabases("before", &SyncSourceReport::m_backupBefore, excludeSource);
             // compare against the old "after" database dump
-            dumpLocalChanges(getPrevLogdir(), "after", "before");
-
-            m_prepared = true;
+            dumpLocalChanges("", "after", "before", excludeSource);
         }
     }
 
@@ -870,12 +1233,14 @@ public:
 
         if (m_doLogging) {
             // dump database after sync, but not if already dumping it at the beginning didn't complete
-            if (m_reportTodo && m_prepared) {
+            if (m_reportTodo && !m_prepared.empty()) {
                 try {
                     dumpDatabases("after", &SyncSourceReport::m_backupAfter);
                 } catch (...) {
                     Exception::handle();
-                    m_prepared = false;
+                    // not exactly sure what the problem was, but don't
+                    // try it again
+                    m_prepared.clear();
                 }
                 if (report) {
                     // update report with more recent information about m_backupAfter
@@ -918,26 +1283,12 @@ public:
                 }
 
                 // compare databases?
-                if (m_logLevel > LOGGING_SUMMARY && m_prepared) {
-                    cout << "\nChanges applied to client during synchronization:\n";
-                    BOOST_FOREACH(SyncSource *source, *this) {
-                        cout << "*** " << source->getName() << " ***\n" << flush;
-
-                        string before = databaseName(*source, "before");
-                        string after = databaseName(*source, "after");
-                        string cmd = string("synccompare '" ) +
-                            before + "' '" + after +
-                            "' && echo 'no changes'";
-                        if (system(cmd.c_str())) {
-                            // ignore error
-                        }
-                    }
-                    cout << "\n";
-                }
+                dumpLocalChanges(m_logdir.getLogdir(),
+                                 "before", "after", "",
+                                 "\nData modified locally during synchronization:\n",
+                                 "CLIENT_TEST_LEFT_NAME='before sync' CLIENT_TEST_RIGHT_NAME='after sync' CLIENT_TEST_REMOVED='removed during sync' CLIENT_TEST_ADDED='added during sync'");
 
-                if (status == STATUS_OK) {
-                    m_logdir.expire();
-                }
+                m_logdir.expire();
             }
         }
     }
@@ -966,18 +1317,35 @@ public:
         }
     }
 
-    /** find sync source by name */
+    /** find sync source by name (both normal and virtual sources) */
     SyncSource *operator [] (const string &name) {
         BOOST_FOREACH(SyncSource *source, *this) {
             if (name == source->getName()) {
                 return source;
             }
         }
+        BOOST_FOREACH(boost::shared_ptr<VirtualSyncSource> &source, m_virtualSources) {
+            if (name == source->getName()) {
+                return source.get();
+            }
+        }
         return NULL;
     }
 
-    /** find by index */
-    SyncSource *operator [] (int index) { return vector<SyncSource *>::operator [] (index); }
+    /** find by XML <dbtypeid> (the ID used by Synthesis to identify sources in progress events) */
+    SyncSource *lookupBySynthesisID(int synthesisid) {
+        BOOST_FOREACH(SyncSource *source, *this) {
+            if (source->getSynthesisID() == synthesisid) {
+                return source;
+            }
+        }
+        BOOST_FOREACH(boost::shared_ptr<VirtualSyncSource> &source, m_virtualSources) {
+            if (source->getSynthesisID() == synthesisid) {
+                return source.get();
+            }
+        }
+        return NULL;
+    }
 };
 
 void unref(SourceList *sourceList)
@@ -1005,24 +1373,47 @@ string SyncContext::askPassword(const string &passwordName, const string &descr,
     }
 }
 
-boost::shared_ptr<TransportAgent> SyncContext::createTransportAgent()
+string SyncContext::getUsedSyncURL() {
+    vector<string> urls = getSyncURL();
+    BOOST_FOREACH (string url, urls) {
+        if (boost::starts_with(url, "http://") ||
+                boost::starts_with(url, "https://")) {
+#ifdef ENABLE_LIBSOUP
+            return url;
+#elif defined(ENABLE_LIBCURL)
+            return url;
+#endif
+        } else if (url.find("obex-bt://") ==0) {
+#ifdef ENABLE_BLUETOOTH
+            return url;
+#endif
+        }
+    }
+    return "";
+}
+
+boost::shared_ptr<TransportAgent> SyncContext::createTransportAgent(void *gmainloop)
 {
-    std::string url = getSyncURL();
+    string url = getUsedSyncURL();
     if (boost::starts_with(url, "http://") ||
         boost::starts_with(url, "https://")) {
 #ifdef ENABLE_LIBSOUP
-        boost::shared_ptr<SoupTransportAgent> agent(new SoupTransportAgent());
+        
+        boost::shared_ptr<SoupTransportAgent> agent(new SoupTransportAgent(static_cast<GMainLoop *>(gmainloop)));
         agent->setConfig(*this);
         return agent;
 #elif defined(ENABLE_LIBCURL)
-        boost::shared_ptr<CurlTransportAgent> agent(new CurlTransportAgent());
-        agent->setConfig(*this);
-        return agent;
+        if (!gmainloop) {
+            boost::shared_ptr<CurlTransportAgent> agent(new CurlTransportAgent());
+            agent->setConfig(*this);
+            return agent;
+        }
 #endif
     } else if (url.find("obex-bt://") ==0) {
 #ifdef ENABLE_BLUETOOTH
         std::string btUrl = url.substr (strlen ("obex-bt://"), std::string::npos);
-        boost::shared_ptr<ObexTransportAgent> agent(new ObexTransportAgent(ObexTransportAgent::OBEX_BLUETOOTH));
+        boost::shared_ptr<ObexTransportAgent> agent(new ObexTransportAgent(ObexTransportAgent::OBEX_BLUETOOTH,
+                                                                           static_cast<GMainLoop *>(gmainloop)));
         agent->setURL (btUrl);
         agent->connect();
         return agent;
@@ -1051,7 +1442,12 @@ void SyncContext::displaySourceProgress(sysync::TProgressEventEnum type,
     case sysync::PEV_PREPARING:
         /* preparing (e.g. preflight in some clients), extra1=progress, extra2=total */
         /* extra2 might be zero */
-        if (source.getFinalSyncMode() == SYNC_NONE) {
+        /*
+         * At the moment, preparing items doesn't do any real work.
+         * Printing this progress just increases the output and slows
+         * us down. Disabled.
+         */
+        if (true || source.getFinalSyncMode() == SYNC_NONE) {
             // not active, suppress output
         } else if (extra2) {
             SE_LOG_INFO(NULL, NULL, "%s: preparing %d/%d",
@@ -1203,6 +1599,10 @@ void SyncContext::displaySourceProgress(sysync::TProgressEventEnum type,
             break;
         case 0:
             break;
+        case sysync::LOCERR_DATASTORE_ABORT:
+            // this can mean only one thing in SyncEvolution: unexpected slow sync
+            extra1 = STATUS_UNEXPECTED_SLOW_SYNC;
+            // no break!
         default:
             // Printing unknown status codes here is of somewhat questionable value,
             // because even "good" sources will get a bad status when the overall
@@ -1386,7 +1786,16 @@ void SyncContext::startLoopThread()
 
 SyncSource *SyncContext::findSource(const char *name)
 {
-    return m_sourceListPtr ? (*m_sourceListPtr)[name] : NULL;
+    if (!m_sourceListPtr) {
+        return NULL;
+    }
+    const char *realname = strrchr(name, m_findSourceSeparator);
+    if (realname) {
+        realname++;
+    } else {
+        realname = name;
+    }
+    return (*m_sourceListPtr)[realname];
 }
 
 SyncContext *SyncContext::findContext(const char *sessionName)
@@ -1397,6 +1806,7 @@ SyncContext *SyncContext::findContext(const char *sessionName)
 void SyncContext::initSources(SourceList &sourceList)
 {
     list<string> configuredSources = getSyncSources();
+    map<string, string> subSources;
 
     // Phase 1, check all virtual sync soruces
     BOOST_FOREACH(const string &name, configuredSources) {
@@ -1412,30 +1822,33 @@ void SyncContext::initSources(SourceList &sourceList)
                 //sub syncsources here
                 SyncSourceParams params(name, source);
                 boost::shared_ptr<VirtualSyncSource> vSource = boost::shared_ptr<VirtualSyncSource> (new VirtualSyncSource (params));
-                std::string evoSyncSource = vSource->getDatabaseID();
-                bool valid = true;
-                std::vector<std::string> mappedSources = unescapeJoinedString (evoSyncSource, ',');
+                std::vector<std::string> mappedSources = vSource->getMappedSources();
                 BOOST_FOREACH (std::string source, mappedSources) {
                     //check whether the mapped source is really available
                     boost::shared_ptr<PersistentSyncSourceConfig> source_config 
                         = getSyncSourceConfig(source);
                     if (!source_config || !source_config->exists()) {
-                        SE_LOG_ERROR (NULL, NULL, 
-                                "Virtual datasource %s referenced a non-existed datasource %s, check your configuration!",
-                                vSource->getName(), source.c_str());
-                        valid = false;
-                        break;
+                        throwError(StringPrintf("Virtual data source \"%s\" references a nonexistent datasource \"%s\".", name.c_str(), source.c_str()));
                     }
-                }
-                if (valid) {
-                    FilterConfigNode::ConfigFilter vFilter;
-                    vFilter["sync"] = sync;
-                    vFilter["uri"] = sc->getURI();
-                    BOOST_FOREACH (std::string source, mappedSources) {
-                        setConfigFilter (false, source, vFilter);
+                    pair< map<string, string>::iterator, bool > res = subSources.insert(make_pair(source, name));
+                    if (!res.second) {
+                        throwError(StringPrintf("Data source \"%s\" included in the virtual sources \"%s\" and \"%s\". It can only be included in one virtual source at a time.",
+                                                source.c_str(), res.first->second.c_str(), name.c_str()));
                     }
+
+                }
+                FilterConfigNode::ConfigFilter vFilter;
+                vFilter["sync"] = sync;
+                if (!m_serverMode) {
+                    // must set special URI for clients so that
+                    // engine knows about superdatastore and its
+                    // URI
+                    vFilter["uri"] = string("<") + vSource->getName() + ">" + vSource->getURI();
                 }
-                sourceList.m_virtualDS.push_back (vSource);
+                BOOST_FOREACH (std::string source, mappedSources) {
+                    setConfigFilter (false, source, vFilter);
+                }
+                sourceList.addSource(vSource);
             }
         }
     }
@@ -1451,17 +1864,17 @@ void SyncContext::initSources(SourceList &sourceList)
         bool enabled = sync != "disabled";
         if (enabled) {
             if (sourceType.m_backend != "virtual") {
-                string url = getSyncURL();
                 SyncSourceParams params(name,
                         source);
-                SyncSource *syncSource =
-                    SyncSource::createSource(params);
+                cxxptr<SyncSource> syncSource(SyncSource::createSource(params));
                 if (!syncSource) {
                     throwError(name + ": type unknown" );
                 }
-                sourceList.push_back(syncSource);
+                if (subSources.find(name) != subSources.end()) {
+                    syncSource->recordVirtualSource(subSources[name]);
+                }
+                sourceList.addSource(syncSource);
             }
-            sourceList.m_configSpecials[name].m_alias = sc->getURI();
         } else {
             // the Synthesis engine is never going to see this source,
             // therefore we have to mark it as 100% complete and
@@ -1487,6 +1900,16 @@ void SyncContext::initSources(SourceList &sourceList)
     }
 }
 
+void SyncContext::startSourceAccess(SyncSource *source)
+{
+    if (m_serverMode) {
+        // source is active in sync, now open it
+        source->open();
+    }
+    // database dumping is delayed in both client and server
+    m_sourceListPtr->syncPrepare(source->getName());
+}
+
 bool SyncContext::transport_cb (void *udata)
 {
     unsigned int interval = reinterpret_cast<uintptr_t>(udata);
@@ -1505,9 +1928,12 @@ void SyncContext::setTransportCallback(int seconds)
                          seconds);
 }
 
-// XML configuration converted to C string constant
+// XML configuration converted to C string constants
 extern "C" {
-    extern const char *SyncEvolutionXML;
+    // including all known fragments for a client
+    extern const char *SyncEvolutionXMLClient;
+    // the remote rules for a client
+    extern const char *SyncEvolutionXMLClientRules;
 }
 
 void SyncContext::setSyncModes(const std::vector<SyncSource *> &sources,
@@ -1522,23 +1948,117 @@ void SyncContext::setSyncModes(const std::vector<SyncSource *> &sources,
     }
 }
 
-void SyncContext::getConfigTemplateXML(string &xml, string &configname)
+/**
+ * helper class which scans directories for
+ * XML config files
+ */
+class XMLFiles
 {
-    try {
-        configname = "syncclient_sample_config.xml";
-        if (ReadFile(configname, xml)) {
-            return;
-        }
-    } catch (...) {
-        Exception::handle();
-    }
+public:
+    enum Category {
+        MAIN,           /**< files directly under searched directories */
+        DATATYPES,      /**< inside datatypes and datatypes/<mode> */
+        SCRIPTING,      /**< inside scripting and scripting/<mode> */
+        REMOTERULES,    /**< inside remoterules and remoterules/<mode> */
+        MAX_CATEGORY
+    };
+
+    /** search file system for XML config fragments */
+    void scan(const string &mode);
+    /** datatypes, scripts and rules concatenated, empty if none found */
+    string get(Category category);
+    /** main file, typically "syncevolution.xml", empty if not found */
+    string get(const string &file);
+
+    static const string m_syncevolutionXML;
+
+private:
+    /* base name as sort key + full file path, iterating is done in lexical order */
+    StringMap m_files[MAX_CATEGORY];
+
+    /**
+     * scan a specific directory for main files directly inside it
+     * and inside datatypes, scripting, remoterules;
+     * it is not an error when it does not exist or is not a directory
+     */
+    void scanRoot(const string &mode, const string &dir);
 
     /**
-     * @TODO read from config directory
+     * scan a datatypes/scripting/remoterules sub directory,
+     * including the <mode> sub-directory
+     */
+    void scanFragments(const string &mode, const string &dir, Category category);
+
+    /**
+     * add all .xml files to the right hash, overwriting old entries
+     */
+    void addFragments(const string &dir, Category category);
+};
+
+const string XMLFiles::m_syncevolutionXML("syncevolution.xml");
+
+void XMLFiles::scan(const string &mode)
+{
+    const char *dir = getenv("SYNCEVOLUTION_XML_CONFIG_DIR");
+    /*
+     * read either one or the other, so that testing can run without
+     * accidentally reading installed files
      */
+    if (dir) {
+        scanRoot(mode, dir);
+    } else {
+        scanRoot(mode, XML_CONFIG_DIR);
+        scanRoot(mode, SubstEnvironment("${XDG_CONFIG_HOME}/syncevolution-xml"));
+    }
+}
+
+void XMLFiles::scanRoot(const string &mode, const string &dir)
+{
+    addFragments(dir, MAIN);
+    scanFragments(mode, dir + "/scripting", SCRIPTING);
+    scanFragments(mode, dir + "/datatypes", DATATYPES);
+    scanFragments(mode, dir + "/remoterules", REMOTERULES);
+}
+
+void XMLFiles::scanFragments(const string &mode, const string &dir, Category category)
+{
+    addFragments(dir, category);
+    addFragments(dir + "/" + mode, category);
+}
 
-    configname = "builtin XML configuration";
-    xml = SyncEvolutionXML;
+void XMLFiles::addFragments(const string &dir, Category category)
+{
+    if (!isDir(dir)) {
+        return;
+    }
+    ReadDir content(dir);
+    BOOST_FOREACH(const string &file, content) {
+        if (boost::ends_with(file, ".xml")) {
+            m_files[category][file] = dir + "/" + file;
+        }
+    }
+}
+
+string XMLFiles::get(Category category)
+{
+    string res;
+
+    BOOST_FOREACH(const StringPair &entry, m_files[category]) {
+        string content;
+        ReadFile(entry.second, content);
+        res += content;
+    }
+    return res;
+}
+
+string XMLFiles::get(const string &file)
+{
+    string res;
+    StringMap::const_iterator entry = m_files[MAIN].find(file);
+    if (entry != m_files[MAIN].end()) {
+        ReadFile(entry->second, res);
+    }
+    return res;
 }
 
 static void substTag(string &xml, const string &tagname, const string &replacement, bool replaceElement = false)
@@ -1582,9 +2102,39 @@ template <class T> void substTag(string &xml, const string &tagname, const T rep
     substTag(xml, tagname, str.str(), replaceElement);
 }
 
+void SyncContext::getConfigTemplateXML(const string &mode,
+                                       string &xml,
+                                       string &rules,
+                                       string &configname)
+{
+    XMLFiles files;
+
+    files.scan(mode);
+    xml = files.get(files.m_syncevolutionXML);
+    if (xml.empty()) {
+        if (mode != "client") {
+            SE_THROW(files.m_syncevolutionXML + " not found");
+        }
+        configname = "builtin XML configuration";
+        xml = SyncEvolutionXMLClient;
+        rules = SyncEvolutionXMLClientRules;
+    } else {
+        configname = "XML configuration files";
+        rules = files.get(XMLFiles::REMOTERULES);
+        substTag(xml, "datatypes",
+                 files.get(XMLFiles::DATATYPES) +
+                 "    <fieldlists/>\n    <profiles/>\n    <datatypedefs/>\n");
+        substTag(xml, "scripting", files.get(XMLFiles::SCRIPTING));
+    }
+}
+
 void SyncContext::getConfigXML(string &xml, string &configname)
 {
-    getConfigTemplateXML(xml, configname);
+    string rules;
+    getConfigTemplateXML(m_serverMode ? "server" : "client",
+                         xml,
+                         rules,
+                         configname);
 
     string tag;
     size_t index;
@@ -1700,22 +2250,24 @@ void SyncContext::getConfigXML(string &xml, string &configname)
         BOOST_FOREACH(SyncSource *source, *m_sourceListPtr) {
             string fragment;
             source->getDatastoreXML(fragment, fragments);
-            hash = Hash(source->getName()) % INT_MAX;
-
-            /**
-             * @TODO handle hash collisions
-             */
-            if (!hash) {
-                hash = 1;
+            string name;
+
+            // Make sure that sub-datastores do not interfere with the global URI namespace
+            // by adding a <superdatastore>/ prefix. That way we can have a "calendar"
+            // alias for "calendar+todo" without conflicting with the underlying
+            // "calendar", which will be called "calendar+todo/calendar" in the XML config.
+            name = source->getVirtualSource();
+            if (!name.empty()) {
+                name += m_findSourceSeparator;
             }
-            datastores << "    <datastore name='" << source->getName() << "' type='plugin'>\n" <<
-                "      <dbtypeid>" << hash << "</dbtypeid>\n" <<
+            name += source->getName();
+
+            datastores << "    <datastore name='" << name << "' type='plugin'>\n" <<
+                "      <dbtypeid>" << source->getSynthesisID() << "</dbtypeid>\n" <<
                 fragment;
 
             string mode = source->getSync();
-            const struct SourceList::SourceConfigSpecials &special =
-                m_sourceListPtr->m_configSpecials[source->getName()];
-            if (special.m_forceSlow) {
+            if (source->getForceSlowSync()) {
                 // we *want* a slow sync, but couldn't tell the client -> force it server-side
                 datastores << "      <alertscript> FORCESLOWSYNC(); </alertscript>\n";
             } else if (mode != "slow" &&
@@ -1723,27 +2275,27 @@ void SyncContext::getConfigXML(string &xml, string &configname)
                                                         // so a slow sync is acceptable in this case
                        !m_serverMode &&
                        getPreventSlowSync() &&
-                       source->m_backupBefore.getNumItems() != 0) { // check is only relevant if we have local data;
-                                                                    // if no backup was made (-1), better check
+                       (!source->getOperations().m_isEmpty ||    // check is only relevant if we have local data;
+                        !source->getOperations().m_isEmpty())) { // if we cannot check, assume we have data
                 // We are not expecting a slow sync => refuse to execute one.
                 // This is the client check for this, server must be handled
                 // differently (TODO, MB #2416).
                 datastores <<
-                    "      <alertscript><![CDATA[\n"
-                    "INTEGER alertcode;\n"
-                    "alertcode = ALERTCODE();\n"
-                    "if (alertcode == 201) {\n" // SLOWSYNC() cannot be used here, refresh-from-client also sets it
-                    "   DEBUGMESSAGE(\"slow sync not expected by SyncEvolution, disabling datastore\");\n"
-                    "   ABORTDATASTORE(" << STATUS_UNEXPECTED_SLOW_SYNC <<
-                    ");\n"
-                    "   // tell UI to abort instead of sending the next message\n"
-                    "   SETSESSIONVAR(\"delayedabort\", 1);\n"
-                    "}\n"
-                    "]]></alertscript>\n";
+                    "      <datastoreinitscript><![CDATA[\n"
+                    "           if (SLOWSYNC() && ALERTCODE() != 203) {\n" // SLOWSYNC() is true for acceptable refresh-from-client, check for that
+                    "              DEBUGMESSAGE(\"slow sync not expected by SyncEvolution, disabling datastore\");\n"
+                    "              ABORTDATASTORE(" << sysync::LOCERR_DATASTORE_ABORT << ");\n"
+                    "              // tell UI to abort instead of sending the next message\n"
+                    "              SETSESSIONVAR(\"delayedabort\", 1);\n"
+                    "           }\n"
+                    "      ]]></datastoreinitscript>\n";
             }
 
-            if (m_serverMode && !special.m_alias.empty()) {
-                datastores << " <alias name='" << special.m_alias << "'/>";
+            if (m_serverMode) {
+                string uri = source->getURI();
+                if (!uri.empty()) {
+                    datastores << " <alias name='" << uri << "'/>";
+                }
             }
 
             datastores << "    </datastore>\n\n";
@@ -1752,54 +2304,49 @@ void SyncContext::getConfigXML(string &xml, string &configname)
         /*If there is super datastore, add it here*/
         //TODO generate specific superdatastore contents (MB #8753)
         //Now only works for synthesis built-in events+tasks
-        BOOST_FOREACH (boost::shared_ptr<VirtualSyncSource> vSource, m_sourceListPtr->m_virtualDS) {
+        BOOST_FOREACH (boost::shared_ptr<VirtualSyncSource> vSource, m_sourceListPtr->getVirtualSources()) {
             std::string superType = vSource->getSourceType().m_format;
             std::string evoSyncSource = vSource->getDatabaseID();
             std::vector<std::string> mappedSources = unescapeJoinedString (evoSyncSource, ',');
-            //only check the type when user uses forceFormat
-            if (vSource->getSourceType().m_forceFormat) {
-                if (superType.find_last_of (":") != superType.npos) {
-                    superType = superType.substr (superType.find_last_of (":"));
-                }
-                BOOST_FOREACH (std::string source, mappedSources) {
-                    //check the data type
-                    SyncSource *subSource = (*m_sourceListPtr)[source];
-                    std::string subType = subSource->getPeerMimeType();
-                    if (subType.empty()) {
-                        subType = subSource->getSourceType().m_format;
-                        if (subType.find_last_of (":") != subType.npos) {
-                            subType = subType.substr (subType.find_last_of (":"));
-                        }
-                    }
-                    if (superType != subType) {
-                        SE_LOG_WARNING (NULL, NULL, 
-                                "Virtual datasource %s and sub datasource %s has different data format, will use the format in virtual datasource",
-                                vSource->getName(), source.c_str());
-                        break;
-                    }
-                }
 
+            // always check for a consistent config
+            SourceType sourceType = vSource->getSourceType();
+            BOOST_FOREACH (std::string source, mappedSources) {
+                //check the data type
+                SyncSource *subSource = (*m_sourceListPtr)[source];
+                SourceType subType = subSource->getSourceType();
+                if (sourceType.m_format != subType.m_format ||
+                    sourceType.m_forceFormat != subType.m_forceFormat) {
+                    SE_LOG_WARNING(NULL, NULL, 
+                                   "Virtual data source \"%s\" and sub data source \"%s\" have different data format. Will use the format in virtual data source.",
+                                   vSource->getName(), source.c_str());
+                }
             }
 
             if (mappedSources.size() !=2) {
-                vSource->throwError ("virtual data source now only supports events+tasks case");
+                vSource->throwError ("virtual data source currently only supports events+tasks combinations");
             } 
 
-            datastores << "    <superdatastore name= '" << vSource->getName() <<"'> \n";
-            datastores << "      <contains datastore = '" << mappedSources[0] <<"'>\n"
+            string name = vSource->getName();
+            datastores << "    <superdatastore name= '" << name << "'> \n";
+            datastores << "      <contains datastore = '" << name << m_findSourceSeparator << mappedSources[0] <<"'>\n"
                 << "        <dispatchfilter>F.ISEVENT:=1</dispatchfilter>\n"
                 << "        <guidprefix>e</guidprefix>\n"
                 << "      </contains>\n"
-                <<"\n      <contains datastore = '" << mappedSources[1] <<"'>\n"
+                << "\n      <contains datastore = '" << name << m_findSourceSeparator << mappedSources[1] <<"'>\n"
                 << "        <dispatchfilter>F.ISEVENT:=0</dispatchfilter>\n"
                 << "        <guidprefix>t</guidprefix>\n"
                 <<"      </contains>\n" ;
 
+            if (m_serverMode) {
+                string uri = vSource->getURI();
+                if (!uri.empty()) {
+                    datastores << " <alias name='" << uri << "'/>";
+                }
+            }
+
             std::string typesupport;
             typesupport = vSource->getDataTypeSupport();
-            if (typesupport.empty()) {
-                SE_THROW ("datatype format is not set in virtual datasource configuration");
-            } 
             datastores << "      <typesupport>\n"
                 << typesupport 
                 << "      </typesupport>\n";
@@ -1825,9 +2372,9 @@ void SyncContext::getConfigXML(string &xml, string &configname)
 
     substTag(xml, "fieldlists", fragments.m_fieldlists.join(), true);
     substTag(xml, "profiles", fragments.m_profiles.join(), true);
-    substTag(xml, "datatypes", fragments.m_datatypes.join(), true);
+    substTag(xml, "datatypedefs", fragments.m_datatypes.join(), true);
     substTag(xml, "remoterules",
-             string("<remoterule name='EVOLUTION'><deviceid>none - this rule is activated via its name in MAKE/PARSETEXTWITHPROFILE() macro calls</deviceid></remoterule>\n") +
+             rules +
              fragments.m_remoterules.join(),
              true);
 
@@ -1993,7 +2540,8 @@ void SyncContext::initEngine(bool logXML)
                      xml.c_str());
         throw;
     }
-    if (logXML) {
+    if (logXML &&
+        getLogLevel() >= 5) {
         SE_LOG_DEV(NULL, NULL, "Full XML configuration:\n%s", xml.c_str());
     }
 }
@@ -2051,9 +2599,7 @@ SyncMLStatus SyncContext::sync(SyncReport *report)
         sourceList.startSession(getLogDir(),
                                 getMaxLogDirs(),
                                 getLogLevel(),
-                                report,
-                                "client");
-
+                                report);
 
         /* Must detect server or client session before creating the
          * underlying SynthesisEngine 
@@ -2109,21 +2655,20 @@ SyncMLStatus SyncContext::sync(SyncReport *report)
             }
 
             // open each source - failing now is still safe
+            // in clients; in servers we wait until the source
+            // is really needed
             BOOST_FOREACH(SyncSource *source, sourceList) {
                 if (m_serverMode) {
                     source->enableServerMode();
+                } else {
+                    source->open();
                 }
-                source->open();
-            }
 
-            // give derived class also a chance to update the configs
-            prepare(sourceList);
+                // request callback when starting to use source
+                source->addCallback(boost::bind(&SyncContext::startSourceAccess, this, source), &SyncSource::Operations::m_startAccess);
+            }
 
-            // TODO: in server mode don't dump all databases. Wait until
-            // the client is logged in successfully and we know which
-            // sources it needs.
-            // ready to go: dump initial databases and prepare for final report
-            sourceList.syncPrepare();
+            // ready to go
             status = doSync();
         } catch (...) {
             // handle the exception here while the engine (and logging!) is still alive
@@ -2193,7 +2738,7 @@ bool SyncContext::initSAN()
 
     /* For each virtual datasoruce, generate the SAN accoring to it and ignoring
      * sub datasource in the later phase*/
-    BOOST_FOREACH (boost::shared_ptr<VirtualSyncSource> vSource, m_sourceListPtr->m_virtualDS) {
+    BOOST_FOREACH (boost::shared_ptr<VirtualSyncSource> vSource, m_sourceListPtr->getVirtualSources()) {
             std::string evoSyncSource = vSource->getDatabaseID();
             std::string sync = vSource->getSync();
             int mode = StringToSyncMode (sync, true);
@@ -2201,7 +2746,9 @@ bool SyncContext::initSAN()
             BOOST_FOREACH (std::string source, mappedSources) {
                 dataSources.erase (source);
                 if (mode == SYNC_SLOW) {
-                    m_sourceListPtr->m_configSpecials[source].m_forceSlow = true;
+                    // We force a source which the client is not expected to use into slow mode.
+                    // Shouldn't we rather reject attempts to synchronize it?
+                    (*m_sourceListPtr)[source]->setForceSlowSync(true);
                 }
             }
             dataSources.insert (vSource->getName());
@@ -2212,7 +2759,7 @@ bool SyncContext::initSAN()
         string sync = sc->getSync();
         int mode = StringToSyncMode (sync, true);
         if (mode == SYNC_SLOW) {
-            m_sourceListPtr->m_configSpecials[name].m_forceSlow = true;
+            (*m_sourceListPtr)[name]->setForceSlowSync(true);
             mode = SA_SYNC_TWO_WAY;
         }
         if (mode <SYNC_FIRST || mode >SYNC_LAST) {
@@ -2289,8 +2836,8 @@ bool SyncContext::initSAN()
                 return true;
             }
         }
-    } catch (TransportException e) {
-        SE_LOG_ERROR (NULL, NULL, "TransportException while sending SAN package");
+    } catch (...) {
+       throw;
     }
     return false;
 }
@@ -2379,7 +2926,7 @@ SyncMLStatus SyncContext::doSync()
             profile = m_engine.OpenSubkey(profiles, sysync::KEYVAL_ID_NEW_DEFAULT);
         }
          
-        m_engine.SetStrValue(profile, "serverURI", getSyncURL());
+        m_engine.SetStrValue(profile, "serverURI", getUsedSyncURL());
         m_engine.SetStrValue(profile, "serverUser", getUsername());
         m_engine.SetStrValue(profile, "serverPassword", getPassword());
         m_engine.SetInt32Value(profile, "encoding",
@@ -2395,7 +2942,7 @@ SyncMLStatus SyncContext::doSync()
             target;
             target = m_engine.OpenSubkey(targets, sysync::KEYVAL_ID_NEXT, true)) {
             s = m_engine.GetStrValue(target, "dbname");
-            SyncSource *source = (*m_sourceListPtr)[s];
+            SyncSource *source = findSource(s.c_str());
             if (source) {
                 m_engine.SetInt32Value(target, "enabled", 1);
                 int slow = 0;
@@ -2558,10 +3105,14 @@ SyncMLStatus SyncContext::doSync()
 
                 // Catch outgoing message and abort if requested by script.
                 // Report which sources are affected, based on their status code.
-                list<string> sources;
+                set<string> sources;
                 BOOST_FOREACH(SyncSource *source, *m_sourceListPtr) {
                     if (source->getStatus() == STATUS_UNEXPECTED_SLOW_SYNC) {
-                        sources.push_back(source->getName());
+                        string name = source->getVirtualSource();
+                        if (name.empty()) {
+                            name = source->getName();
+                        }
+                        sources.insert(name);
                     }
                 }
                 string explanation = SyncReport::slowSyncExplanation(m_server,
@@ -2620,9 +3171,7 @@ SyncMLStatus SyncContext::doSync()
                         if (!m_serverMode) {
                             // specific for a certain sync source:
                             // find it...
-                            target = m_engine.OpenSubkey(targets, progressInfo.targetID);
-                            s = m_engine.GetStrValue(target, "dbname");
-                            SyncSource *source = (*m_sourceListPtr)[s];
+                            SyncSource *source = m_sourceListPtr->lookupBySynthesisID(progressInfo.targetID);
                             if (source) {
                                 displaySourceProgress(sysync::TProgressEventEnum(progressInfo.eventtype),
                                                       *source,
@@ -2888,7 +3437,7 @@ void SyncContext::status()
     SE_LOG_INFO(NULL, NULL, "Local item changes:\n%s",
                 out.str().c_str());
 
-    sourceList.startSession(getLogDir(), 0, 0, NULL, "status");
+    sourceList.startSession(getLogDir(), 0, 0, NULL);
     LoggerBase::instance().setLevel(Logger::INFO);
     string prevLogdir = sourceList.getPrevLogdir();
     bool found = access(prevLogdir.c_str(), R_OK|X_OK) == 0;
@@ -2897,7 +3446,7 @@ void SyncContext::status()
         try {
             sourceList.setPath(prevLogdir);
             sourceList.dumpDatabases("current", NULL);
-            sourceList.dumpLocalChanges(sourceList.getPrevLogdir(), "after", "current");
+            sourceList.dumpLocalChanges("", "after", "current", "");
         } catch(...) {
             Exception::handle();
         }
@@ -2937,7 +3486,7 @@ static void logRestoreReport(const SyncReport &report, bool dryrun)
     if (!report.empty()) {
         stringstream out;
         report.prettyPrint(out, SyncReport::WITHOUT_SERVER|SyncReport::WITHOUT_CONFLICTS|SyncReport::WITH_TOTAL);
-        SE_LOG_INFO(NULL, NULL, "Item changes %s applied to client during restore:\n%s",
+        SE_LOG_INFO(NULL, NULL, "Item changes %s applied locally during restore:\n%s",
                     dryrun ? "to be" : "that were",
                     out.str().c_str());
         SE_LOG_INFO(NULL, NULL, "The same incremental changes will be applied to the server during the next sync.");
@@ -2993,7 +3542,7 @@ void SyncContext::restore(const string &dirname, RestoreDatabase database)
     }
 
     SourceList sourceList(*this, false);
-    sourceList.startSession(dirname.c_str(), 0, 0, NULL, "restore");
+    sourceList.startSession(dirname.c_str(), 0, 0, NULL);
     LoggerBase::instance().setLevel(Logger::INFO);
     initSources(sourceList);
     BOOST_FOREACH(SyncSource *source, sourceList) {
@@ -3014,8 +3563,8 @@ void SyncContext::restore(const string &dirname, RestoreDatabase database)
 
     if (!m_quiet) {
         sourceList.dumpDatabases("current", NULL);
-        sourceList.dumpLocalChanges(dirname, "current", datadump,
-                                    "Data changes to be applied to local data during restore:\n",
+        sourceList.dumpLocalChanges(dirname, "current", datadump, "",
+                                    "Data changes to be applied locally during restore:\n",
                                     "CLIENT_TEST_LEFT_NAME='current data' "
                                     "CLIENT_TEST_REMOVED='after restore' " 
                                     "CLIENT_TEST_REMOVED='to be removed' "
@@ -3050,7 +3599,7 @@ void SyncContext::restore(const string &dirname, RestoreDatabase database)
 void SyncContext::getSessions(vector<string> &dirs)
 {
     LogDir logging(*this);
-    logging.previousLogdirs(getLogDir(), dirs);
+    logging.previousLogdirs(dirs);
 }
 
 string SyncContext::readSessionInfo(const string &dir, SyncReport &report)
@@ -3061,4 +3610,436 @@ string SyncContext::readSessionInfo(const string &dir, SyncReport &report)
     return logging.getPeerNameFromLogdir(dir);
 }
 
+#ifdef ENABLE_UNIT_TESTS
+/**
+ * This class works LogDirTest as scratch directory.
+ * LogDirTest/[ical20|vcard30]_[one|two|empty] contain different
+ * sets of items for use in a FileSyncSource.
+ *
+ * With that setup and a fake SyncContext it is possible to simulate
+ * sessions and test the resulting logdirs.
+ */
+class LogDirTest : public CppUnit::TestFixture, private SyncContext
+{
+public:
+    LogDirTest() :
+        SyncContext("nosuchconfig@nosuchcontext"),
+        m_maxLogDirs(10)
+    {}
+
+    void setUp() {
+        static const char *vcard_1 =
+            "BEGIN:VCARD\n"
+            "VERSION:2.1\n"
+            "TITLE:tester\n"
+            "FN:John Doe\n"
+            "N:Doe;John;;;\n"
+            "X-MOZILLA-HTML:FALSE\n"
+            "TEL;TYPE=WORK;TYPE=VOICE:business 1\n"
+            "EMAIL:john.doe@work.com\n"
+            "X-AIM:AIM JOHN\n"
+            "END:VCARD\n";
+        static const char *vcard_2 =
+            "BEGIN:VCARD\n"
+            "VERSION:2.1\n"
+            "TITLE:developer\n"
+            "FN:John Doe\n"
+            "N:Doe;John;;;\n"
+            "X-MOZILLA-HTML:TRUE\n"
+            "BDAY:2006-01-08\n"
+            "END:VCARD\n";
+        static const char *ical_1 =
+            "BEGIN:VCALENDAR\n"
+            "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
+            "VERSION:2.0\n"
+            "METHOD:PUBLISH\n"
+            "BEGIN:VEVENT\n"
+            "SUMMARY:phone meeting\n"
+            "DTEND:20060406T163000Z\n"
+            "DTSTART:20060406T160000Z\n"
+            "UID:1234567890!@#$%^&*()<>@dummy\n"
+            "DTSTAMP:20060406T211449Z\n"
+            "LAST-MODIFIED:20060409T213201\n"
+            "CREATED:20060409T213201\n"
+            "LOCATION:calling from home\n"
+            "DESCRIPTION:let's talk\n"
+            "CLASS:PUBLIC\n"
+            "TRANSP:OPAQUE\n"
+            "SEQUENCE:1\n"
+            "BEGIN:VALARM\n"
+            "DESCRIPTION:alarm\n"
+            "ACTION:DISPLAY\n"
+            "TRIGGER;VALUE=DURATION;RELATED=START:-PT15M\n"
+            "END:VALARM\n"
+            "END:VEVENT\n"
+            "END:VCALENDAR\n";
+        static const char *ical_2 =
+            "BEGIN:VCALENDAR\n"
+            "PRODID:-//Ximian//NONSGML Evolution Calendar//EN\n"
+            "VERSION:2.0\n"
+            "METHOD:PUBLISH\n"
+            "BEGIN:VEVENT\n"
+            "SUMMARY:phone meeting\n"
+            "DTEND:20060406T163000Z\n"
+            "DTSTART:20060406T160000Z\n"
+            "UID:1234567890!@#$%^&*()<>@dummy\n"
+            "DTSTAMP:20060406T211449Z\n"
+            "LAST-MODIFIED:20060409T213201\n"
+            "CREATED:20060409T213201\n"
+            "LOCATION:my office\n"
+            "CATEGORIES:WORK\n"
+            "DESCRIPTION:what the heck\\, let's even shout a bit\n"
+            "CLASS:PUBLIC\n"
+            "TRANSP:OPAQUE\n"
+            "SEQUENCE:1\n"
+            "END:VEVENT\n"
+            "END:VCALENDAR\n";
+        rm_r("LogDirTest");
+        dump("ical20.one", "1", ical_1);
+        dump("ical20.two", "1", ical_1);
+        dump("ical20.two", "2", ical_2);
+        mkdir_p(getLogData() + "/ical20.empty");
+        dump("vcard30.one", "1", vcard_1);
+        dump("vcard30.two", "1", vcard_1);
+        dump("vcard30.two", "2", vcard_2);
+        mkdir_p(getLogData() + "/vcard30.empty");
+
+        mkdir_p(getLogDir());
+        m_maxLogDirs = 0;
+    }
+
+private:
+
+    string getLogData() { return "LogDirTest/data"; }
+    virtual const char *getLogDir() { return "LogDirTest/cache/syncevolution"; }
+    int m_maxLogDirs;
+
+    void dump(const char *dir, const char *file, const char *data) {
+        string name = getLogData();
+        name += "/";
+        name += dir;
+        mkdir_p(name);
+        name += "/";
+        name += file;
+        ofstream out(name.c_str());
+        out << data;
+    }
+
+    CPPUNIT_TEST_SUITE(LogDirTest);
+    CPPUNIT_TEST(testQuickCompare);
+    CPPUNIT_TEST(testSessionNoChanges);
+    CPPUNIT_TEST(testSessionChanges);
+    CPPUNIT_TEST(testMultipleSessions);
+    CPPUNIT_TEST(testExpire);
+    CPPUNIT_TEST(testExpire2);
+    CPPUNIT_TEST_SUITE_END();
+
+    /**
+     * Simulate a session involving one or more sources.
+     *
+     * @param changeServer   pretend that peer got changed
+     * @param status         result of session
+     * @param varargs        sourcename ("ical20"),
+     *                       statebefore (NULL for no dump, or suffix like "_one"),
+     *                       stateafter (NULL for same as before), ..., NULL
+     * @return logdir created for the session
+     */
+    string session(bool changeServer, SyncMLStatus status, ...) {
+        SourceList list(*this, true);
+        list.setLogLevel(SourceList::LOGGING_QUIET);
+        SyncReport report;
+        list.startSession(NULL, m_maxLogDirs, 0, &report);
+        va_list ap;
+        va_start(ap, status);
+        while (true) {
+            const char *sourcename = va_arg(ap, const char *);
+            if (!sourcename) {
+                break;
+            }
+            const char *type = NULL;
+            if (!strcmp(sourcename, "ical20")) {
+                type = "file:text/calendar:2.0";
+            } else if (!strcmp(sourcename, "vcard30")) {
+                type = "file:text/vcard:3.0";
+            }
+            CPPUNIT_ASSERT(type);
+            string datadir = getLogData() + "/";
+            cxxptr<SyncSource> source(SyncSource::createTestingSource(sourcename, type, true,
+                                                                      (string("file://") + datadir).c_str()));
+            datadir += sourcename;
+            datadir += "_1";
+            source->open();
+            if (changeServer) {
+                // fake one added item on server
+                source->setItemStat(SyncSourceReport::ITEM_REMOTE,
+                                    SyncSourceReport::ITEM_ADDED,
+                                    SyncSourceReport::ITEM_TOTAL,
+                                    1);
+            }
+            list.addSource(source);
+            const char *before = va_arg(ap, const char *);
+            const char *after = va_arg(ap, const char *);
+            if (before) {
+                // do a "before" dump after directing the source towards the desired data
+                rm_r(datadir);
+                CPPUNIT_ASSERT_EQUAL(0, symlink((string(sourcename) + before).c_str(),
+                                                datadir.c_str()));
+                list.syncPrepare(sourcename);
+                if (after) {
+                    rm_r(datadir);
+                    CPPUNIT_ASSERT_EQUAL(0, symlink((string(sourcename) + after).c_str(),
+                                                    datadir.c_str()));
+                }
+            }
+        }
+        list.syncDone(status, &report);
+
+        return list.getLogdir();
+    }
+
+    typedef vector<string> Sessions_t;
+    // full paths to all sessions, sorted
+    Sessions_t listSessions() {
+        Sessions_t sessions;
+        string logdir = getLogDir();
+        ReadDir dirs(logdir);
+        BOOST_FOREACH(const string &dir, dirs) {
+            sessions.push_back(logdir + "/" + dir);
+        }
+        sort(sessions.begin(), sessions.end());
+        return sessions;
+    }
+
+    void testQuickCompare() {
+        // identical dirs => identical files
+        CPPUNIT_ASSERT(!LogDir::haveDifferentContent("ical20",
+                                                     getLogData(), "empty",
+                                                     getLogData(), "empty"));
+        CPPUNIT_ASSERT(!LogDir::haveDifferentContent("ical20",
+                                                     getLogData(), "one",
+                                                     getLogData(), "one"));
+        CPPUNIT_ASSERT(!LogDir::haveDifferentContent("ical20",
+                                                     getLogData(), "two",
+                                                     getLogData(), "two"));
+        // some files shared
+        CPPUNIT_ASSERT(!system("cp -l -r LogDirTest/data/ical20.two LogDirTest/data/ical20.copy && rm LogDirTest/data/ical20.copy/2"));
+        CPPUNIT_ASSERT(LogDir::haveDifferentContent("ical20",
+                                                    getLogData(), "two",
+                                                    getLogData(), "copy"));
+        CPPUNIT_ASSERT(LogDir::haveDifferentContent("ical20",
+                                                    getLogData(), "copy",
+                                                    getLogData(), "one"));
+    }
+
+    void testSessionNoChanges() {
+        ScopedEnvChange config("XDG_CONFIG_HOME", "LogDirTest/config");
+        ScopedEnvChange cache("XDG_CACHE_HOME", "LogDirTest/cache");
+
+        // simple session with no changes
+        string dir = session(false, STATUS_OK, "ical20", ".one", ".one", (char *)0);
+        Sessions_t sessions = listSessions();
+        CPPUNIT_ASSERT_EQUAL((size_t)1, sessions.size());
+        CPPUNIT_ASSERT_EQUAL(dir, sessions[0]);
+        FileConfigNode status(dir, "status.ini", true);
+        CPPUNIT_ASSERT(status.exists());
+        CPPUNIT_ASSERT_EQUAL(string("1"), status.readProperty("source-ical20-backup-before"));
+        CPPUNIT_ASSERT_EQUAL(string("1"), status.readProperty("source-ical20-backup-after"));
+        CPPUNIT_ASSERT_EQUAL(string("200"), status.readProperty("status"));
+        CPPUNIT_ASSERT(!LogDir::haveDifferentContent("ical20",
+                                                     dir, "before",
+                                                     dir, "after"));
+    }
+
+    void testSessionChanges() {
+        ScopedEnvChange config("XDG_CONFIG_HOME", "LogDirTest/config");
+        ScopedEnvChange cache("XDG_CACHE_HOME", "LogDirTest/cache");
+
+        // session with local changes
+        string dir = session(false, STATUS_OK, "ical20", ".one", ".two", (char *)0);
+        Sessions_t sessions = listSessions();
+        CPPUNIT_ASSERT_EQUAL((size_t)1, sessions.size());
+        CPPUNIT_ASSERT_EQUAL(dir, sessions[0]);
+        FileConfigNode status(dir, "status.ini", true);
+        CPPUNIT_ASSERT(status.exists());
+        CPPUNIT_ASSERT_EQUAL(string("1"), status.readProperty("source-ical20-backup-before"));
+        CPPUNIT_ASSERT_EQUAL(string("2"), status.readProperty("source-ical20-backup-after"));
+        CPPUNIT_ASSERT_EQUAL(string("200"), status.readProperty("status"));
+        CPPUNIT_ASSERT(LogDir::haveDifferentContent("ical20",
+                                                    dir, "before",
+                                                    dir, "after"));
+    }
+
+    void testMultipleSessions() {
+        ScopedEnvChange config("XDG_CONFIG_HOME", "LogDirTest/config");
+        ScopedEnvChange cache("XDG_CACHE_HOME", "LogDirTest/cache");
+
+        // two sessions, starting with 1 item, adding 1 during the sync, then
+        // removing it again during the second
+        string dir = session(false, STATUS_OK,
+                             "ical20", ".one", ".two",
+                             "vcard30", ".one", ".two",
+                             (char *)0);
+        {
+            Sessions_t sessions = listSessions();
+            CPPUNIT_ASSERT_EQUAL((size_t)1, sessions.size());
+            CPPUNIT_ASSERT_EQUAL(dir, sessions[0]);
+            FileConfigNode status(dir, "status.ini", true);
+            CPPUNIT_ASSERT(status.exists());
+            CPPUNIT_ASSERT_EQUAL(string("1"), status.readProperty("source-ical20-backup-before"));
+            CPPUNIT_ASSERT_EQUAL(string("2"), status.readProperty("source-ical20-backup-after"));
+            CPPUNIT_ASSERT_EQUAL(string("1"), status.readProperty("source-vcard30-backup-before"));
+            CPPUNIT_ASSERT_EQUAL(string("2"), status.readProperty("source-vcard30-backup-after"));
+            CPPUNIT_ASSERT_EQUAL(string("200"), status.readProperty("status"));
+            CPPUNIT_ASSERT(LogDir::haveDifferentContent("ical20",
+                                                        dir, "before",
+                                                        dir, "after"));
+            CPPUNIT_ASSERT(LogDir::haveDifferentContent("vcard30",
+                                                        dir, "before",
+                                                        dir, "after"));
+        }
+
+        string seconddir = session(false, STATUS_OK,
+                                   "ical20", ".two", ".one",
+                                   "vcard30", ".two", ".one",
+                                   (char *)0);
+        {
+            Sessions_t sessions = listSessions();
+            CPPUNIT_ASSERT_EQUAL((size_t)2, sessions.size());
+            CPPUNIT_ASSERT_EQUAL(dir, sessions[0]);
+            CPPUNIT_ASSERT_EQUAL(seconddir, sessions[1]);
+            FileConfigNode status(seconddir, "status.ini", true);
+            CPPUNIT_ASSERT(status.exists());
+            CPPUNIT_ASSERT_EQUAL(string("2"), status.readProperty("source-ical20-backup-before"));
+            CPPUNIT_ASSERT_EQUAL(string("1"), status.readProperty("source-ical20-backup-after"));
+            CPPUNIT_ASSERT_EQUAL(string("2"), status.readProperty("source-vcard30-backup-before"));
+            CPPUNIT_ASSERT_EQUAL(string("1"), status.readProperty("source-vcard30-backup-after"));
+            CPPUNIT_ASSERT_EQUAL(string("200"), status.readProperty("status"));
+            CPPUNIT_ASSERT(LogDir::haveDifferentContent("ical20",
+                                                        seconddir, "before",
+                                                        seconddir, "after"));
+            CPPUNIT_ASSERT(LogDir::haveDifferentContent("vcard30",
+                                                        seconddir, "before",
+                                                        seconddir, "after"));
+        }
+
+        CPPUNIT_ASSERT(!LogDir::haveDifferentContent("ical20",
+                                                     dir, "after",
+                                                     seconddir, "before"));
+        CPPUNIT_ASSERT(!LogDir::haveDifferentContent("vcard30",
+                                                     dir, "after",
+                                                     seconddir, "before"));
+    }
+
+    void testExpire() {
+        ScopedEnvChange config("XDG_CONFIG_HOME", "LogDirTest/config");
+        ScopedEnvChange cache("XDG_CACHE_HOME", "LogDirTest/cache");
+
+        string dirs[5];
+        Sessions_t sessions;
+
+        m_maxLogDirs = 1;
+
+        // The latest session always must be preserved, even if it
+        // is normally considered less important (no error in this case).
+        dirs[0] = session(false, STATUS_FATAL, (char *)0);
+        dirs[0] = session(false, STATUS_OK, (char *)0);
+        sessions = listSessions();
+        CPPUNIT_ASSERT_EQUAL((size_t)1, sessions.size());
+        CPPUNIT_ASSERT_EQUAL(dirs[0], sessions[0]);
+
+        // all things being equal, then expire the oldest session,
+        // leaving us with two here
+        m_maxLogDirs = 2;
+        dirs[0] = session(false, STATUS_OK, (char *)0);
+        dirs[1] = session(false, STATUS_OK, (char *)0);
+        sessions = listSessions();
+        CPPUNIT_ASSERT_EQUAL((size_t)2, sessions.size());
+        CPPUNIT_ASSERT_EQUAL(dirs[0], sessions[0]);
+        CPPUNIT_ASSERT_EQUAL(dirs[1], sessions[1]);
+
+        // When syncing first ical20, then vcard30, both sessions
+        // must be preserved despite m_maxLogDirs = 1, otherwise
+        // we would loose the only existent backup of ical20.
+        dirs[0] = session(false, STATUS_OK, "ical20", ".two", ".one", (char *)0);
+        dirs[1] = session(false, STATUS_OK, "vcard30", ".two", ".one", (char *)0);
+        sessions = listSessions();
+        CPPUNIT_ASSERT_EQUAL((size_t)2, sessions.size());
+        CPPUNIT_ASSERT_EQUAL(dirs[0], sessions[0]);
+        CPPUNIT_ASSERT_EQUAL(dirs[1], sessions[1]);
+
+        // after synchronizing both, we can expire both the old sessions
+        m_maxLogDirs = 1;
+        dirs[0] = session(false, STATUS_OK,
+                          "ical20", ".two", ".one",
+                          "vcard30", ".two", ".one",
+                          (char *)0);
+        sessions = listSessions();
+        CPPUNIT_ASSERT_EQUAL((size_t)1, sessions.size());
+        CPPUNIT_ASSERT_EQUAL(dirs[0], sessions[0]);
+    }
+
+    void testExpire2() {
+        ScopedEnvChange config("XDG_CONFIG_HOME", "LogDirTest/config");
+        ScopedEnvChange cache("XDG_CACHE_HOME", "LogDirTest/cache");
+        string dirs[5];
+        Sessions_t sessions;
+
+        // Have to restart with clean log dir because our sequence counter
+        // runs over after 10 sessions. Continue where testExpire() stopped.
+        dirs[0] = session(false, STATUS_OK,
+                          "ical20", ".two", ".one",
+                          "vcard30", ".two", ".one",
+                          (char *)0);
+
+        // when doing multiple failed syncs without dumps, keep the sessions
+        // which have database dumps
+        m_maxLogDirs = 2;
+        dirs[1] = session(false, STATUS_FATAL, (char *)0);
+        dirs[1] = session(false, STATUS_FATAL, (char *)0);
+        sessions = listSessions();
+        CPPUNIT_ASSERT_EQUAL((size_t)2, sessions.size());
+        CPPUNIT_ASSERT_EQUAL(dirs[0], sessions[0]);
+        CPPUNIT_ASSERT_EQUAL(dirs[1], sessions[1]);
+
+        // when doing syncs which don't change data, keep the sessions which
+        // did change something: keep oldest backup because it created the
+        // backups for the first time
+        dirs[1] = session(false, STATUS_OK,
+                          "ical20", ".one", ".one",
+                          "vcard30", ".one", ".one",
+                          (char *)0);
+        dirs[1] = session(false, STATUS_OK,
+                          "ical20", ".one", ".one",
+                          "vcard30", ".one", ".one",
+                          (char *)0);
+        sessions = listSessions();
+        CPPUNIT_ASSERT_EQUAL((size_t)2, sessions.size());
+        CPPUNIT_ASSERT_EQUAL(dirs[0], sessions[0]);
+        CPPUNIT_ASSERT_EQUAL(dirs[1], sessions[1]);
+
+        // when making a change in each sync, we end up with the two
+        // most recent sessions eventually: first change server,
+        // then local
+        dirs[1] = session(true, STATUS_OK,
+                          "ical20", ".one", ".one",
+                          "vcard30", ".one", ".one",
+                          (char *)0);
+        sessions = listSessions();
+        CPPUNIT_ASSERT_EQUAL((size_t)2, sessions.size());
+        CPPUNIT_ASSERT_EQUAL(dirs[0], sessions[0]);
+        CPPUNIT_ASSERT_EQUAL(dirs[1], sessions[1]);
+        dirs[0] = dirs[1];
+        dirs[1] = session(false, STATUS_OK,
+                          "ical20", ".one", ".two",
+                          "vcard30", ".one", ".two",
+                          (char *)0);
+        sessions = listSessions();
+        CPPUNIT_ASSERT_EQUAL((size_t)2, sessions.size());
+        CPPUNIT_ASSERT_EQUAL(dirs[0], sessions[0]);
+        CPPUNIT_ASSERT_EQUAL(dirs[1], sessions[1]);
+    }
+};
+SYNCEVOLUTION_TEST_SUITE_REGISTRATION(LogDirTest);
+#endif // ENABLE_UNIT_TESTS
+
 SE_END_CXX
index 4bffe5f..8929785 100644 (file)
@@ -153,6 +153,11 @@ class SyncContext : public SyncConfig, public ConfigUserInterface {
         }
     };
 
+    /*
+     * The URL this SyncContext is actually using, since we may support multiple
+     * urls in the configuration.
+     * */
+    string m_usedSyncURL;
   public:
     /**
      * SyncContext using a volatile config
@@ -344,10 +349,14 @@ class SyncContext : public SyncConfig, public ConfigUserInterface {
      * session. Called by Synthesis DB plugin to find active
      * sources.
      *
+     * @param name     can be both <SyncSource::getName()> as well as <prefix>_<SyncSource::getName()>
+     *                 (necessary when renaming sources in the Synthesis XML config)
+     *
      * @TODO: roll SourceList into SyncContext and
      * make this non-static
      */
     static SyncSource *findSource(const char *name);
+    static const char m_findSourceSeparator = '@';
 
     /**
      * Find the active sync context for the given session.
@@ -475,20 +484,38 @@ class SyncContext : public SyncConfig, public ConfigUserInterface {
     /**
      * Return skeleton Synthesis client XML configuration.
      *
-     * If it contains a <datastore/> element, then that element will
-     * be replaced by the configurations of all active sync
-     * sources. Otherwise the configuration is used as-is.
+     * The <scripting/>, <datatypes/>, <clientorserver/> elements (if
+     * present) are replaced by the caller with fragments found in the
+     * file system. When <datatypes> already has content, that content
+     * may contain <fieldlists/>, <profiles/>, <datatypedefs/>, which
+     * will be replaced by definitions gathered from backends.
      *
      * The default implementation of this function takes the configuration from
      * (in this order):
-     * - ./syncevolution.xml
-     * - <server config dir>/syncevolution.xml
-     * - built-in default
+     * - $(XDG_CONFIG_HOME)/syncevolution-xml
+     * - $(datadir)/syncevolution/xml
+     * Files with identical names are read from the first location where they
+     * are found. If $(SYNCEVOLUTION_XML_CONFIG_DIR) is set, then it overrides
+     * the previous two locations.
+     *
+     * The syncevolution.xml file is read from the first place where it is found.
+     * In addition, further .xml files in sub-directories are gathered and get
+     * inserted into the syncevolution.xml template.
      *
+     * If none of these locations has XML configs, then builtin strings are
+     * used as fallback. This only works for mode == "client". Otherwise an
+     * error is thrown.
+     *
+     * @param mode         "client" or "server"
      * @retval xml         is filled with Synthesis client config which may hav <datastore/>
+     * @retval rules       remote rules which the caller needs for <clientorserver/>
      * @retval configname  a string describing where the config came from
      */
-    virtual void getConfigTemplateXML(string &xml, string &configname);
+    virtual void getConfigTemplateXML(const string &mode,
+                                      string &xml,
+                                      string &rules,
+                                      string &configname);
+                                      
 
     /**
      * Return complete Synthesis XML configuration.
@@ -535,15 +562,6 @@ class SyncContext : public SyncConfig, public ConfigUserInterface {
     virtual void prepare() {}
 
     /**
-     * Callback for derived classes: called after setting up the client's
-     * and sources' configuration. Can be used to reconfigure sources before
-     * actually starting the synchronization.
-     *
-     * @param sources   a NULL terminated array of all active sources
-     */
-    virtual void prepare(const std::vector<SyncSource *> &sources) {}
-
-    /**
      * instantiate transport agent
      *
      * Called by engine when it needs to do HTTP POST requests.  The
@@ -558,9 +576,13 @@ class SyncContext : public SyncConfig, public ConfigUserInterface {
      * The default implementation instantiates one of the builtin
      * transport agents, depending on how it was compiled.
      *
+     * @param gmainloop    the GMainLoop to be used by transports, if not NULL;
+     *                     transports not supporting that should not be created;
+     *                     transports will increase the reference count for the loop
      * @return transport agent
      */
-    virtual boost::shared_ptr<TransportAgent> createTransportAgent();
+    virtual boost::shared_ptr<TransportAgent> createTransportAgent(void *gmainloop);
+    virtual boost::shared_ptr<TransportAgent> createTransportAgent() { return createTransportAgent(NULL); }
 
     /**
      * display a text message from the server
@@ -648,6 +670,11 @@ class SyncContext : public SyncConfig, public ConfigUserInterface {
     void initSources(SourceList &sourceList);
 
     /**
+     * called by SynthesDBPlugin in SyncEvolution_StartDataRead()
+     */
+    void startSourceAccess(SyncSource *source);
+
+    /**
      * utility function for status() and getChanges():
      * iterate over sources, check for changes and copy result
      */
@@ -688,6 +715,8 @@ class SyncContext : public SyncConfig, public ConfigUserInterface {
 public:
     static bool transport_cb (void *data);
     void setTransportCallback(int seconds);
+
+    string getUsedSyncURL();
 };
 
 SE_END_CXX
diff --git a/src/syncevo/SyncEvolutionXML.c b/src/syncevo/SyncEvolutionXML.c
deleted file mode 100644 (file)
index dc09129..0000000
+++ /dev/null
@@ -1,1437 +0,0 @@
-const char *SyncEvolutionXML =
-"<?xml version=\"1.0\"?>\n"
-"<!-- SYNTHESIS SYNCML CLIENT Version 3.2 Configuration file -->\n"
-"\n"
-"<sysync_config version=\"1.0\">\n"
-"\n"
-"  <configvar name=\"logpath\" value=\"$(defout_path)\"/>\n"
-"\n"
-"  <!-- this string is output to every session debug logfile to identify the config in use -->\n"
-"  <configidstring>SyncEvolution client config</configidstring>\n"
-"\n"
-"  <!-- information about maximum supported message and object size (in bytes) -->\n"
-"  <maxmsgsize/>\n"
-"  <maxobjsize/>\n"
-"\n"
-"  <!-- information for DevInf -->\n"
-"  <model/>\n"
-"  <manufacturer/>\n"
-"  <hardwareversion/>\n"
-"  <firmwareversion/>\n"
-"  <devicetype/>\n"
-"  <configdate/>\n"
-"\n"
-"  <debug/>\n"
-"\n"
-"  <transport type=\"xpt\">\n"
-"    <!-- allow HTTP 1.1 kepp-alive (multiple request-answer-exchanges in single TCP connection) -->\n"
-"    <keepconnection>true</keepconnection>\n"
-"  </transport>\n"
-"\n"
-"\n"
-"  <scripting>\n"
-"    <looptimeout>5</looptimeout>\n"
-"\n"
-"    <function><![CDATA[\n"
-"      // create a UID\n"
-"      string newuid() {\n"
-"        return \"syuid\" + NUMFORMAT(RANDOM(1000000),6,\"0\") + \".\" + (string)MILLISECONDS(NOW());\n"
-"      }\n"
-"    ]]></function>\n"
-"    <macro name=\"VCARD_BEFOREWRITE_SCRIPT_EVOLUTION\"><![CDATA[\n"
-"      // a wordaround for cellphone in evolution. for incoming contacts, if there is only one CELL,\n"
-"      // strip the HOME or WORK flag from it. Evolution then should show it. */\n"
-"      INTEGER i, wanted, cell_phones;\n"
-"      i = 0;\n"
-"      cell_phones = 0;\n"
-"      while(i < SIZE(TEL_FLAGS)) {\n"
-"        // 0x10 is the flag of 'cell' type of telephone\n"
-"        if(TEL_FLAGS[i] & 0x10) {\n"
-"          cell_phones = cell_phones + 1;\n"
-"          wanted = i;\n"
-"        }\n"
-"        i = i + 1;\n"
-"      }\n"
-"      if(cell_phones == 1) {\n"
-"        TEL_FLAGS[wanted] = 0x10;\n"
-"      }\n"
-"\n"
-"      // Google sends TYPE=WORK and TYPE=HOME when it means\n"
-"      // normal VOICE phone numbers. Add that flag when\n"
-"      // importing into Evolution, because Evolution does not\n"
-"      // display the numbers without VOICE.\n"
-"      i = 0;\n"
-"      while(i < SIZE(TEL_FLAGS)) {\n"
-"        if(TEL_FLAGS[i] == 1 || TEL_FLAGS[i] == 2) {\n"
-"          TEL_FLAGS[i] = TEL_FLAGS[i] | 8;\n"
-"        }\n"
-"        i = i + 1;\n"
-"      }\n"
-"    ]]></macro>\n"
-"    <macro name=\"VCALENDAR_20TO10_PRIORITY_CONVERSION\"><![CDATA[\n"
-"      //vCalendar10 has different interpretation from iCalendar20 in 'priority'.\n"
-"      //see mappings:  \n"
-"      //  Category      vCalendar1.0     iCalendar2.0\n"
-"      //   undefined         0               0\n"
-"      //   high              1             1 ~ 4\n"
-"      //   normal            2               5\n"
-"      //   low               3             6 ~ bigger\n"
-"      if(PRIORITY<5 && PRIORITY>0) {\n"
-"        PRIORITY=1;\n"
-"      }else if(PRIORITY==5){\n"
-"        PRIORITY=2;\n"
-"      }else if(PRIORITY>5){\n"
-"        PRIORITY=3;\n"
-"      } // 0 is undefined and remains unchanged\n"
-"    ]]></macro>\n"
-"    <macro name=\"VCALENDAR_10TO20_PRIORITY_CONVERSION\"><![CDATA[\n"
-"      if(PRIORITY==2) {\n"
-"        PRIORITY=5;\n"
-"      }else if(PRIORITY==3){\n"
-"        PRIORITY=7;\n"
-"      } //others remain unchanged\n"
-"    ]]></macro>\n"
-"    <macro name=\"VCALENDAR10_BEFOREWRITE_SCRIPT\"><![CDATA[\n"
-"      $VCALENDAR_20TO10_PRIORITY_CONVERSION;\n"
-"    ]]></macro>\n"
-"    <macro name=\"VCALENDAR10_AFTERREAD_SCRIPT\"><![CDATA[\n"
-"      $VCALENDAR_10TO20_PRIORITY_CONVERSION;\n"
-"    ]]></macro>\n"
-"\n"
-"    <macro name=\"VCARD_INCOMING_NAMECHANGE_SCRIPT\"><![CDATA[\n"
-"      STRING tmp;\n"
-"      tmp=NORMALIZED(FN);\n"
-"      if (tmp==EMPTY){\n"
-"        tmp=N_FIRST;\n"
-"        if (N_MIDDLE != EMPTY) {\n"
-"          if (tmp != EMPTY) {\n"
-"            tmp = tmp + \" \";\n"
-"          }\n"
-"          tmp = tmp + N_MIDDLE;\n"
-"        }\n"
-"        if (N_LAST != EMPTY) {\n"
-"          if (tmp != EMPTY) {\n"
-"            tmp = tmp + \" \";\n"
-"          }\n"
-"          tmp = tmp + N_LAST;\n"
-"        }\n"
-"        FN = tmp;\n"
-"      }\n"
-"    ]]></macro>\n"
-"\n"
-"              <!-- define script macros for scripts that are used by both vCalendar 1.0 and iCalendar 2.0 -->\n"
-"\n"
-"    <macro name=\"VCALENDAR_INCOMING_SCRIPT\"><![CDATA[\n"
-"      STRING MATCHES[];\n"
-"      STRING CAT,CN,EM;\n"
-"      INTEGER i;\n"
-"      // make sure we have all trailing and leading spaces eliminated\n"
-"      DESCRIPTION=NORMALIZED(DESCRIPTION);\n"
-"      SUMMARY=NORMALIZED(SUMMARY);\n"
-"      // make sure that we have a DESCRIPTION\n"
-"      if (DESCRIPTION==EMPTY) DESCRIPTION=SUMMARY;\n"
-"      // calendar or todo\n"
-"      if (ISEVENT) {\n"
-"        // VEVENT\n"
-"        // - handle duration cases \n"
-"        if (ISDURATION(DURATION)) {\n"
-"          if (DTEND==EMPTY) DTEND = DTSTART + DURATION;\n"
-"          if (DTSTART==EMPTY) DTSTART = DTEND - DURATION;\n"
-"        }\n"
-"        // - detect alldays in vCalendar 1.0 (0:00-0:00 or 23:59 localtime)\n"
-"        i = ALLDAYCOUNT(DTSTART,DTEND,TRUE);\n"
-"        if (ITEMDATATYPE()==\"vCalendar10\" && i>0) {\n"
-"          // DTSTART and DTEND represent allday event, make them date-only values\n"
-"          // - convert start to user zone (or floating) so it represents midnight\n"
-"          DTSTART = CONVERTTOUSERZONE(DTSTART);\n"
-"          MAKEALLDAY(DTSTART,DTEND,i);\n"
-"        }\n"
-"\n"
-"        // Make sure that all EXDATE times are in the same timezone as the start\n"
-"        // time. Some servers send them as UTC, which is all fine and well, but\n"
-"        // only if the timezone definition doesn't change. Also, libical does not\n"
-"        // handle such UTC EXDATEs, so let's convert it while the UTC and\n"
-"        // time zone definition (hopefully) are in sync.\n"
-"        if (TIMEZONE(DTSTART) != \"UTC\" && !ISFLOATING(DTSTART)) {\n"
-"          i = 0;\n"
-"          timestamp exdate;\n"
-"          while (i<SIZE(EXDATES)) {\n"
-"            exdate = EXDATES[i];\n"
-"            if (!ISDATEONLY(exdate) &&\n"
-"                (TIMEZONE(exdate) == \"UTC\" || ISFLOATING(exdate))) {\n"
-"              // \"unfloat\" floating time stamps: not sure whether that occcurs\n"
-"              // in practice, but it looks as wrong as UTC EXDATEs\n"
-"              EXDATES[i] = CONVERTTOZONE(exdate,DTSTART,TRUE);\n"
-"            }\n"
-"            i=i+1;\n"
-"          }\n"
-"        }\n"
-"        // If vcalendar1.0, rrule is not secondly, minutely, or hourly, we strip time information\n"
-"        // and only reserve date information\n"
-"        if (ITEMDATATYPE()==\"vCalendar10\" && RR_FREQ!=\"h\" && RR_FREQ!=\"m\" && RR_FREQ!=\"s\") {\n"
-"          timestamp exdate;\n"
-"          i = 0;\n"
-"          while (i<SIZE(EXDATES)) {\n"
-"            exdate = EXDATES[i];\n"
-"            if (!ISDATEONLY(exdate)) {\n"
-"              EXDATES[i] = DATEONLY(exdate);\n"
-"            }\n"
-"            i=i+1;\n"
-"          }  \n"
-"        }\n"
-"\n"
-"        // - shape attendees (and make sure ATTENDEES[] is assigned even for empty email addresses)\n"
-"        i=0;\n"
-"        while(i<SIZE(ATTENDEES) || i<SIZE(ATTENDEE_CNS)) {\n"
-"          PARSEEMAILSPEC(ATTENDEES[i], CN, EM);\n"
-"          ATTENDEES[i] = EM; // pure email address\n"
-"          // in case we have no specific common name, use the one extracted from the email\n"
-"          // This catches the vCalendar 1.0 case and eventually ill-formed iCalendar 2.0 as well\n"
-"          if (ATTENDEE_CNS[i]==EMPTY)\n"
-"            ATTENDEE_CNS[i]=CN;\n"
-"          // default participation status to needs-action\n"
-"          if (ATTENDEE_PARTSTATS[i]==EMPTY)\n"
-"            ATTENDEE_PARTSTATS[i]=1; // 1=needs action\n"
-"          i=i+1;\n"
-"        }\n"
-"        // - shape organizer\n"
-"        PARSEEMAILSPEC(ORGANIZER, CN, EM);\n"
-"        ORGANIZER = EM; // pure email address\n"
-"        if (ORGANIZER_CN==EMPTY)\n"
-"          ORGANIZER_CN=CN;\n"
-"      }\n"
-"      else {\n"
-"        // VTODO\n"
-"        // - make sure we have at least a summary\n"
-"        if (SUMMARY==EMPTY) SUMMARY=DESCRIPTION; // use description if we don't have a summary\n"
-"        if (SUMMARY==EMPTY) SUMMARY=\"unnamed\"; // set dummy summary if we still don't have one\n"
-"        // due shaping for non-iCalendar 2.0\n"
-"        if (ITEMDATATYPE()==\"vCalendar10\" && ALLDAYCOUNT(DUE,DUE,TRUE,TRUE)>0) {\n"
-"              DUE = DATEONLY(DUE);\n"
-"        }\n"
-"        if (ITEMDATATYPE()==\"vCalendar10\") {\n"
-"          $VCALENDAR_10TO20_PRIORITY_CONVERSION;\n"
-"        }\n"
-"      }\n"
-"      // a workaround for funambol: adding 'action' for 'alarm'\n"
-"      if (ITEMDATATYPE()==\"iCalendar20\") {\n"
-"        if (ALARM_TIME!=EMPTY && ALARM_ACTION==EMPTY) {\n"
-"            ALARM_ACTION = \"DISPLAY\";\n"
-"        }\n"
-"      }\n"
-"    ]]></macro>\n"
-"\n"
-"    <macro name=\"VCALENDAR_OUTGOING_SCRIPT\"><![CDATA[\n"
-"      // set UTC time of generation for iCalendar 2.0 DTSTAMP\n"
-"      DGENERATED = NOW();\n"
-"      // make sure we have all trailing and leading spaces eliminated\n"
-"      DESCRIPTION=NORMALIZED(DESCRIPTION);\n"
-"      SUMMARY=NORMALIZED(SUMMARY);\n"
-"      if (ISEVENT) {\n"
-"        // VEVENT\n"
-"        // - combine attendee email address and common name into single string for vCalendar 1.0\n"
-"        if (ITEMDATATYPE()==\"vCalendar10\") {\n"
-"          i=0;\n"
-"          while(i<SIZE(ATTENDEES)) {\n"
-"            ATTENDEES[i] = MAKEEMAILSPEC(ATTENDEE_CNS[i], ATTENDEES[i]);\n"
-"            i=i+1;\n"
-"          }\n"
-"          ORGANIZER = MAKEEMAILSPEC(ORGANIZER_CN, ORGANIZER);\n"
-"        }\n"
-"      }\n"
-"      else {\n"
-"        // VTODO \n"
-"        // interal representation is iCalendar20\n"
-"        if (ITEMDATATYPE()==\"vCalendar10\") {\n"
-"          $VCALENDAR_20TO10_PRIORITY_CONVERSION;\n"
-"        }\n"
-"      }\n"
-"      // make sure we have at least a summary\n"
-"      if (SUMMARY==EMPTY) SUMMARY=SUBSTR(DESCRIPTION,0,32); // derive from description\n"
-"      if (SUMMARY==EMPTY) SUMMARY=\"unnamed\"; // in case description is empty as well\n"
-"      // make sure that we have a DESCRIPTION\n"
-"      if (DESCRIPTION==EMPTY) DESCRIPTION=SUMMARY;\n"
-"      // do NOT send duration (some servers crash when doing so)\n"
-"      DURATION = UNASSIGNED;\n"
-"      // shape alarm\n"
-"      if (ALARM_TIME!=EMPTY) {\n"
-"        if (ITEMDATATYPE()==\"iCalendar20\") {\n"
-"          if (ALARM_ACTION==EMPTY) ALARM_ACTION = \"AUDIO\";\n"
-"        }\n"
-"        else {\n"
-"          if (ALARM_MSG==EMPTY) ALARM_MSG=\"alarm\";\n"
-"        }\n"
-"      }\n"
-"    ]]></macro>\n"
-"\n"
-"  </scripting>\n"
-"\n"
-"\n"
-"  <datatypes>\n"
-"\n"
-"    <!-- list of internal fields representing vCard data -->\n"
-"    <fieldlist name=\"contacts\">\n"
-"      <field name=\"REV\" type=\"timestamp\" compare=\"never\" age=\"yes\"/>\n"
-"\n"
-"      <!-- Name elements -->\n"
-"      <field name=\"N_LAST\" type=\"string\" compare=\"always\"/>\n"
-"      <field name=\"N_FIRST\" type=\"string\" compare=\"always\"/>\n"
-"      <field name=\"N_MIDDLE\" type=\"string\" compare=\"always\"/>\n"
-"      <field name=\"N_PREFIX\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"N_SUFFIX\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"NICKNAME\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"TITLE\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"\n"
-"      <field name=\"FN\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"      <field name=\"FILE-AS\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"\n"
-"      <field name=\"GENDER\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"\n"
-"      <!-- categories and classification -->\n"
-"      <field name=\"CATEGORIES\" array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"\n"
-"      <!-- organisation -->\n"
-"      <field name=\"ORG_NAME\" type=\"string\" compare=\"slowsync\" merge=\"fillempty\"/>\n"
-"      <field name=\"ORG_DIVISION\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"      <field name=\"ORG_OFFICE\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"      <field name=\"ORG_TEAM\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"      <field name=\"ROLE\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"\n"
-"      <!-- birthday and anniversary (not necessarily the same) -->\n"
-"      <field name=\"BDAY\" type=\"date\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"      <field name=\"ANNIVERSARY\" type=\"date\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"\n"
-"      <!-- telephone numbers -->\n"
-"      <field name=\"TEL\"         array=\"yes\" type=\"telephone\" compare=\"conflict\"/>\n"
-"      <field name=\"TEL_FLAGS\"   array=\"yes\" type=\"integer\"   compare=\"conflict\"/> <!-- offset 0 -->\n"
-"      <field name=\"TEL_LABEL\"   array=\"yes\" type=\"string\"    compare=\"conflict\"/> <!-- offset 1 -->\n"
-"      <field name=\"TEL_ID\"      array=\"yes\" type=\"integer\"   compare=\"conflict\"/> <!-- offset 2 -->\n"
-"      <field name=\"TEL_SLOT\"    array=\"yes\" type=\"integer\"   compare=\"never\"/>    <!-- offset 3 -->\n"
-"\n"
-"      <!-- emails -->\n"
-"      <field name=\"EMAIL\"       array=\"yes\" type=\"multiline\" compare=\"conflict\"/>\n"
-"      <field name=\"EMAIL_FLAGS\" array=\"yes\" type=\"integer\"   compare=\"conflict\"/> <!-- offset 0 -->\n"
-"      <field name=\"EMAIL_LABEL\" array=\"yes\" type=\"string\"    compare=\"conflict\"/> <!-- offset 1 -->\n"
-"      <field name=\"EMAIL_ID\"    array=\"yes\" type=\"integer\"   compare=\"conflict\"/> <!-- offset 2 -->\n"
-"      <field name=\"EMAIL_SLOT\"  array=\"yes\" type=\"integer\"   compare=\"never\"/>    <!-- offset 3 -->\n"
-"\n"
-"      <!-- web addresses -->\n"
-"      <field name=\"WEB\"         array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"WEB_FLAGS\"   array=\"yes\" type=\"integer\"   compare=\"conflict\"/> <!-- offset 0 -->\n"
-"      <field name=\"WEB_LABEL\"   array=\"yes\" type=\"string\"    compare=\"conflict\"/> <!-- offset 1 -->\n"
-"      <field name=\"WEB_ID\"      array=\"yes\" type=\"integer\"   compare=\"conflict\"/> <!-- offset 2 -->\n"
-"\n"
-"      <!-- would be nicer to have as part of WEB, but parser/encoder does not support mapping\n"
-"           with more than one property per field -->\n"
-"      <field name=\"CALURI\"      array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"FBURL\"       array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"BLOGURL\"     array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"VIDEOURL\"    array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"\n"
-"      <!-- related persons: should be turned into array, like WEB and CALURI/FBURL -->\n"
-"      <field name=\"MANAGER\"    type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"ASSISTANT\"  type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"SPOUSE\"     type=\"string\" compare=\"conflict\"/>\n"
-"\n"
-"      <!-- does this person want HTML mails? Valid values are TRUE/FALSE; a \"boolean\"\n"
-"           type would be useful, maybe add that later. -->\n"
-"      <field name=\"WANTS_HTML\"  type=\"string\" compare=\"conflict\"/>\n"
-"\n"
-"      <!-- chat handles: should be turned into one array, like WEB and CALURI/FBURL -->\n"
-"      <field name=\"AIM_HANDLE\"        array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"AIM_SLOT\"          array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"GADUGADU_HANDLE\"        array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"GADUGADU_SLOT\"          array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"GROUPWISE_HANDLE\"        array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"GROUPWISE_SLOT\"          array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"ICQ_HANDLE\"        array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"ICQ_SLOT\"          array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"JABBER_HANDLE\"        array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"JABBER_SLOT\"          array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"MSN_HANDLE\"        array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"MSN_SLOT\"          array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"YAHOO_HANDLE\"        array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"YAHOO_SLOT\"          array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"SKYPE_HANDLE\"      array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"SKYPE_SLOT\"        array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"SIP_HANDLE\"        array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"SIP_SLOT\"          array=\"yes\" type=\"string\" compare=\"conflict\"/>\n"
-"\n"
-"      <!-- home address -->\n"
-"      <field name=\"ADR_STREET\"        array=\"yes\" type=\"multiline\" compare=\"conflict\"/>\n"
-"      <field name=\"ADR_ADDTL\"         array=\"yes\" type=\"multiline\" compare=\"conflict\"/>\n"
-"      <field name=\"ADR_STREET_FLAGS\"  array=\"yes\" type=\"integer\"   compare=\"conflict\"/> <!-- offset 0 (from ADR_STREET_FLAGS) -->\n"
-"      <field name=\"ADR_STREET_LABEL\"  array=\"yes\" type=\"string\"    compare=\"conflict\"/> <!-- offset 1 -->\n"
-"      <field name=\"ADR_STREET_ID\"     array=\"yes\" type=\"integer\"   compare=\"conflict\"/> <!-- offset 2 -->\n"
-"      <field name=\"ADR_POBOX\"         array=\"yes\" type=\"multiline\" compare=\"conflict\"/>\n"
-"      <field name=\"ADR_CITY\"          array=\"yes\" type=\"multiline\" compare=\"conflict\"/>\n"
-"      <field name=\"ADR_REG\"           array=\"yes\" type=\"multiline\" compare=\"conflict\"/>\n"
-"      <field name=\"ADR_ZIP\"           array=\"yes\" type=\"multiline\" compare=\"conflict\"/>\n"
-"      <field name=\"ADR_COUNTRY\"       array=\"yes\" type=\"multiline\" compare=\"conflict\"/>\n"
-"\n"
-"      <!-- Note -->\n"
-"      <field name=\"NOTE\" type=\"multiline\" compare=\"conflict\" merge=\"lines\"/>\n"
-"\n"
-"      <!-- Photo -->\n"
-"      <field name=\"PHOTO\" type=\"blob\" compare=\"never\" merge=\"fillempty\"/>\n"
-"      <field name=\"PHOTO_TYPE\" type=\"integer\" compare=\"never\" merge=\"fillempty\"/>\n"
-"\n"
-"    </fieldlist>\n"
-"\n"
-"    <!-- vCard profile -->\n"
-"    <mimeprofile name=\"vCard\" fieldlist=\"contacts\">\n"
-"\n"
-"      <profile name=\"VCARD\" nummandatory=\"0\"> <!-- we allow records without \"N\" as Address book can store them -->\n"
-"        <property name=\"VERSION\">\n"
-"          <value conversion=\"version\"/>\n"
-"        </property>\n"
-"\n"
-"        <property onlyformode=\"standard\" name=\"PRODID\" mandatory=\"no\">\n"
-"          <value conversion=\"prodid\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"REV\">\n"
-"          <value field=\"REV\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"N\" values=\"5\" mandatory=\"yes\"> <!-- Note: makes N parse and generate even if not in remote's CTCap -->\n"
-"          <value index=\"0\" field=\"N_LAST\"/>\n"
-"          <value index=\"1\" field=\"N_FIRST\"/>\n"
-"          <value index=\"2\" field=\"N_MIDDLE\"/>\n"
-"          <value index=\"3\" field=\"N_PREFIX\"/>\n"
-"          <value index=\"4\" field=\"N_SUFFIX\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"FN\">\n"
-"          <value field=\"FN\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"X-EVOLUTION-FILE-AS\">\n"
-"          <value field=\"FILE-AS\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"X-GENDER\">\n"
-"          <value field=\"GENDER\"/>\n"
-"        </property>\n"
-"\n"
-"        <!-- onlyformode=\"standard\": not part of vCard 2.1, but some\n"
-"             peers (like the Funambol server) accept it anyway in\n"
-"             vCard 2.1 -->\n"
-"        <property name=\"NICKNAME\">\n"
-"          <value field=\"NICKNAME\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"TITLE\">\n"
-"          <value field=\"TITLE\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"CATEGORIES\" values=\"list\" valueseparator=\",\" altvalueseparator=\";\" > <!-- non-standard, but 1:1 as in vCard 3.0 (NOT like in vCalendar 1.0, where separator is \";\") -->\n"
-"          <value field=\"CATEGORIES\"/>\n"
-"          <position field=\"CATEGORIES\" repeat=\"array\" increment=\"1\" minshow=\"0\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"ORG\" values=\"4\">\n"
-"          <value index=\"0\" field=\"ORG_NAME\"/>\n"
-"          <value index=\"1\" field=\"ORG_DIVISION\"/>\n"
-"          <value index=\"2\" field=\"ORG_OFFICE\"/>\n"
-"          <value index=\"3\" field=\"ORG_TEAM\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"ROLE\">\n"
-"          <value field=\"ROLE\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"TEL\">\n"
-"          <value field=\"TEL\"/>\n"
-"          <position field=\"TEL\" repeat=\"array\" increment=\"1\" minshow=\"1\"/>\n"
-"          <parameter name=\"TYPE\" default=\"yes\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"TEL_FLAGS\" conversion=\"multimix\" combine=\",\">\n"
-"              <enum name=\"HOME\"     value=\"B0\"/>\n"
-"              <enum name=\"WORK\"     value=\"B1\"/>\n"
-"              <enum mode=\"ignore\"   value=\"B2\"/> <!-- OTHER -->\n"
-"              <enum name=\"VOICE\"    value=\"B3\"/>\n"
-"              <enum name=\"CELL\"     value=\"B4\"/>\n"
-"              <enum name=\"FAX\"      value=\"B5\"/>\n"
-"              <enum name=\"PAGER\"    value=\"B6\"/>\n"
-"              <enum name=\"PREF\"     value=\"B7\"/>\n"
-"              <enum name=\"CAR\"      value=\"B8\"/>\n"
-"              <enum name=\"X-EVOLUTION-CALLBACK\" value=\"B9\"/>\n"
-"              <enum name=\"X-EVOLUTION-RADIO\" value=\"B10\"/>\n"
-"              <enum name=\"X-EVOLUTION-TELEX\" value=\"B11\"/>\n"
-"              <enum name=\"X-EVOLUTION-TTYTDD\" value=\"B12\"/>\n"
-"\n"
-"              <enum mode=\"prefix\" name=\"X-CustomLabel-\" value=\"1.L\"/>\n"
-"              <enum mode=\"prefix\" name=\"X-Synthesis-Ref\" value=\"2.L\"/>\n"
-"            </value>\n"
-"          </parameter>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"TEL_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"EMAIL\">\n"
-"          <value field=\"EMAIL\"/>\n"
-"          <position field=\"EMAIL\" repeat=\"array\" increment=\"1\" minshow=\"1\"/>\n"
-"          <parameter name=\"TYPE\" default=\"yes\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"EMAIL_FLAGS\" conversion=\"multimix\" combine=\",\">\n"
-"              <enum name=\"HOME\"     value=\"B0\"/>\n"
-"              <enum name=\"WORK\"     value=\"B1\"/>\n"
-"              <enum mode=\"ignore\"   value=\"B2\"/> <!-- OTHER -->\n"
-"              <enum name=\"INTERNET\" value=\"B3\"/>\n"
-"\n"
-"              <enum mode=\"prefix\" name=\"X-CustomLabel-\" value=\"1.L\"/>\n"
-"              <enum mode=\"prefix\" name=\"X-Synthesis-Ref\" value=\"2.L\"/>\n"
-"            </value>\n"
-"          </parameter>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"EMAIL_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"URL\">\n"
-"          <value field=\"WEB\"/>\n"
-"          <position field=\"WEB\" repeat=\"array\" increment=\"1\" minshow=\"1\"/>\n"
-"          <parameter name=\"TYPE\" default=\"yes\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"WEB_FLAGS\" conversion=\"multimix\" combine=\",\">\n"
-"              <enum name=\"HOME\"     value=\"B0\"/>\n"
-"              <enum name=\"WORK\"     value=\"B1\"/>\n"
-"              <enum mode=\"ignore\"   value=\"B2\"/> <!-- OTHER -->\n"
-"              <enum name=\"PREF\"     value=\"B3\"/>\n"
-"\n"
-"              <enum mode=\"prefix\" name=\"X-CustomLabel-\" value=\"1.L\"/>\n"
-"              <enum mode=\"prefix\" name=\"X-Synthesis-Ref\" value=\"2.L\"/>\n"
-"            </value>\n"
-"          </parameter>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"CALURI\" suppressempty=\"yes\">\n"
-"          <value field=\"CALURI\" show=\"yes\"/>\n"
-"        </property>\n"
-"        <property name=\"FBURL\" suppressempty=\"yes\">\n"
-"          <value field=\"FBURL\" show=\"yes\"/>\n"
-"        </property>\n"
-"        <property name=\"X-EVOLUTION-BLOG-URL\" suppressempty=\"yes\">\n"
-"          <value field=\"BLOGURL\" show=\"yes\"/>\n"
-"        </property>\n"
-"        <property name=\"X-EVOLUTION-VIDEO-URL\" suppressempty=\"yes\">\n"
-"          <value field=\"VIDEOURL\" show=\"yes\"/>\n"
-"        </property>\n"
-"\n"
-"        <!-- item for SyncML server: EVOLUTION rule not active,\n"
-"             both X-EVOLUTION-MANAGER and X-MANAGER are sent.\n"
-"\n"
-"             item from SyncML server: EVOLUTION rule not active,\n"
-"             both X-EVOLUTION-MANAGER and X-MANAGER are checked,\n"
-"             but X-EVOLUTION-MANAGER later so that it overwrites\n"
-"             a value set earlier by X-MANAGER (if any). This is\n"
-"             a more or less arbitrary priority, chosen because\n"
-"             servers that know about SyncEvolution (ScheduleWorld,\n"
-"             Memotoo) use the X-EVOLUTION variant.\n"
-"\n"
-"             item to/from Evolution: EVOLUTION rule is active,\n"
-"             only X-EVOLUTION-MANAGER is used. -->\n"
-"        <property name=\"X-EVOLUTION-MANAGER\" suppressempty=\"yes\" delayedparsing=\"1\">\n"
-"          <value field=\"MANAGER\" show=\"yes\"/>\n"
-"        </property>\n"
-"        <property name=\"X-MANAGER\" suppressempty=\"yes\" rule=\"EVOLUTION\"/> <!-- disables the X-MANAGER for EVOLUTION -->\n"
-"        <property name=\"X-MANAGER\" suppressempty=\"yes\" rule=\"other\">\n"
-"          <value field=\"MANAGER\" show=\"yes\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"X-EVOLUTION-ASSISTANT\" suppressempty=\"yes\" delayedparsing=\"1\">\n"
-"          <value field=\"ASSISTANT\" show=\"yes\"/>\n"
-"        </property>\n"
-"        <property name=\"X-ASSISTANT\" suppressempty=\"yes\" rule=\"EVOLUTION\"/>\n"
-"        <property name=\"X-ASSISTANT\" suppressempty=\"yes\" rule=\"other\">\n"
-"          <value field=\"ASSISTANT\" show=\"yes\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"X-EVOLUTION-SPOUSE\" suppressempty=\"yes\" delayedparsing=\"1\">\n"
-"          <value field=\"SPOUSE\" show=\"yes\"/>\n"
-"        </property>\n"
-"        <property name=\"X-SPOUSE\" suppressempty=\"yes\" rule=\"EVOLUTION\"/>\n"
-"        <property name=\"X-SPOUSE\" suppressempty=\"yes\" rule=\"other\">\n"
-"          <value field=\"SPOUSE\" show=\"yes\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"X-EVOLUTION-ANNIVERSARY\" suppressempty=\"yes\" delayedparsing=\"1\">\n"
-"          <value field=\"ANNIVERSARY\" show=\"yes\"/>\n"
-"        </property>\n"
-"        <property name=\"X-ANNIVERSARY\" suppressempty=\"yes\" rule=\"EVOLUTION\"/>\n"
-"        <property name=\"X-ANNIVERSARY\" suppressempty=\"yes\" rule=\"other\">\n"
-"          <value field=\"ANNIVERSARY\" show=\"yes\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"X-MOZILLA-HTML\">\n"
-"          <value field=\"WANTS_HTML\" show=\"yes\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"X-AIM\" suppressempty=\"yes\">\n"
-"          <value field=\"AIM_HANDLE\"/>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"AIM_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"        <property name=\"X-GADUGADU\" suppressempty=\"yes\">\n"
-"          <value field=\"GADUGADU_HANDLE\"/>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"GADUGADU_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"        <property name=\"X-GROUPWISE\" suppressempty=\"yes\">\n"
-"          <value field=\"GROUPWISE_HANDLE\"/>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"GROUPWISE_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"        <property name=\"X-ICQ\" suppressempty=\"yes\">\n"
-"          <value field=\"ICQ_HANDLE\"/>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"ICQ_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"        <property name=\"X-JABBER\" suppressempty=\"yes\">\n"
-"          <value field=\"JABBER_HANDLE\"/>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"JABBER_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"        <property name=\"X-MSN\" suppressempty=\"yes\">\n"
-"          <value field=\"MSN_HANDLE\"/>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"MSN_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"        <property name=\"X-YAHOO\" suppressempty=\"yes\">\n"
-"          <value field=\"YAHOO_HANDLE\"/>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"YAHOO_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"X-SKYPE\" suppressempty=\"yes\"> \n"
-"          <value field=\"SKYPE_HANDLE\"/>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"SKYPE_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"X-SIP\" suppressempty=\"yes\">\n"
-"          <value field=\"SIP_HANDLE\"/>\n"
-"          <parameter name=\"X-EVOLUTION-UI-SLOT\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"SIP_SLOT\"/>\n"
-"          </parameter>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"ADR\" values=\"7\">\n"
-"          <value index=\"0\" field=\"ADR_POBOX\"/>\n"
-"          <value index=\"1\" field=\"ADR_ADDTL\"/>\n"
-"          <value index=\"2\" field=\"ADR_STREET\"/>\n"
-"          <value index=\"3\" field=\"ADR_CITY\"/>\n"
-"          <value index=\"4\" field=\"ADR_REG\"/>\n"
-"          <value index=\"5\" field=\"ADR_ZIP\"/>\n"
-"          <value index=\"6\" field=\"ADR_COUNTRY\"/>\n"
-"          <position field=\"ADR_POBOX\" repeat=\"array\" increment=\"1\" minshow=\"1\"/>\n"
-"          <parameter name=\"TYPE\" default=\"yes\" positional=\"no\" show=\"yes\">\n"
-"            <value field=\"ADR_STREET_FLAGS\" conversion=\"multimix\" combine=\",\">\n"
-"              <enum name=\"HOME\"     value=\"B0\"/>\n"
-"              <enum name=\"WORK\"     value=\"B1\"/>\n"
-"              <enum mode=\"ignore\"   value=\"B2\"/> <!-- OTHER -->\n"
-"\n"
-"              <enum mode=\"prefix\" name=\"X-CustomLabel-\" value=\"1.L\"/>\n"
-"              <enum mode=\"prefix\" name=\"X-Synthesis-Ref\" value=\"2.L\"/>\n"
-"            </value>\n"
-"          </parameter>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"BDAY\">\n"
-"          <value field=\"BDAY\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"NOTE\" filter=\"no\">\n"
-"          <value field=\"NOTE\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"PHOTO\" filter=\"no\">\n"
-"          <value field=\"PHOTO\" conversion=\"BLOB_B64\"/>\n"
-"          <parameter name=\"TYPE\" default=\"no\" show=\"yes\">\n"
-"            <value field=\"PHOTO_TYPE\">\n"
-"              <enum name=\"JPEG\" value=\"0\"/>\n"
-"            </value>\n"
-"          </parameter>\n"
-"        </property>\n"
-"\n"
-"      </profile>\n"
-"    </mimeprofile>\n"
-"\n"
-"    <!-- vCard 2.1 datatype, using vCard profile defined above -->\n"
-"    <datatype name=\"vCard21\" basetype=\"vcard\">\n"
-"      <version>2.1</version>\n"
-"      <use mimeprofile=\"vCard\"/>\n"
-"     <incomingscript><![CDATA[\n"
-"        $VCARD_INCOMING_NAMECHANGE_SCRIPT\n"
-"      ]]></incomingscript>\n"
-"    </datatype>\n"
-"\n"
-"    <!-- vCard 3.0 datatype, using vCard profile defined above -->\n"
-"    <datatype name=\"vCard30\" basetype=\"vcard\">\n"
-"      <version>3.0</version>\n"
-"      <use mimeprofile=\"vCard\"/>\n"
-"      <incomingscript><![CDATA[\n"
-"        $VCARD_INCOMING_NAMECHANGE_SCRIPT\n"
-"      ]]></incomingscript>\n"
-"    </datatype>\n"
-"\n"
-"\n"
-"    <!-- common field list for events and todos (both represented by vCalendar/iCalendar) -->\n"
-"    <fieldlist name=\"calendar\">\n"
-"      <field name=\"ISEVENT\" type=\"integer\" compare=\"always\"/>\n"
-"\n"
-"      <field name=\"DMODIFIED\" type=\"timestamp\" compare=\"never\" age=\"yes\"/>\n"
-"      <field name=\"DCREATED\" type=\"timestamp\" compare=\"never\"/>\n"
-"\n"
-"      <field name=\"DGENERATED\" type=\"timestamp\" compare=\"never\"/>\n"
-"\n"
-"      <field name=\"UID\" type=\"string\" compare=\"never\"/>\n"
-"\n"
-"      <field name=\"CATEGORIES\" array=\"yes\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"      <field name=\"CLASS\" type=\"integer\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"      <field name=\"TRANSP\" type=\"integer\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"\n"
-"      <field name=\"SUMMARY\" type=\"multiline\" compare=\"always\"/>\n"
-"      <field name=\"DESCRIPTION\" type=\"multiline\" compare=\"slowsync\" merge=\"lines\"/>\n"
-"      <field name=\"LOCATION\" type=\"multiline\" compare=\"slowsync\" merge=\"lines\"/>\n"
-"      <field name=\"URL\" type=\"url\" compare=\"conflict\"/>\n"
-"\n"
-"      <!-- recurrence rule block, fields must be in that order, including\n"
-"           DTSTART as last field !! -->\n"
-"      <field name=\"RR_FREQ\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"RR_INTERVAL\" type=\"integer\" compare=\"conflict\"/>\n"
-"      <field name=\"RR_FMASK\" type=\"integer\" compare=\"conflict\"/>\n"
-"      <field name=\"RR_LMASK\" type=\"integer\" compare=\"conflict\"/>\n"
-"      <field name=\"RR_END\" type=\"timestamp\" compare=\"conflict\"/>\n"
-"\n"
-"      <!-- Note: DTSTART/DTEND are compared in the <comparescript>,\n"
-"                 therefore compare is set no \"never\" here -->\n"
-"      <field name=\"DTSTART\" type=\"timestamp\" compare=\"never\"/>\n"
-"      <field name=\"DTEND\" type=\"timestamp\" compare=\"never\"/>\n"
-"      <field name=\"DURATION\" type=\"timestamp\" compare=\"never\"/>\n"
-"      <field name=\"COMPLETED\" type=\"timestamp\" compare=\"never\"/>\n"
-"      <field name=\"DUE\" type=\"timestamp\" compare=\"never\"/>\n"
-"\n"
-"      <field name=\"GEO_LAT\" type=\"string\" compare=\"never\"/>\n"
-"      <field name=\"GEO_LONG\" type=\"string\" compare=\"never\"/>\n"
-"\n"
-"      <field name=\"PRIORITY\" type=\"integer\" compare=\"conflict\"/>\n"
-"      <field name=\"STATUS\" type=\"integer\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"      <field name=\"PERCENT_COMPLETE\" type=\"integer\" compare=\"conflict\"/>\n"
-"\n"
-"      <field name=\"ALARM_TIME\" type=\"timestamp\" compare=\"conflict\"/>\n"
-"      <field name=\"ALARM_SNOOZE\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"ALARM_REPEAT\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"ALARM_MSG\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"ALARM_ACTION\" type=\"string\" compare=\"conflict\"/>\n"
-"      <field name=\"ALARM_REL\" type=\"integer\" compare=\"never\"/>\n"
-"      <field name=\"ALARM_UID\" type=\"string\" compare=\"conflict\"/>\n"
-"\n"
-"      <!-- non-standard -->\n"
-"      <field name=\"PARENT_UID\" type=\"string\" compare=\"never\"/>\n"
-"\n"
-"      <!-- for events -->\n"
-"      <field name=\"EXDATES\" array=\"yes\" type=\"timestamp\" compare=\"never\"/>\n"
-"\n"
-"      <field name=\"ORIGSTART\" array=\"no\" type=\"timestamp\" compare=\"never\"/>\n"
-"      <field name=\"SEQNO\" array=\"no\" type=\"integer\" compare=\"never\"/>\n"
-"\n"
-"      <field name=\"ATTENDEES\" array=\"yes\" type=\"string\" compare=\"never\"/>\n"
-"      <field name=\"ATTENDEE_CNS\" array=\"yes\" type=\"string\" compare=\"never\"/>\n"
-"      <field name=\"ATTENDEE_PARTSTATS\" array=\"yes\" type=\"integer\" compare=\"never\"/>\n"
-"      <field name=\"ATTENDEE_ROLE\" array=\"yes\" type=\"integer\" compare=\"never\"/>\n"
-"      <field name=\"ATTENDEE_RSVP\" array=\"yes\" type=\"integer\" compare=\"never\"/>\n"
-"      <field name=\"ATTENDEE_LANG\" array=\"yes\" type=\"string\" compare=\"never\"/>\n"
-"      <field name=\"ATTENDEE_CUTYPE\" array=\"yes\" type=\"integer\" compare=\"never\"/>\n"
-"      <field name=\"ORGANIZER\" array=\"no\" type=\"string\" compare=\"never\"/>\n"
-"      <field name=\"ORGANIZER_CN\" array=\"no\" type=\"string\" compare=\"never\"/>\n"
-"\n"
-"    </fieldlist>\n"
-"\n"
-"\n"
-"    <!-- vCalendar with VTODO and VEVENT variants -->\n"
-"    <mimeprofile name=\"vCalendar\" fieldlist=\"calendar\">\n"
-"\n"
-"      <vtimezonegenmode>current</vtimezonegenmode>\n"
-"      <tzidgenmode>olson</tzidgenmode>\n"
-"\n"
-"      <profile name=\"VCALENDAR\" nummandatory=\"1\">\n"
-"\n"
-"        <property name=\"VERSION\" mandatory=\"yes\">\n"
-"          <value conversion=\"version\"/>\n"
-"        </property>\n"
-"\n"
-"        <property onlyformode=\"standard\" name=\"PRODID\" mandatory=\"no\">\n"
-"          <value conversion=\"prodid\"/>\n"
-"        </property>\n"
-"\n"
-"        <property onlyformode=\"old\" name=\"TZ\" filter=\"false\" suppressempty=\"yes\">\n"
-"          <value field=\"DTSTART\" conversion=\"tz\"/>\n"
-"        </property>\n"
-"\n"
-"        <property onlyformode=\"old\" name=\"DAYLIGHT\" mode=\"daylight\" filter=\"false\" suppressempty=\"yes\">\n"
-"          <value field=\"DTSTART\" conversion=\"daylight\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"GEO\" values=\"2\" suppressempty=\"yes\" onlyformode=\"old\" valueseparator=\",\">\n"
-"          <!-- LON,LAT in vCalendar 1.0 -->\n"
-"          <value index=\"0\" field=\"GEO_LAT\"/>\n"
-"          <value index=\"1\" field=\"GEO_LONG\"/>\n"
-"        </property>\n"
-"\n"
-"        <subprofile onlyformode=\"standard\" name=\"VTIMEZONE\" mode=\"vtimezones\"/>\n"
-"\n"
-"        <!-- sub-profile for todoz -->\n"
-"        <subprofile name=\"VTODO\" nummandatory=\"1\" showifselectedonly=\"yes\" field=\"ISEVENT\" value=\"0\">\n"
-"\n"
-"          <property name=\"LAST-MODIFIED\" suppressempty=\"yes\">\n"
-"            <value field=\"DMODIFIED\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"DTSTAMP\" suppressempty=\"yes\" onlyformode=\"standard\">\n"
-"            <value field=\"DGENERATED\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"DCREATED\" suppressempty=\"yes\" onlyformode=\"old\">\n"
-"            <value field=\"DCREATED\"/>\n"
-"          </property>\n"
-"          <property name=\"CREATED\" suppressempty=\"yes\" onlyformode=\"standard\">\n"
-"            <value field=\"DCREATED\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"UID\" suppressempty=\"yes\">\n"
-"            <value field=\"UID\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"SEQUENCE\" suppressempty=\"yes\">\n"
-"            <value field=\"SEQNO\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"GEO\" values=\"2\" suppressempty=\"yes\" onlyformode=\"standard\" valueseparator=\";\">\n"
-"            <!-- LAT;LON in iCalendar 2.0 -->\n"
-"            <value index=\"0\" field=\"GEO_LONG\"/>\n"
-"            <value index=\"1\" field=\"GEO_LAT\"/>\n"
-"          </property>\n"
-"\n"
-"          <property onlyformode=\"standard\" name=\"CATEGORIES\" values=\"list\" valueseparator=\",\" suppressempty=\"yes\">\n"
-"            <value field=\"CATEGORIES\" />\n"
-"            <position field=\"CATEGORIES\" repeat=\"array\" minshow=\"0\"/>\n"
-"          </property>\n"
-"\n"
-"          <property onlyformode=\"old\" name=\"CATEGORIES\" values=\"list\" valueseparator=\";\" altvalueseparator=\",\" suppressempty=\"yes\">\n"
-"            <value field=\"CATEGORIES\" />\n"
-"            <position field=\"CATEGORIES\" repeat=\"array\" minshow=\"0\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"CLASS\" suppressempty=\"yes\">\n"
-"            <value field=\"CLASS\">\n"
-"              <enum name=\"PUBLIC\"       value=\"0\"/>\n"
-"              <enum name=\"PRIVATE\"      value=\"1\"/>\n"
-"              <enum name=\"CONFIDENTIAL\" value=\"2\"/>\n"
-"            </value>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"SUMMARY\" mandatory=\"yes\">\n"
-"            <value field=\"SUMMARY\"/>\n"
-"          </property>\n"
-"\n"
-"          <!-- DESCRIPTION is an optional property and libical does not like\n"
-"               empty properties, so suppress it here. However, in the scripts\n"
-"               we ensure that the DESCRIPTION field should never be empty. -->\n"
-"          <property name=\"DESCRIPTION\" suppressempty=\"yes\" mandatory=\"no\">\n"
-"            <value field=\"DESCRIPTION\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"LOCATION\" suppressempty=\"yes\" mandatory=\"no\">\n"
-"            <value field=\"LOCATION\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"URL\" suppressempty=\"yes\" mandatory=\"no\">\n"
-"            <value field=\"URL\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"DTSTART\" suppressempty=\"yes\" delayedparsing=\"1\">\n"
-"            <value field=\"DTSTART\" conversion=\"autodate\"/>\n"
-"            <parameter onlyformode=\"standard\" name=\"TZID\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"DTSTART\" conversion=\"TZID\"/>\n"
-"            </parameter>\n"
-"            <parameter onlyformode=\"standard\" name=\"VALUE\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"DTSTART\" conversion=\"VALUETYPE\"/>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"COMPLETED\" suppressempty=\"yes\" delayedparsing=\"1\">\n"
-"            <value field=\"COMPLETED\" conversion=\"autoenddate\"/>\n"
-"            <parameter onlyformode=\"standard\" name=\"TZID\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"COMPLETED\" conversion=\"TZID\"/>\n"
-"            </parameter>\n"
-"            <parameter onlyformode=\"standard\" name=\"VALUE\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"COMPLETED\" conversion=\"VALUETYPE\"/>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"DUE\" suppressempty=\"yes\" delayedparsing=\"1\">\n"
-"            <value field=\"DUE\" conversion=\"autodate\"/>\n"
-"            <parameter onlyformode=\"standard\" name=\"TZID\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"DUE\" conversion=\"TZID\"/>\n"
-"            </parameter>\n"
-"            <parameter onlyformode=\"standard\" name=\"VALUE\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"DUE\" conversion=\"VALUETYPE\"/>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"PRIORITY\" suppressempty=\"yes\">\n"
-"            <value field=\"PRIORITY\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"STATUS\" onlyformode=\"standard\" suppressempty=\"yes\">\n"
-"            <value field=\"STATUS\" conversion=\"emptyonly\">\n"
-"              <enum name=\"COMPLETED\"      value=\"0\"/>\n"
-"              <enum name=\"NEEDS-ACTION\"   value=\"1\"/>\n"
-"              <enum name=\"IN-PROCESS\"     value=\"2\"/>\n"
-"              <enum name=\"CANCELLED\"      value=\"3\"/>\n"
-"              <enum name=\"ACCEPTED\"       value=\"4\"/>\n"
-"              <enum name=\"TENTATIVE\"      value=\"5\"/>\n"
-"              <enum name=\"DELEGATED\"      value=\"6\"/>\n"
-"              <enum name=\"DECLINED\"       value=\"7\"/>\n"
-"              <enum name=\"SENT\"           value=\"8\"/>\n"
-"              <enum name=\"CONFIRMED\"      value=\"9\"/>\n"
-"              <enum name=\"DRAFT\"          value=\"10\"/>\n"
-"              <enum name=\"FINAL\"          value=\"11\"/>\n"
-"            </value>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"STATUS\" onlyformode=\"old\" suppressempty=\"yes\">\n"
-"            <value field=\"STATUS\" conversion=\"emptyonly\">\n"
-"              <enum name=\"COMPLETED\"      value=\"0\"/>\n"
-"              <enum name=\"NEEDS ACTION\"   value=\"1\"/>\n"
-"              <enum mode=\"defaultvalue\"   value=\"1\"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->\n"
-"              <enum name=\"IN PROCESS\"     value=\"2\"/>\n"
-"              <enum name=\"CANCELLED\"      value=\"3\"/>\n"
-"              <enum name=\"ACCEPTED\"       value=\"4\"/>\n"
-"              <enum name=\"TENTATIVE\"      value=\"5\"/>\n"
-"              <enum name=\"DELEGATED\"      value=\"6\"/>\n"
-"              <enum name=\"DECLINED\"       value=\"7\"/>\n"
-"              <enum name=\"SENT\"           value=\"8\"/>\n"
-"              <enum name=\"CONFIRMED\"      value=\"9\"/>\n"
-"              <enum name=\"DRAFT\"          value=\"10\"/>\n"
-"              <enum name=\"FINAL\"          value=\"11\"/>\n"
-"            </value>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"PERCENT-COMPLETE\" onlyformode=\"standard\" suppressempty=\"yes\">\n"
-"            <value field=\"PERCENT_COMPLETE\"/>\n"
-"          </property>          \n"
-"\n"
-"          <!-- AALARM and DALARM both use the same fields -->\n"
-"          <property name=\"AALARM\" onlyformode=\"old\" values=\"4\" suppressempty=\"yes\">\n"
-"            <value index=\"0\" field=\"ALARM_TIME\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"1\" field=\"ALARM_SNOOZE\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"2\" field=\"ALARM_REPEAT\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"3\" field=\"ALARM_MSG\" conversion=\"emptyonly\"/>\n"
-"          </property>\n"
-"          <property name=\"DALARM\" onlyformode=\"old\" values=\"4\" suppressempty=\"yes\">\n"
-"            <value index=\"0\" field=\"ALARM_TIME\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"1\" field=\"ALARM_SNOOZE\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"2\" field=\"ALARM_REPEAT\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"3\" field=\"ALARM_MSG\" conversion=\"emptyonly\"/>\n"
-"          </property>\n"
-"\n"
-"          <subprofile onlyformode=\"standard\" name=\"VALARM\" nummandatory=\"1\" field=\"ALARM_TIME\">\n"
-"            <property name=\"TRIGGER\" suppressempty=\"no\" mandatory=\"yes\">\n"
-"              <value field=\"ALARM_TIME\"/>\n"
-"              <parameter name=\"VALUE\" default=\"no\" show=\"yes\">\n"
-"                <value field=\"ALARM_TIME\" conversion=\"FULLVALUETYPE\"/>\n"
-"              </parameter>\n"
-"              <parameter name=\"RELATED\" default=\"no\" show=\"yes\">\n"
-"                <value field=\"ALARM_REL\">\n"
-"                  <enum mode=\"ignore\" value=\"0\"/>\n"
-"                  <enum name=\"START\" value=\"1\"/>\n"
-"                  <enum name=\"END\"   value=\"2\"/>\n"
-"                </value>\n"
-"              </parameter>\n"
-"            </property>\n"
-"            <property name=\"ACTION\" suppressempty=\"yes\" mandatory=\"yes\">\n"
-"              <value field=\"ALARM_ACTION\"/>\n"
-"            </property>\n"
-"            <property name=\"DESCRIPTION\" suppressempty=\"yes\">\n"
-"              <value field=\"ALARM_MSG\"/>\n"
-"            </property>\n"
-"            <property name=\"REPEAT\" suppressempty=\"yes\">\n"
-"              <value field=\"ALARM_REPEAT\"/>\n"
-"            </property>\n"
-"            <property name=\"X-EVOLUTION-ALARM-UID\" suppressempty=\"yes\">\n"
-"              <value field=\"ALARM_UID\"/>\n"
-"            </property>\n"
-"          </subprofile>\n"
-"\n"
-"          <property onlyformode=\"old\" name=\"RELATED-TO\" suppressempty=\"yes\">\n"
-"            <value field=\"PARENT_UID\"/>\n"
-"          </property>\n"
-"\n"
-"          <property onlyformode=\"standard\" name=\"RELATED-TO\" suppressempty=\"yes\">\n"
-"            <value field=\"PARENT_UID\"/>\n"
-"            <parameter onlyformode=\"standard\" name=\"RELTYPE\" default=\"no\" positional=\"yes\" show=\"yes\">\n"
-"              <value>\n"
-"                <enum name=\"PARENT\"/>\n"
-"                <enum mode=\"defaultvalue\" name=\"other\"/>\n"
-"              </value>\n"
-"              <position hasnot=\"other\" shows=\"PARENT\" field=\"PARENT_UID\"/>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"        </subprofile>\n"
-"\n"
-"        <!-- sub-profile for event -->\n"
-"        <subprofile name=\"VEVENT\" nummandatory=\"1\" showifselectedonly=\"yes\" field=\"ISEVENT\" value=\"1\">\n"
-"\n"
-"            <property name=\"STATUS\"  suppressempty=\"yes\" onlyformode=\"old\">\n"
-"                <value field=\"STATUS\" conversion=\"emptyonly\">\n"
-"                    <enum name=\"COMPLETED\"      value=\"0\"/>\n"
-"                    <enum mode=\"defaultvalue\"   value=\"1\"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->\n"
-"                    <enum name=\"NEEDS ACTION\"   value=\"1\"/>\n"
-"                    <enum name=\"CANCELLED\"      value=\"3\"/>\n"
-"                    <enum name=\"ACCEPTED\"       value=\"4\"/>\n"
-"                    <enum name=\"TENTATIVE\"      value=\"5\"/>\n"
-"                    <enum name=\"DELEGATED\"      value=\"6\"/>\n"
-"                    <enum name=\"DECLINED\"       value=\"7\"/>\n"
-"                    <enum name=\"SENT\"           value=\"8\"/>\n"
-"                    <enum name=\"CONFIRMED\"      value=\"9\"/>\n"
-"                    <enum name=\"FINAL\"          value=\"11\"/>\n"
-"                </value>\n"
-"            </property>\n"
-"\n"
-"            <property name=\"STATUS\" suppressempty=\"yes\" onlyformode=\"standard\">\n"
-"                <value field=\"STATUS\" conversion=\"emptyonly\">\n"
-"                    <enum name=\"CANCELLED\" value=\"3\"/>\n"
-"                    <enum name=\"TENTATIVE\" value=\"5\"/>\n"
-"                    <enum name=\"CONFIRMED\" value=\"9\"/>\n"
-"                </value>\n"
-"            </property>\n"
-"\n"
-"          <property name=\"LAST-MODIFIED\" suppressempty=\"yes\">\n"
-"            <value field=\"DMODIFIED\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"DTSTAMP\" suppressempty=\"yes\" onlyformode=\"standard\">\n"
-"            <value field=\"DGENERATED\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"DCREATED\" suppressempty=\"yes\" onlyformode=\"old\">\n"
-"            <value field=\"DCREATED\"/>\n"
-"          </property>\n"
-"          <property name=\"CREATED\" suppressempty=\"yes\" onlyformode=\"standard\">\n"
-"            <value field=\"DCREATED\"/>\n"
-"          </property>\n"
-"\n"
-"\n"
-"          <property name=\"UID\" suppressempty=\"yes\">\n"
-"            <value field=\"UID\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"SEQUENCE\" suppressempty=\"yes\">\n"
-"            <value field=\"SEQNO\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"GEO\" values=\"2\" suppressempty=\"yes\" onlyformode=\"standard\" valueseparator=\";\">\n"
-"            <!-- LAT;LON in iCalendar 2.0 -->\n"
-"            <value index=\"0\" field=\"GEO_LONG\"/>\n"
-"            <value index=\"1\" field=\"GEO_LAT\"/>\n"
-"          </property>\n"
-"\n"
-"          <property onlyformode=\"standard\" name=\"CATEGORIES\" values=\"list\" valueseparator=\",\" suppressempty=\"yes\">\n"
-"            <value field=\"CATEGORIES\" />\n"
-"            <position field=\"CATEGORIES\" repeat=\"array\" minshow=\"0\"/>\n"
-"          </property>\n"
-"\n"
-"          <property onlyformode=\"old\" name=\"CATEGORIES\" values=\"list\" valueseparator=\";\" altvalueseparator=\",\" suppressempty=\"yes\">\n"
-"            <value field=\"CATEGORIES\" />\n"
-"            <position field=\"CATEGORIES\" repeat=\"array\" minshow=\"0\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"CLASS\" suppressempty=\"yes\">\n"
-"            <value field=\"CLASS\">\n"
-"              <enum name=\"PUBLIC\"       value=\"0\"/>\n"
-"              <enum name=\"PRIVATE\"      value=\"1\"/>\n"
-"              <enum name=\"CONFIDENTIAL\" value=\"2\"/>\n"
-"            </value>\n"
-"          </property>\n"
-"\n"
-"\n"
-"          <property name=\"TRANSP\" suppressempty=\"yes\" onlyformode=\"standard\">\n"
-"            <value field=\"TRANSP\">\n"
-"              <enum name=\"OPAQUE\"       value=\"0\"/>\n"
-"              <enum name=\"TRANSPARENT\"  value=\"1\"/>\n"
-"              <enum name=\"TENTATIVE\"     value=\"2\"/> <!-- according to Oracle de facto usage in vCalendar 1.0 -->\n"
-"              <enum name=\"OUT_OF_OFFICE\" value=\"3\"/> <!-- according to Oracle de facto usage in vCalendar 1.0 -->\n"
-"              <enum mode=\"defaultvalue\" value=\"0\"/>\n"
-"            </value>\n"
-"          </property>\n"
-"          <property name=\"TRANSP\" suppressempty=\"yes\" onlyformode=\"old\">\n"
-"            <value field=\"TRANSP\"/> <!-- directly numeric in vCalendar 1.0 -->\n"
-"          </property>\n"
-"\n"
-"\n"
-"          <property name=\"PRIORITY\" suppressempty=\"yes\">\n"
-"            <value field=\"PRIORITY\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"SUMMARY\" mandatory=\"yes\">\n"
-"            <value field=\"SUMMARY\"/>\n"
-"          </property>\n"
-"\n"
-"          <!-- DESCRIPTION is an optional property and libical does not like\n"
-"               empty properties, so suppress it here. However, in the scripts\n"
-"               we ensure that the DESCRIPTION field should never be empty. -->\n"
-"          <property name=\"DESCRIPTION\" suppressempty=\"yes\" mandatory=\"no\">\n"
-"            <value field=\"DESCRIPTION\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"LOCATION\" suppressempty=\"yes\" mandatory=\"no\">\n"
-"            <value field=\"LOCATION\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"DTSTART\" suppressempty=\"yes\" delayedparsing=\"1\">\n"
-"            <value field=\"DTSTART\" conversion=\"autodate\"/>\n"
-"            <parameter onlyformode=\"standard\" name=\"TZID\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"DTSTART\" conversion=\"TZID\"/>\n"
-"            </parameter>\n"
-"            <parameter onlyformode=\"standard\" name=\"VALUE\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"DTSTART\" conversion=\"VALUETYPE\"/>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"          <!-- recurrence rule (with delayed parsing, as it is dependent on DTSTART) -->\n"
-"          <property name=\"RRULE\" suppressempty=\"yes\" delayedparsing=\"2\">\n"
-"            <!-- Note: RR_FREQ is the beginning of a block of fields\n"
-"                 suitable for the \"rrule\" conversion mode -->\n"
-"            <value field=\"RR_FREQ\" conversion=\"rrule\"/>\n"
-"          </property>\n"
-"\n"
-"          <!-- Symbian uses this, so it might make the client work with symbian-prepared servers better -->\n"
-"          <property name=\"X-RECURRENCE-ID\" suppressempty=\"yes\" onlyformode=\"old\">\n"
-"            <value field=\"ORIGSTART\" conversion=\"autodate\"/>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"RECURRENCE-ID\" suppressempty=\"yes\" onlyformode=\"standard\" delayedparsing=\"1\">\n"
-"            <value field=\"ORIGSTART\" conversion=\"autodate\"/>\n"
-"            <parameter name=\"TZID\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"ORIGSTART\" conversion=\"TZID\"/>\n"
-"            </parameter>\n"
-"            <parameter name=\"VALUE\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"ORIGSTART\" conversion=\"VALUETYPE\"/>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"          <!-- ScheduleWorld has a problem (bugzilla.moblin.org #2226)\n"
-"               with the EXDATE:value1,value2 format (correct in iCalendar 2.0):\n"
-"               as a workaround, accept all valid formats plus ; but\n"
-"               generate separate properties with one value each. -->\n"
-"          <property name=\"EXDATE\" values=\"expandedlist\" suppressempty=\"yes\" onlyformode=\"standard\" delayedparsing=\"1\" valueseparator=\",\" altvalueseparator=\";\">\n"
-"            <value field=\"EXDATES\"/>\n"
-"            <position field=\"EXDATES\" repeat=\"array\" increment=\"1\" minshow=\"0\"/>\n"
-"            <parameter name=\"TZID\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"EXDATES\" conversion=\"TZID\"/>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"EXDATE\" values=\"list\" suppressempty=\"yes\" onlyformode=\"old\" delayedparsing=\"1\" valueseparator=\";\" altvalueseparator=\",\">\n"
-"            <value field=\"EXDATES\"/>\n"
-"            <position field=\"EXDATES\" repeat=\"array\" increment=\"1\" minshow=\"0\"/>\n"
-"          </property>\n"
-"\n"
-"\n"
-"          <property name=\"DTEND\" suppressempty=\"yes\" delayedparsing=\"1\">\n"
-"            <value field=\"DTEND\" conversion=\"autoenddate\"/>\n"
-"            <parameter onlyformode=\"standard\" name=\"TZID\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"DTEND\" conversion=\"TZID\"/>\n"
-"            </parameter>\n"
-"            <parameter onlyformode=\"standard\" name=\"VALUE\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"DTEND\" conversion=\"VALUETYPE\"/>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"DURATION\" suppressempty=\"yes\" delayedparsing=\"1\" onlyformode=\"standard\">\n"
-"            <value field=\"DURATION\"/>\n"
-"            <parameter onlyformode=\"standard\" name=\"VALUE\" default=\"no\" show=\"no\">\n"
-"              <value field=\"DURATION\" conversion=\"VALUETYPE\"/>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"ATTENDEE\" suppressempty=\"yes\" onlyformode=\"old\">\n"
-"            <value field=\"ATTENDEES\"/>\n"
-"            <parameter name=\"ROLE\" default=\"no\" positional=\"yes\" show=\"yes\">\n"
-"              <value>\n"
-"                <enum name=\"ORGANIZER\"/>\n"
-"              </value>\n"
-"              <position has=\"ORGANIZER\" field=\"ORGANIZER\" overwriteempty=\"yes\"/>\n"
-"              <position hasnot=\"ORGANIZER\" field=\"ATTENDEES\" repeat=\"array\" increment=\"1\" overwriteempty=\"yes\"/>\n"
-"            </parameter>\n"
-"            <parameter name=\"STATUS\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"ATTENDEE_PARTSTATS\">\n"
-"                <enum name=\"NEEDS ACTION\"   value=\"1\"/>\n"
-"                <enum mode=\"defaultvalue\"   value=\"1\"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->\n"
-"                <enum name=\"ACCEPTED\"       value=\"4\"/>\n"
-"                <enum name=\"DECLINED\"       value=\"7\"/>\n"
-"                <enum name=\"TENTATIVE\"      value=\"5\"/>\n"
-"                <enum name=\"DELEGATED\"      value=\"6\"/>\n"
-"              </value>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"ATTENDEE\" suppressempty=\"yes\" onlyformode=\"standard\">\n"
-"            <value field=\"ATTENDEES\" conversion=\"mailto\"/>\n"
-"            <position field=\"ATTENDEES\" repeat=\"array\" increment=\"1\" minshow=\"0\"/>\n"
-"            <parameter name=\"CN\" default=\"no\" show=\"yes\" shownonempty=\"yes\">\n"
-"              <value field=\"ATTENDEE_CNS\"/>\n"
-"            </parameter>\n"
-"            <parameter name=\"PARTSTAT\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"ATTENDEE_PARTSTATS\">\n"
-"                <enum name=\"NEEDS-ACTION\"   value=\"1\"/>\n"
-"                <enum mode=\"defaultvalue\"   value=\"1\"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->\n"
-"                <enum name=\"ACCEPTED\"       value=\"4\"/>\n"
-"                <enum name=\"DECLINED\"       value=\"7\"/>\n"
-"                <enum name=\"TENTATIVE\"      value=\"5\"/>\n"
-"                <enum name=\"DELEGATED\"      value=\"6\"/>\n"
-"              </value>\n"
-"            </parameter>\n"
-"            <parameter name=\"ROLE\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"ATTENDEE_ROLE\">\n"
-"                <enum name=\"CHAIR\"            value=\"1\"/>\n"
-"                <enum name=\"REQ-PARTICIPANT\"  value=\"2\"/>\n"
-"                <enum name=\"OPT-PARTICIPANT\"  value=\"3\"/>\n"
-"                <enum name=\"NON-PARTICIPANT\"  value=\"4\"/>\n"
-"              </value>\n"
-"            </parameter>\n"
-"            <parameter name=\"RSVP\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"ATTENDEE_RSVP\">\n"
-"                <enum name=\"TRUE\"             value=\"1\"/>\n"
-"                <enum name=\"FALSE\"            value=\"0\"/>\n"
-"              </value>\n"
-"            </parameter>\n"
-"            <parameter name=\"LANGUAGE\" show=\"yes\">\n"
-"              <value field=\"ATTENDEE_LANG\"/>\n"
-"            </parameter>\n"
-"            <parameter name=\"CUTYPE\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"ATTENDEE_CUTYPE\">\n"
-"                <enum name=\"INDIVIDUAL\"  value=\"1\"/>\n"
-"                <enum name=\"GROUP\"       value=\"2\"/>\n"
-"                <enum name=\"RESOURCE\"    value=\"3\"/>\n"
-"                <enum name=\"ROOM\"        value=\"4\"/>\n"
-"                <enum name=\"UNKNOWN\"     value=\"5\"/>\n"
-"              </value>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"          <property name=\"ORGANIZER\" suppressempty=\"yes\" onlyformode=\"standard\">\n"
-"            <value field=\"ORGANIZER\" conversion=\"mailto\"/>\n"
-"            <parameter name=\"CN\" default=\"no\" show=\"yes\">\n"
-"              <value field=\"ORGANIZER_CN\"/>\n"
-"            </parameter>\n"
-"          </property>\n"
-"\n"
-"\n"
-"          <!-- AALARM and DALARM both use the same fields -->\n"
-"          <property name=\"AALARM\" onlyformode=\"old\" values=\"4\" suppressempty=\"yes\">\n"
-"            <value index=\"0\" field=\"ALARM_TIME\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"1\" field=\"ALARM_SNOOZE\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"2\" field=\"ALARM_REPEAT\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"3\" field=\"ALARM_MSG\" conversion=\"emptyonly\"/>\n"
-"          </property>\n"
-"          <property name=\"DALARM\" onlyformode=\"old\" values=\"4\" suppressempty=\"yes\">\n"
-"            <value index=\"0\" field=\"ALARM_TIME\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"1\" field=\"ALARM_SNOOZE\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"2\" field=\"ALARM_REPEAT\" conversion=\"emptyonly\"/>\n"
-"            <value index=\"3\" field=\"ALARM_MSG\" conversion=\"emptyonly\"/>\n"
-"          </property>\n"
-"\n"
-"          <subprofile onlyformode=\"standard\" name=\"VALARM\" nummandatory=\"1\" field=\"ALARM_TIME\">\n"
-"            <property name=\"TRIGGER\" suppressempty=\"no\" mandatory=\"yes\">\n"
-"              <value field=\"ALARM_TIME\"/>\n"
-"              <parameter name=\"VALUE\" default=\"no\" show=\"yes\">\n"
-"                <value field=\"ALARM_TIME\" conversion=\"FULLVALUETYPE\"/>\n"
-"              </parameter>\n"
-"              <parameter name=\"RELATED\" default=\"no\" show=\"yes\">\n"
-"                <value field=\"ALARM_REL\">\n"
-"                  <enum mode=\"ignore\" value=\"0\"/>\n"
-"                  <enum name=\"START\" value=\"1\"/>\n"
-"                  <enum name=\"END\"   value=\"2\"/>\n"
-"                </value>\n"
-"              </parameter>\n"
-"            </property>\n"
-"            <property name=\"ACTION\" suppressempty=\"yes\" mandatory=\"yes\">\n"
-"              <value field=\"ALARM_ACTION\"/>\n"
-"            </property>\n"
-"            <property name=\"DESCRIPTION\" suppressempty=\"yes\">\n"
-"              <value field=\"ALARM_MSG\"/>\n"
-"            </property>\n"
-"            <property name=\"REPEAT\" suppressempty=\"yes\">\n"
-"              <value field=\"ALARM_REPEAT\"/>\n"
-"            </property>\n"
-"            <property name=\"X-EVOLUTION-ALARM-UID\" suppressempty=\"yes\">\n"
-"              <value field=\"ALARM_UID\"/>\n"
-"            </property>\n"
-"          </subprofile>\n"
-"\n"
-"        </subprofile>\n"
-"        \n"
-"      </profile>\n"
-"    </mimeprofile>\n"
-"\n"
-"\n"
-"    <!-- vCalendar 1.0 datatype, using vCalendar profile defined above -->\n"
-"    <datatype name=\"vCalendar10\" basetype=\"vcalendar\">\n"
-"      <version>1.0</version>\n"
-"      <use mimeprofile=\"vCalendar\"/>\n"
-"\n"
-"      <incomingscript><![CDATA[\n"
-"        $VCALENDAR_INCOMING_SCRIPT\n"
-"      ]]></incomingscript>\n"
-"      \n"
-"      <outgoingscript><![CDATA[\n"
-"        $VCALENDAR_OUTGOING_SCRIPT\n"
-"      ]]></outgoingscript>\n"
-"\n"
-"    </datatype>\n"
-"\n"
-"\n"
-"    <!-- iCalendar 2.0 datatype, using vCalendar profile defined above -->\n"
-"    <datatype name=\"iCalendar20\" basetype=\"vcalendar\">\n"
-"      <version>2.0</version>\n"
-"      <use mimeprofile=\"vCalendar\"/>\n"
-"\n"
-"      <incomingscript><![CDATA[\n"
-"        $VCALENDAR_INCOMING_SCRIPT\n"
-"      ]]></incomingscript>\n"
-"\n"
-"\n"
-"      <outgoingscript><![CDATA[\n"
-"        $VCALENDAR_OUTGOING_SCRIPT\n"
-"      ]]></outgoingscript>\n"
-"\n"
-"    </datatype>\n"
-"\n"
-"\n"
-"    <!-- list of internal fields representing plain text note data -->\n"
-"    <fieldlist name=\"Note\">\n"
-"      <field name=\"SYNCLVL\" type=\"integer\" compare=\"never\"/>\n"
-"      <field name=\"SUBJECT\" type=\"multiline\" compare=\"always\"/>\n"
-"      <field name=\"TEXT\" type=\"multiline\" compare=\"conflict\" merge=\"lines\"/>\n"
-"    </fieldlist>\n"
-"\n"
-"    <textprofile name=\"Note\" fieldlist=\"Note\">\n"
-"      <linemap field=\"SUBJECT\">\n"
-"        <numlines>1</numlines>\n"
-"        <inheader>false</inheader>\n"
-"        <allowempty>true</allowempty>\n"
-"        <filterkeyword>SUBJECT</filterkeyword>\n"
-"      </linemap>\n"
-"      <linemap field=\"TEXT\">\n"
-"        <numlines>0</numlines>\n"
-"        <inheader>false</inheader>\n"
-"        <allowempty>true</allowempty>\n"
-"      </linemap>\n"
-"    </textprofile>\n"
-"\n"
-"    <datatype name=\"note10\" basetype=\"text\">\n"
-"      <use profile=\"Note\"/>\n"
-"      <typestring>text/plain</typestring>\n"
-"      <versionstring>1.0</versionstring>\n"
-"    </datatype>\n"
-"\n"
-"    <datatype name=\"note11\" basetype=\"text\">\n"
-"      <use profile=\"Note\"/>\n"
-"      <typestring>text/plain</typestring>\n"
-"      <versionstring>1.1</versionstring>\n"
-"    </datatype>\n"
-"\n"
-"\n"
-"    <!-- list of internal fields representing vBookmark data -->\n"
-"    <fieldlist name=\"bookmarks\">\n"
-"      <field name=\"REV\" type=\"timestamp\" compare=\"never\" age=\"yes\"/>\n"
-"      <field name=\"SYNCLVL\" type=\"integer\" compare=\"never\"/>\n"
-"\n"
-"      <!-- Name -->\n"
-"      <field name=\"TITLE\" type=\"string\" compare=\"always\"/>\n"
-"\n"
-"      <!-- categories and classification -->\n"
-"      <field name=\"CATEGORIES\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"      <field name=\"CLASS\" type=\"string\" compare=\"conflict\" merge=\"fillempty\"/>\n"
-"\n"
-"      <!-- web addresses -->\n"
-"      <field name=\"URL\" type=\"url\" compare=\"slowsync\" merge=\"fillempty\"/>\n"
-"\n"
-"      <!-- Note -->\n"
-"      <field name=\"NOTE\" type=\"multiline\" compare=\"conflict\" merge=\"lines\"/>\n"
-"\n"
-"    </fieldlist>\n"
-"\n"
-"    <!-- vBookmark profile -->\n"
-"    <mimeprofile name=\"vBookmark\" fieldlist=\"bookmarks\">\n"
-"\n"
-"      <profile name=\"VBKM\" nummandatory=\"0\">\n"
-"        <property name=\"VERSION\">\n"
-"          <value conversion=\"version\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"X-LAST-MODIFIED\">\n"
-"          <value field=\"REV\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"TITLE\">\n"
-"          <value field=\"TITLE\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"URL\">\n"
-"          <value field=\"URL\"/>\n"
-"        </property>\n"
-"\n"
-"        <!-- non-standard properties -->\n"
-"\n"
-"        <property name=\"CATEGORIES\">\n"
-"          <value field=\"CATEGORIES\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"CLASS\" suppressempty=\"yes\">\n"
-"          <value field=\"CLASS\"/>\n"
-"        </property>\n"
-"\n"
-"        <property name=\"NOTE\" filter=\"no\">\n"
-"          <value field=\"NOTE\"/>\n"
-"        </property>\n"
-"\n"
-"      </profile>\n"
-"    </mimeprofile>\n"
-"\n"
-"    <!-- vBookmark datatype, using vBookmark profile defined above -->\n"
-"    <datatype name=\"vBookmark10\" basetype=\"mimedir\">\n"
-"      <typestring>text/x-vbookmark</typestring>\n"
-"      <versionstring>1.0</versionstring>\n"
-"      <use profile=\"vBookmark\"/>\n"
-"    </datatype>\n"
-"\n"
-"    <fieldlists/>\n"
-"    <profiles/>\n"
-"    <datatypes/>\n"
-"  </datatypes>\n"
-"\n"
-"  <clientorserver/>\n"
-"\n"
-"  <client type=\"plugin\">\n"
-"      <remoterule name=\"ZYB\">\n"
-"          <manufacturer>ZYB</manufacturer>\n"
-"          <model>ZYB</model>\n"
-"          <!-- information to disable anchors checking -->\n"
-"          <lenientmode>yes</lenientmode>\n"
-"      </remoterule>\n"
-"  </client>\n"
-"\n"
-"</sysync_config>\n"
-;
index 7ce58fb..ff21900 100644 (file)
@@ -380,6 +380,17 @@ void SyncSourceReport::StringToStatTuple(const std::string &str, ItemLocation &l
     result = tokens.size() > 2 ? StringToResult(tokens[2]) : ITEM_RESULT_MAX;
 }
 
+bool SyncSourceReport::wasChanged(ItemLocation location)
+{
+    for (int i = ITEM_ADDED; i < ITEM_ANY; i++) {
+        if (getItemStat(location, (ItemState)i, ITEM_TOTAL) > 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+
 std::ostream &operator << (std::ostream &out, const SyncReport &report)
 {
     report.prettyPrint(out, 0);
@@ -741,7 +752,7 @@ std::string SyncReport::formatSyncTimes() const
 }
 
 std::string SyncReport::slowSyncExplanation(const std::string &peer,
-                                            const std::list<std::string> &sources)
+                                            const std::set<std::string> &sources)
 {
     if (sources.empty()) {
         return "";
@@ -766,12 +777,15 @@ std::string SyncReport::slowSyncExplanation(const std::string &peer,
 
 std::string SyncReport::slowSyncExplanation(const std::string &peer) const
 {
-    std::list<std::string> sources;
+    std::set<std::string> sources;
     BOOST_FOREACH(const SyncReport::value_type &entry, *this) {
         const std::string &name = entry.first;
         const SyncSourceReport &source = entry.second;
         if (source.getStatus() == STATUS_UNEXPECTED_SLOW_SYNC) {
-            sources.push_back(name);
+            string virtualsource = source.getVirtualSource();
+            sources.insert(virtualsource.empty() ?
+                           name :
+                           virtualsource);
         }
     }
     return slowSyncExplanation(peer, sources);
@@ -807,6 +821,11 @@ ConfigNode &operator << (ConfigNode &node, const SyncReport &report)
         node.setProperty(key, source.isResumeSync());
         key = prefix + "-status";
         node.setProperty(key, static_cast<long>(source.getStatus()));
+        string virtualsource = source.getVirtualSource();
+        if (!virtualsource.empty()) {
+            key = prefix + "-virtualsource";
+            node.setProperty(key, virtualsource);
+        }
         key = prefix + "-backup-before";
         node.setProperty(key, source.m_backupBefore.getNumItems());
         key = prefix + "-backup-after";
@@ -897,6 +916,8 @@ ConfigNode &operator >> (ConfigNode &node, SyncReport &report)
                     if (node.getProperty(prop.first, value)) {
                         source.recordStatus(static_cast<SyncMLStatus>(value));
                     }
+                } else if (key == "virtualsource") {
+                    source.recordVirtualSource(node.readProperty(prop.first));
                 } else if (key == "backup-before") {
                     long value;
                     if (node.getProperty(prop.first, value)) {
index 5baaf54..f1070d7 100644 (file)
@@ -24,6 +24,7 @@
 #include <string>
 #include <map>
 #include <list>
+#include <set>
 #include <ostream>
 #include <string.h>
 
@@ -124,6 +125,15 @@ enum SyncMLStatus {
     /** no error at the SyncML level, but some items did not transfer correctly */
     STATUS_PARTIAL_FAILURE = 22001,
 
+    /**
+     * Set by SyncEvolution in status.ini before starting an sync.
+     * Replaced with the final status code if the sync completes.
+     * Finding this code here in a session report implies that
+     * the process responsible for the session died unexpectedly,
+     * for unknown reasons.
+     */
+    STATUS_DIED_PREMATURELY = 22002,
+
     STATUS_MAX = 0x7FFFFFF
 };
 
@@ -225,6 +235,9 @@ class SyncSourceReport {
         m_stat[location][state][success]++;
     }
 
+    /** true if statistics indicate that peer or local was modified during sync */
+    bool wasChanged(ItemLocation location);
+
     void recordFinalSyncMode(SyncMode mode) { m_mode = mode; }
     SyncMode getFinalSyncMode() const { return m_mode; }
 
@@ -237,6 +250,13 @@ class SyncSourceReport {
     void recordStatus(SyncMLStatus status ) { m_status = status; }
     SyncMLStatus getStatus() const { return m_status; }
 
+    /**
+     * if not empty, then this was the virtual source which cause the
+     * current one to be included in the sync
+     */
+    void recordVirtualSource(const std::string &virtualsource) { m_virtualSource = virtualsource; }
+    std::string getVirtualSource() const { return m_virtualSource; }
+
     /** information about database dump before and after session */
     BackupReport m_backupBefore, m_backupAfter;
 
@@ -248,6 +268,7 @@ class SyncSourceReport {
     bool m_first;
     bool m_resume;
     SyncMLStatus m_status;
+    std::string m_virtualSource;
 };
 
 class SyncReport : public std::map<std::string, SyncSourceReport> {
@@ -262,6 +283,8 @@ class SyncReport : public std::map<std::string, SyncSourceReport> {
         m_status(STATUS_OK)
         {}
 
+    typedef std::pair<std::string, SyncSourceReport> SourceReport_t;
+
     void addSyncSourceReport(const std::string &name,
                              const SyncSourceReport &report) {
         (*this)[name] = report;
@@ -322,7 +345,7 @@ class SyncReport : public std::map<std::string, SyncSourceReport> {
      * @return explanation, empty string if list of sources is empty
      */
     static std::string slowSyncExplanation(const std::string &peer,
-                                           const std::list<std::string> &sources);
+                                           const std::set<std::string> &sources);
 
     /**
      * Produces a non-localized explanation for recovering from unexpected 
index 4a82691..35e1157 100644 (file)
@@ -309,11 +309,20 @@ string SyncSource::backendsDebug() {
     return scannedModules.debug.str();
 }
 
-SyncSource *SyncSource::createSource(const SyncSourceParams &params, bool error)
+SyncSource *SyncSource::createSource(const SyncSourceParams &params, bool error, SyncConfig *config)
 {
     string sourceTypeString = getSourceTypeString(params.m_nodes);
     SourceType sourceType = getSourceType(params.m_nodes);
 
+    if (sourceType.m_backend == "virtual") {
+        SyncSource *source = NULL;
+        source = new VirtualSyncSource(params, config);
+        if (error && !source) {
+            SyncContext::throwError(params.m_name + ": virtual source cannot be instantiated");
+        }
+        return source;
+    }
+
     const SourceRegistry &registry(getSourceRegistry());
     BOOST_FOREACH(const RegisterSyncSource *sourceInfos, registry) {
         SyncSource *source = sourceInfos->m_create(params);
@@ -353,6 +362,51 @@ SyncSource *SyncSource::createTestingSource(const string &name, const string &ty
     return createSource(params, error);
 }
 
+VirtualSyncSource::VirtualSyncSource(const SyncSourceParams &params, SyncConfig *config) :
+    DummySyncSource(params)
+{
+    if (config) {
+        BOOST_FOREACH(std::string name, getMappedSources()) {
+            SyncSourceNodes source = config->getSyncSourceNodes(name);
+            SyncSourceParams params(name, source);
+            boost::shared_ptr<SyncSource> syncSource(createSource(params, true, config));
+            m_sources.push_back(syncSource);
+        }
+    }
+}
+
+void VirtualSyncSource::open()
+{
+    getDataTypeSupport();
+    BOOST_FOREACH(boost::shared_ptr<SyncSource> &source, m_sources) {
+        source->open();
+    }
+}
+
+void VirtualSyncSource::close()
+{
+    BOOST_FOREACH(boost::shared_ptr<SyncSource> &source, m_sources) {
+        source->close();
+    }
+}
+
+std::vector<std::string> VirtualSyncSource::getMappedSources()
+{
+    std::string evoSyncSource = getDatabaseID();
+    std::vector<std::string> mappedSources = unescapeJoinedString (evoSyncSource, ',');
+    return mappedSources;
+}
+
+std::string VirtualSyncSource::getDataTypeSupport()
+{
+    string datatypes;
+    SourceType sourceType = getSourceType();
+    string type = sourceType.m_format;
+
+    datatypes = getDataTypeSupport(type, sourceType.m_forceFormat);
+    return datatypes;
+}
+
 void SyncSourceSession::init(SyncSource::Operations &ops)
 {
     ops.m_startDataRead = boost::bind(&SyncSourceSession::startDataRead, this, _1, _2);
@@ -481,44 +535,55 @@ void SyncSourceSerialize::getSynthesisInfo(SynthesisInfo &info,
     if (!sourceType.m_format.empty()) {
         type = sourceType.m_format;
     }
+    info.m_datatypes = getDataTypeSupport(type, sourceType.m_forceFormat);
+}
+
+std::string SyncSourceBase::getDataTypeSupport(const std::string &type,
+                                               bool forceFormat)
+{
+    std::string datatypes;
 
     if (type == "text/x-vcard:2.1" || type == "text/x-vcard") {
-        info.m_datatypes =
+        datatypes =
             "        <use datatype='vCard21' mode='rw' preferred='yes'/>\n";
-        if (!sourceType.m_forceFormat) {
-            info.m_datatypes +=
+        if (!forceFormat) {
+            datatypes +=
                 "        <use datatype='vCard30' mode='rw'/>\n";
         }
     } else if (type == "text/vcard:3.0" || type == "text/vcard") {
-        info.m_datatypes =
+        datatypes =
             "        <use datatype='vCard30' mode='rw' preferred='yes'/>\n";
-        if (!sourceType.m_forceFormat) {
-            info.m_datatypes +=
+        if (!forceFormat) {
+            datatypes +=
                 "        <use datatype='vCard21' mode='rw'/>\n";
         }
     } else if (type == "text/x-vcalendar:1.0" || type == "text/x-vcalendar"
              || type == "text/x-calendar:1.0" || type == "text/x-calendar") {
-        info.m_datatypes =
+        datatypes =
             "        <use datatype='vcalendar10' mode='rw' preferred='yes'/>\n";
-        if (!sourceType.m_forceFormat) {
-            info.m_datatypes +=
+        if (!forceFormat) {
+            datatypes +=
                 "        <use datatype='icalendar20' mode='rw'/>\n";
         }
     } else if (type == "text/calendar:2.0" || type == "text/calendar") {
-        info.m_datatypes =
+        datatypes =
             "        <use datatype='icalendar20' mode='rw' preferred='yes'/>\n";
-        if (!sourceType.m_forceFormat) {
-            info.m_datatypes +=
+        if (!forceFormat) {
+            datatypes +=
                 "        <use datatype='vcalendar10' mode='rw'/>\n";
         }
     } else if (type == "text/plain:1.0" || type == "text/plain") {
         // note10 are the same as note11, so ignore force format
-        info.m_datatypes =
+        datatypes =
             "        <use datatype='note10' mode='rw' preferred='yes'/>\n"
             "        <use datatype='note11' mode='rw'/>\n";
+    } else if (type.empty()) {
+        throwError("no MIME type configured");
     } else {
         throwError(string("configured MIME type not supported: ") + type);
     }
+
+    return datatypes;
 }
 
 sysync::TSyError SyncSourceSerialize::readItemAsKey(sysync::cItemID aID, sysync::KeyH aItemKey)
@@ -554,66 +619,199 @@ void SyncSourceSerialize::init(SyncSource::Operations &ops)
                                         this, _1, _2, _3);
 }
 
+/**
+ * Mapping from Hash() value to file.
+ */
+class ItemCache
+{
+public:
+#ifdef USE_SHA256
+    typedef std::string Hash_t;
+    Hash_t hashFunc(const std::string &data) { return SHA_256(data); }
+#else
+    typedef unsigned long Hash_t;
+    Hash_t hashFunc(const std::string &data) { return Hash(data); }
+#endif
+    typedef unsigned long Counter_t;
+
+    /** mark the algorithm used for the hash via different suffices */
+    static const char *m_hashSuffix;
+
+    /**
+     * Collect information about stored hashes. Provides
+     * access to file name via hash.
+     *
+     * If no hashes were written (as in an old SyncEvoltion
+     * version), we could read the files to recreate the
+     * hashes. This is not done because it won't occur
+     * often enough.
+     *
+     * Hashes are also not verified. Users should better
+     * not edit them or file contents...
+     *
+     * @param oldBackup     existing backup to read; may be empty
+     */
+    void init(const SyncSource::Operations::ConstBackupInfo &oldBackup)
+    {
+        m_hash2counter.clear();
+        m_dirname = oldBackup.m_dirname;
+        if (m_dirname.empty() || !oldBackup.m_node) {
+            return;
+        }
+
+        long numitems;
+        if (!oldBackup.m_node->getProperty("numitems", numitems)) {
+            return;
+        }
+        for (long counter = 1; counter <= numitems; counter++) {
+            stringstream key;
+            key << counter << m_hashSuffix;
+            Hash_t hash;
+            if (oldBackup.m_node->getProperty(key.str(), hash)) {
+                m_hash2counter[hash] = counter;
+            }
+        }
+    }
+
+    /**
+     * create file name for a specific hash, empty if no such hash
+     */
+    string getFilename(Hash_t hash)
+    {
+        Map_t::const_iterator it = m_hash2counter.find(hash);
+        if (it != m_hash2counter.end()) {
+            stringstream dirname;
+            dirname << m_dirname << "/" << it->second;
+            return dirname.str();
+        } else {
+            return "";
+        }
+    }
+
+private:
+    typedef std::map<Hash_t, Counter_t> Map_t;
+    Map_t m_hash2counter;
+    string m_dirname;
+};
+
+const char *ItemCache::m_hashSuffix =
+#ifdef USE_SHA256
+    "-sha256"
+#else
+    "-hash"
+#endif
+;
 
-void SyncSourceRevisions::backupData(const string &dir, ConfigNode &node, BackupReport &report)
+void SyncSourceRevisions::initRevisions()
 {
-    RevisionMap_t revisions;
-    listAllItems(revisions);
+    if (!m_revisionsSet) {
+        listAllItems(m_revisions);
+        m_revisionsSet = true;
+    }
+}
+
+
+void SyncSourceRevisions::backupData(const SyncSource::Operations::ConstBackupInfo &oldBackup,
+                                     const SyncSource::Operations::BackupInfo &newBackup,
+                                     BackupReport &report)
+{
+    ItemCache cache;
+    cache.init(oldBackup);
+
+    bool startOfSync = newBackup.m_mode == SyncSource::Operations::BackupInfo::BACKUP_BEFORE;
+    RevisionMap_t buffer;
+    RevisionMap_t *revisions;
+    if (startOfSync) {
+        initRevisions();
+        revisions = &m_revisions;
+    } else {
+        listAllItems(buffer);
+        revisions = &buffer;
+    }
 
     unsigned long counter = 1;
     string item;
     errno = 0;
-    BOOST_FOREACH(const StringPair &mapping, revisions) {
+    BOOST_FOREACH(const StringPair &mapping, *revisions) {
         const string &uid = mapping.first;
         const string &rev = mapping.second;
         m_raw->readItemRaw(uid, item);
 
         stringstream filename;
-        filename << dir << "/" << counter;
+        filename << newBackup.m_dirname << "/" << counter;
+
+        ItemCache::Hash_t hash = cache.hashFunc(item);
+        string oldfilename = cache.getFilename(hash);
+        if (!oldfilename.empty()) {
+            // found old file with same content, reuse it via hardlink
+            if (link(oldfilename.c_str(), filename.str().c_str())) {
+                // Hard linking failed. Record this, then continue
+                // by ignoring the old file.
+                SE_LOG_DEBUG(NULL, NULL, "hard linking old %s new %s: %s",
+                             oldfilename.c_str(),
+                             filename.str().c_str(),
+                             strerror(errno));
+                oldfilename.clear();
+            }
+        }
 
-        ofstream out(filename.str().c_str());
-        out.write(item.c_str(), item.size());
-        out.close();
-        if (out.fail()) {
-            throwError(string("error writing ") + filename.str() + ": " + strerror(errno));
+        if (oldfilename.empty()) {
+            // write new file instead of reusing old one
+            ofstream out(filename.str().c_str());
+            out.write(item.c_str(), item.size());
+            out.close();
+            if (out.fail()) {
+                throwError(string("error writing ") + filename.str() + ": " + strerror(errno));
+            }
         }
 
         stringstream key;
         key << counter << "-uid";
-        node.setProperty(key.str(), uid);
+        newBackup.m_node->setProperty(key.str(), uid);
+        // clear() does not remove the existing content, which was
+        // intended here. This should have been key.str(""). As a
+        // result, keys for -rev are longer than intended because they
+        // start with the -uid part. We cannot change it now, because
+        // that would break compatibility with nodes that use the
+        // older, longer keys for -rev.
         key.clear();
         key << counter << "-rev";
-        node.setProperty(key.str(), rev);
+        newBackup.m_node->setProperty(key.str(), rev);
+        key.str("");
+        key << counter << ItemCache::m_hashSuffix;
+        newBackup.m_node->setProperty(key.str(), hash);
 
         counter++;
     }
 
     stringstream value;
     value << counter - 1;
-    node.setProperty("numitems", value.str());
-    node.flush();
+    newBackup.m_node->setProperty("numitems", value.str());
+    newBackup.m_node->flush();
 
     report.setNumItems(counter - 1);
 }
 
-void SyncSourceRevisions::restoreData(const string &dir, const ConfigNode &node, bool dryrun, SyncSourceReport &report)
+void SyncSourceRevisions::restoreData(const SyncSource::Operations::ConstBackupInfo &oldBackup,
+                                      bool dryrun,
+                                      SyncSourceReport &report)
 {
     RevisionMap_t revisions;
     listAllItems(revisions);
 
     long numitems;
     string strval;
-    strval = node.readProperty("numitems");
+    strval = oldBackup.m_node->readProperty("numitems");
     stringstream stream(strval);
     stream >> numitems;
 
     for (long counter = 1; counter <= numitems; counter++) {
         stringstream key;
         key << counter << "-uid";
-        string uid = node.readProperty(key.str());
+        string uid = oldBackup.m_node->readProperty(key.str());
         key.clear();
         key << counter << "-rev";
-        string rev = node.readProperty(key.str());
+        string rev = oldBackup.m_node->readProperty(key.str());
         RevisionMap_t::iterator it = revisions.find(uid);
         report.incrementItemStat(report.ITEM_LOCAL,
                                  report.ITEM_ANY,
@@ -625,7 +823,7 @@ void SyncSourceRevisions::restoreData(const string &dir, const ConfigNode &node,
         } else {
             // add or update, so need item
             stringstream filename;
-            filename << dir << "/" << counter;
+            filename << oldBackup.m_dirname << "/" << counter;
             string data;
             if (!ReadFile(filename.str(), data)) {
                 throwError(StringPrintf("restoring %s from %s failed: could not read file",
@@ -691,10 +889,9 @@ void SyncSourceRevisions::restoreData(const string &dir, const ConfigNode &node,
 
 void SyncSourceRevisions::detectChanges(ConfigNode &trackingNode)
 {
-    RevisionMap_t revisions;
-    listAllItems(revisions);
+    initRevisions();
 
-    BOOST_FOREACH(const StringPair &mapping, revisions) {
+    BOOST_FOREACH(const StringPair &mapping, m_revisions) {
         const string &uid = mapping.first;
         const string &revision = mapping.second;
 
@@ -771,13 +968,14 @@ void SyncSourceRevisions::init(SyncSourceRaw *raw,
     m_del = del;
     m_modTimeStamp = 0;
     m_revisionAccuracySeconds = granularity;
+    m_revisionsSet = false;
     if (raw) {
         ops.m_backupData = boost::bind(&SyncSourceRevisions::backupData,
                                        this, _1, _2, _3);
     }
     if (raw && del) {
         ops.m_restoreData = boost::bind(&SyncSourceRevisions::restoreData,
-                                        this, _1, _2, _3, _4);
+                                        this, _1, _2, _3);
     }
     ops.m_endSession.push_back(boost::bind(&SyncSourceRevisions::sleepSinceModification,
                                            this));
index 9b6d7fe..be6305d 100644 (file)
@@ -700,6 +700,21 @@ class SyncSourceBase : public Logger {
      */
     virtual void getSynthesisInfo(SynthesisInfo &info,
                                   XMLConfigFragments &fragments) = 0;
+
+    /**
+     * utility code: creates Synthesis <use datatype=...>
+     * statements, using the predefined vCard21/vCard30/vcalendar10/icalendar20
+     * types. Throws an error if no suitable result can be returned (empty or invalid type)
+     *
+     * @param type         the format specifier as used in SyncEvolution configs, with and without version
+     *                     (text/x-vcard:2.1, text/x-vcard, text/x-vcalendar, text/calendar, text/plain, ...);
+     *                     see SourceType::m_format
+     * @param forceFormat  if true, then don't allow alternative formats (like vCard 3.0 in addition to 2.1);
+     *                     see SourceType::m_force
+     * @return generated XML fragment
+     */
+    std::string getDataTypeSupport(const std::string &type,
+                                   bool forceFormat);
 };
 
 /**
@@ -720,7 +735,8 @@ class SyncSource : virtual public SyncSourceBase, public SyncSourceConfig, publi
  public:
     SyncSource(const SyncSourceParams &params) :
         SyncSourceConfig(params.m_name, params.m_nodes),
-        m_numDeleted(0)
+        m_numDeleted(0),
+        m_forceSlowSync(false)
         {
         }
     virtual ~SyncSource() {}
@@ -755,8 +771,17 @@ class SyncSource : virtual public SyncSourceBase, public SyncSourceConfig, publi
     /**
      * Actually opens the data source specified in the constructor,
      * will throw the normal exceptions if that fails. Should
-     * not modify the state of the sync source: that can be deferred
-     * until the server is also ready and beginSync() is called.
+     * not modify the state of the sync source.
+     *
+     * The expectation is that this call is fairly light-weight, but
+     * does enough checking to determine whether the source is
+     * usable. More expensive operations (like determining changes)
+     * should be done in the m_startDataRead callback (bound to
+     * beginSync() in some of the utility classes).
+     *
+     * In clients, it will be called for all sources before
+     * the sync starts. In servers, it is called for each source once
+     * the client asks for it, but not sooner.
      */
     virtual void open() = 0;
 
@@ -775,22 +800,77 @@ class SyncSource : virtual public SyncSourceBase, public SyncSourceConfig, publi
      */
     struct Operations {
         /**
-         * Dump all data from source unmodified into the given directory.
-         * The ConfigNode can be used to store meta information needed for
-         * restoring that state. Both directory and node are empty.
+         * The caller determines where item data is stored (m_dirname)
+         * and where meta information about them (m_node). The callee
+         * then can use both arbitrarily. As an additional hint,
+         * m_mode specifies why and when the backup is made, which
+         * is useful to determine whether information can be reused.
+         */
+        struct BackupInfo {
+            enum Mode {
+                BACKUP_BEFORE,   /**< directly at start of sync */
+                BACKUP_AFTER,    /**< directly after sync */
+                BACKUP_OTHER
+            } m_mode;
+            string m_dirname;
+            boost::shared_ptr<ConfigNode> m_node;
+            BackupInfo() {}
+            BackupInfo(Mode mode,
+                       const string &dirname,
+                       const boost::shared_ptr<ConfigNode> &node) :
+                m_mode(mode),
+                m_dirname(dirname),
+                m_node(node)
+            {}
+        };
+        struct ConstBackupInfo {
+            BackupInfo::Mode m_mode;
+            string m_dirname;
+            boost::shared_ptr<const ConfigNode> m_node;
+            ConstBackupInfo() {}
+            ConstBackupInfo(BackupInfo::Mode mode,
+                            const string &dirname,
+                            const boost::shared_ptr<const ConfigNode> &node) :
+                m_mode(mode),
+                m_dirname(dirname),
+                m_node(node)
+            {}
+        };
+
+        /**
+         * Dump all data from source unmodified into the given backup location.
          * Information about the created backup is added to the
          * report.
          *
-         * Required for the backup/restore functionality in SyncEvolution,
-         * not for syncing itself.
+         * Required for the backup/restore functionality in
+         * SyncEvolution, not for syncing itself. But typically it is
+         * called before syncing (can be turned off by users), so
+         * implementations can reuse the information gathered while
+         * making a backup in later operations.
+         *
+         * @param previous     the most recent backup, empty m_dirname if none
+         * @param next         the backup which is to be created, directory and node are empty
+         * @param report       to be filled with information about backup (number of items, etc.)
          */
-        typedef void (BackupData_t)(const string &dirname, ConfigNode &node, BackupReport &report);
+        typedef void (BackupData_t)(const ConstBackupInfo &oldBackup,
+                                    const BackupInfo &newBackup,
+                                    BackupReport &report);
         boost::function<BackupData_t> m_backupData;
 
         /**
          * Restore database from data stored in backupData().
+         * If possible don't touch items which are the same as in the
+         * backup, to mimimize impact on future incremental syncs.
+         *
+         * @param oldBackup    the backup which is to be restored
+         * @param dryrun       pretend to restore and fill in report, without
+         *                     actually touching backend data
+         * @param report       to be filled with information about restore
+         *                     (number of total items and changes)
          */
-        typedef void (RestoreData_t)(const string &dirname, const ConfigNode &node, bool dryrun, SyncSourceReport &report);
+        typedef void (RestoreData_t)(const ConstBackupInfo &oldBackup,
+                                     bool dryrun,
+                                     SyncSourceReport &report);
         boost::function<RestoreData_t> m_restoreData;
 
         /**
@@ -810,6 +890,20 @@ class SyncSource : virtual public SyncSourceBase, public SyncSourceConfig, publi
         boost::function<CheckStatus_t> m_checkStatus;
 
         /**
+         * A quick check whether the source currently has data.
+         *
+         * If this cannot be determined easily, don't provide the
+         * operation. The information is currently only used to
+         * determine whether a slow sync should be allowed. If
+         * the operation is not provided, the assumption is that
+         * there is local data, which disables the "allow slow
+         * sync for empty databases" heuristic and forces the user
+         * to choose.
+         */
+        typedef bool (IsEmpty_t)();
+        boost::function<IsEmpty_t> m_isEmpty;
+
+        /**
          * Synthesis DB API callbacks. For documentation see the
          * Synthesis API specification (PDF and/or sync_dbapi.h).
          *
@@ -817,14 +911,20 @@ class SyncSource : virtual public SyncSourceBase, public SyncSourceConfig, publi
          * to be part of a sync session.
          */
         /**@{*/
+        typedef void (Callback_t)();
+        typedef boost::function<Callback_t> CallbackFunctor_t;
+        typedef std::list<CallbackFunctor_t> Callbacks_t;
+
+        /** all of these functions will be called before accessing
+            the source's data for the first time, i.e., before m_startDataRead */
+        Callbacks_t m_startAccess;
+
         typedef sysync::TSyError (StartDataRead_t)(const char *lastToken, const char *resumeToken);
         boost::function<StartDataRead_t> m_startDataRead;
 
-        typedef void (Callback_t)();
-        typedef boost::function<Callback_t> CallbackFunctor_t;
         /** all of these functions will be called directly after
             m_startDataRead() returned successfully */
-        std::list<CallbackFunctor_t> m_startSession;
+        Callbacks_t m_startSession;
 
         typedef sysync::TSyError (EndDataRead_t)();
         boost::function<EndDataRead_t> m_endDataRead;
@@ -894,6 +994,12 @@ class SyncSource : virtual public SyncSourceBase, public SyncSourceConfig, publi
         /**@}*/
     };
     const Operations &getOperations() { return m_operations; }
+
+    /**
+     * outside users of the source are only allowed to add callbacks,
+     * not overwrite arbitrary operations
+     */
+    void addCallback(Operations::CallbackFunctor_t callback, Operations::Callbacks_t Operations::* where) { (m_operations.*where).push_back(callback); }
         
     /**
      * closes the data source so that it can be reopened
@@ -926,10 +1032,12 @@ class SyncSource : virtual public SyncSourceBase, public SyncSourceConfig, publi
      * source type specified in the params.m_nodes.m_configNode
      *
      * @param error    throw a runtime error describing what the problem is if no matching source is found
-     * @return NULL if no source can handle the given type
+     * @param config   optional, needed for intantiating virtual sources
+     * @return valid instance, NULL if no source can handle the given type (only when error==false)
      */
     static SyncSource *createSource(const SyncSourceParams &params,
-                                    bool error = true);
+                                    bool error = true,
+                                    SyncConfig *config = NULL);
 
     /**
      * Factory function for a SyncSource with the given name
@@ -972,6 +1080,16 @@ class SyncSource : virtual public SyncSourceBase, public SyncSourceConfig, publi
     virtual void setNumDeleted(long num) { m_numDeleted = num; }
     virtual void incrementNumDeleted() { m_numDeleted++; }
 
+    /**
+     * Set to true in SyncContext::initSAN() when a SyncML server has
+     * to force a client into slow sync mode. This is necessary because
+     * the server cannot request that mode (missing in the standard).
+     * Forcing the slow sync mode is done via a FORCESLOWSYNC() macro
+     * call in an <alertscript>.
+     */
+    void setForceSlowSync(bool forceSlowSync) { m_forceSlowSync = forceSlowSync; }
+    bool getForceSlowSync() const { return m_forceSlowSync; }
+
  protected:
     Operations m_operations;
 
@@ -985,6 +1103,8 @@ class SyncSource : virtual public SyncSourceBase, public SyncSourceConfig, publi
      */
     long m_numDeleted;
 
+    bool m_forceSlowSync;
+
     /**
      * Interface pointer for this sync source, allocated for us by the
      * Synthesis engine and registered here by
@@ -1018,60 +1138,39 @@ class DummySyncSource : public SyncSource
 };
 
 /**
- * Virtual SyncSources
+ * A special source which combines one or more real sources.
+ * Most of the special handling for that is in SyncContext.cpp.
+ *
+ * This class can be instantiated, opened and closed if and only if
+ * the underlying sources also support that.
  */
 class VirtualSyncSource : public DummySyncSource 
 {
+    std::vector< boost::shared_ptr<SyncSource> > m_sources;
+
 public:
-    VirtualSyncSource(const SyncSourceParams &params) :
-       DummySyncSource(params) {}
-
-    std::string getDataTypeSupport() {
-        string datatypes;
-        SourceType sourceType = getSourceType();
-        string type = sourceType.m_format;
-
-        if (type.empty()) {
-            return "";
-        } else if (type == "text/x-vcard:2.1" || type == "text/x-vcard") {
-            datatypes =
-                "        <use datatype='vCard21' mode='rw' preferred='yes'/>\n";
-            if (!sourceType.m_forceFormat) {
-                datatypes +=
-                    "        <use datatype='vCard30' mode='rw'/>\n";
-            }
-        } else if (type == "text/vcard:3.0" || type == "text/vcard") {
-            datatypes =
-                "        <use datatype='vCard30' mode='rw' preferred='yes'/>\n";
-            if (!sourceType.m_forceFormat) {
-                datatypes +=
-                    "        <use datatype='vCard21' mode='rw'/>\n";
-            }
-        } else if (type == "text/x-vcalendar:1.0" || type == "text/x-vcalendar" 
-                  || type == "text/x-calendar:1.0" || type == "text/x-calendar") {
-            datatypes =
-                "        <use datatype='vcalendar10' mode='rw' preferred='yes'/>\n";
-            if (!sourceType.m_forceFormat) {
-                datatypes +=
-                    "        <use datatype='icalendar20' mode='rw'/>\n";
-            }
-        } else if (type == "text/calendar:2.0" || type == "text/calendar") {
-            datatypes =
-                "        <use datatype='icalendar20' mode='rw' preferred='yes'/>\n";
-            if (!sourceType.m_forceFormat) {
-                datatypes +=
-                    "        <use datatype='vcalendar10' mode='rw'/>\n";
-            }
-        } else if (type == "text/plain:1.0" || type == "text/plain") {
-            // note10 are the same as note11, so ignore force format
-            datatypes =
-                "        <use datatype='note10' mode='rw' preferred='yes'/>\n"
-                "        <use datatype='note11' mode='rw'/>\n";
-        } else {
-            throwError(string("configured MIME type not supported: ") + type);
-        }
-        return datatypes;
-    }
+    /**
+     * @param config   optional: when given, the constructor will instantiate the
+     *                 referenced underlying sources and check them in open()
+     */
+    VirtualSyncSource(const SyncSourceParams &params, SyncConfig *config = NULL);
+
+    /** opens underlying sources and checks config by calling getDataTypeSupport() */
+    virtual void open();
+    virtual void close();
+
+    /**
+     * returns array with source names that are referenced by this
+     * virtual source
+     */
+    std::vector<std::string> getMappedSources();
+
+    /**
+     * returns <use datatype=...> statements for XML config,
+     * throws error if not configured correctly
+     */
+    std::string getDataTypeSupport();
+    using SyncSourceBase::getDataTypeSupport;
 };
 
 /**
@@ -1384,6 +1483,10 @@ class SyncSourceRevisions : virtual public SyncSourceChanges, virtual public Syn
      * should not throw errors when it cannot create a non-empty
      * string. The caller of this method will detect situations where
      * a non-empty string is necessary and none was provided.
+     *
+     * This call is typically only invoked only once during the
+     * lifetime of a source. The result returned in that invocation is
+     * used throught the session.
      */
     virtual void listAllItems(RevisionMap_t &revisions) = 0;
 
@@ -1438,18 +1541,27 @@ class SyncSourceRevisions : virtual public SyncSourceChanges, virtual public Syn
     SyncSourceDelete *m_del;
     int m_revisionAccuracySeconds;
 
+    /** buffers the result of the initial listAllItems() call */
+    RevisionMap_t m_revisions;
+    bool m_revisionsSet;
+    void initRevisions();
+
     /**
      * Dump all data from source unmodified into the given directory.
      * The ConfigNode can be used to store meta information needed for
      * restoring that state. Both directory and node are empty.
      */
-    void backupData(const string &dirname, ConfigNode &node, BackupReport &report);
+    void backupData(const SyncSource::Operations::ConstBackupInfo &oldBackup,
+                    const SyncSource::Operations::BackupInfo &newBackup,
+                    BackupReport &report);
 
     /**
      * Restore database from data stored in backupData(). Will be
      * called inside open()/close() pair. beginSync() is *not* called.
      */
-    void restoreData(const string &dirname, const ConfigNode &node, bool dryrun, SyncSourceReport &report);
+    void restoreData(const SyncSource::Operations::ConstBackupInfo &oldBackup,
+                     bool dryrun,
+                     SyncSourceReport &report);
 
     /**
      * Increments the time stamp of the latest database modification,
index c269c6f..ea91a07 100644 (file)
@@ -637,6 +637,10 @@ TSyError SyncEvolution_StartDataRead( CContext aContext, cAppCharP   lastToken,
     }
     TSyError res = LOCERR_OK;
     try {
+        BOOST_FOREACH(const SyncSource::Operations::CallbackFunctor_t &callback,
+                      source->getOperations().m_startAccess) {
+            callback();
+        }
         if (source->getOperations().m_startDataRead) {
             res = source->getOperations().m_startDataRead(lastToken, resumeToken);
         }
index 6c98225..559e002 100644 (file)
@@ -34,6 +34,7 @@ TrackingSyncSource::TrackingSyncSource(const SyncSourceParams &params,
                                         boost::shared_ptr<ConfigNode>(new SafeConfigNode(params.m_nodes.getTrackingNode()))))
 {
     m_operations.m_checkStatus = boost::bind(&TrackingSyncSource::checkStatus, this, _1);
+    m_operations.m_isEmpty = boost::bind(&TrackingSyncSource::isEmpty, this);
     SyncSourceRevisions::init(this, this, granularitySeconds, m_operations);
 }
 
index b35fb40..fe58d70 100644 (file)
@@ -82,6 +82,14 @@ class TrackingSyncSource : public TestingSyncSource,
     ~TrackingSyncSource() {}
 
     /**
+     * ConfigNode used for change tracking in SyncSourceRevisions.
+     * Derived classes might need that when implementing operations
+     * which have side effects on other items (for example,
+     * EvolutionCalendarSource::removeItem()).
+     */
+    ConfigNode &getTrackingNode() { return *m_trackingNode; }
+
+    /**
      * returns a list of all know sources for the kind of items
      * supported by this sync source
      */
@@ -90,12 +98,28 @@ class TrackingSyncSource : public TestingSyncSource,
     /**
      * Actually opens the data source specified in the constructor,
      * will throw the normal exceptions if that fails. Should
-     * not modify the state of the sync source: that can be deferred
-     * until the server is also ready and beginSync() is called.
+     * not modify the state of the sync source.
+     *
+     * The expectation is that this call is fairly light-weight, but
+     * does enough checking to determine whether the source is
+     * usable. More expensive operations (like determining changes)
+     * should be done in the beginSync() callback.
+     *
+     * In clients, it will be called for all sources before
+     * the sync starts. In servers, it is called for each source once
+     * the client asks for it, but not sooner.
      */
     virtual void open() = 0;
 
     /**
+     * A quick check whether the source currently has data. Currently
+     * used as part of the "allow slow sync" checking after open() and
+     * before beginSync(). Returning false is acceptable when it is
+     * uncertain and too expensive to check.
+     */
+    virtual bool isEmpty() = 0;
+
+    /**
      * fills the complete mapping from LUID to revision string of all
      * currently existing items
      *
diff --git a/src/syncevo/configs/Makefile.am b/src/syncevo/configs/Makefile.am
new file mode 100644 (file)
index 0000000..fc449bf
--- /dev/null
@@ -0,0 +1,2 @@
+xmldir = $(datadir)/syncevolution/xml
+nobase_dist_xml_DATA = $(shell cd $(srcdir) && find * -name '*.xml') update-samples.pl
diff --git a/src/syncevo/configs/Makefile.in b/src/syncevo/configs/Makefile.in
new file mode 100644 (file)
index 0000000..2a696fd
--- /dev/null
@@ -0,0 +1,469 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/syncevo/configs
+DIST_COMMON = README $(nobase_dist_xml_DATA) $(srcdir)/Makefile.am \
+       $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4-repo/ax_boost_base.m4 \
+       $(top_srcdir)/m4/intltool.m4 $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(xmldir)"
+nobase_dist_xmlDATA_INSTALL = $(install_sh_DATA)
+DATA = $(nobase_dist_xml_DATA)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ADDRESSBOOK_CFLAGS = @ADDRESSBOOK_CFLAGS@
+ADDRESSBOOK_LIBS = @ADDRESSBOOK_LIBS@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BACKEND_CPPFLAGS = @BACKEND_CPPFLAGS@
+BACKEND_DEFINES = @BACKEND_DEFINES@
+BLUEZ_CFLAGS = @BLUEZ_CFLAGS@
+BLUEZ_LIBS = @BLUEZ_LIBS@
+BOOST_CPPFLAGS = @BOOST_CPPFLAGS@
+BOOST_LDFLAGS = @BOOST_LDFLAGS@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CORE_LDADD_DEP = @CORE_LDADD_DEP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPUNIT_CXXFLAGS = @CPPUNIT_CXXFLAGS@
+CPPUNIT_LDFLAGS = @CPPUNIT_LDFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_BINDING_TOOL = @DBUS_BINDING_TOOL@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_CFLAGS = @DBUS_GLIB_CFLAGS@
+DBUS_GLIB_LIBS = @DBUS_GLIB_LIBS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SERVICES_DIR = @DBUS_SERVICES_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+EBOOK_CFLAGS = @EBOOK_CFLAGS@
+EBOOK_LIBS = @EBOOK_LIBS@
+ECAL_CFLAGS = @ECAL_CFLAGS@
+ECAL_LIBS = @ECAL_LIBS@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EPACKAGE_CFLAGS = @EPACKAGE_CFLAGS@
+EPACKAGE_LIBS = @EPACKAGE_LIBS@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+FILE_CFLAGS = @FILE_CFLAGS@
+FILE_LIBS = @FILE_LIBS@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
+GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
+GOBJECT_LIBS = @GOBJECT_LIBS@
+GREP = @GREP@
+GTHREAD_CFLAGS = @GTHREAD_CFLAGS@
+GTHREAD_LIBS = @GTHREAD_LIBS@
+GTK_2_18_CFLAGS = @GTK_2_18_CFLAGS@
+GTK_2_18_LIBS = @GTK_2_18_LIBS@
+GTK_BUILDER_CONV = @GTK_BUILDER_CONV@
+GUI_CFLAGS = @GUI_CFLAGS@
+GUI_DESKTOP_FILES = @GUI_DESKTOP_FILES@
+GUI_LIBS = @GUI_LIBS@
+GUI_PROGRAMS = @GUI_PROGRAMS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+KEYRING_2_20_CFLAGS = @KEYRING_2_20_CFLAGS@
+KEYRING_2_20_LIBS = @KEYRING_2_20_LIBS@
+KEYRING_CFLAGS = @KEYRING_CFLAGS@
+KEYRING_LIBS = @KEYRING_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
+LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
+LIBS = @LIBS@
+LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
+LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MCALB_CFLAGS = @MCALB_CFLAGS@
+MCALB_LIBS = @MCALB_LIBS@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MODIFY_SYNCCOMPARE = @MODIFY_SYNCCOMPARE@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SQLITE_CFLAGS = @SQLITE_CFLAGS@
+SQLITE_LIBS = @SQLITE_LIBS@
+STABLE_VERSION = @STABLE_VERSION@
+STRIP = @STRIP@
+SYNCEVOLUTION_CXXFLAGS = @SYNCEVOLUTION_CXXFLAGS@
+SYNCEVOLUTION_LDADD = @SYNCEVOLUTION_LDADD@
+SYNCEVOLUTION_LOCALEDIR = @SYNCEVOLUTION_LOCALEDIR@
+SYNCSOURCES = @SYNCSOURCES@
+SYNTHESIS = @SYNTHESIS@
+SYNTHESISSRC = @SYNTHESISSRC@
+SYNTHESIS_CFLAGS = @SYNTHESIS_CFLAGS@
+SYNTHESIS_DEP = @SYNTHESIS_DEP@
+SYNTHESIS_ENGINE = @SYNTHESIS_ENGINE@
+SYNTHESIS_LIB = @SYNTHESIS_LIB@
+SYNTHESIS_LIBS = @SYNTHESIS_LIBS@
+SYNTHESIS_SRC = @SYNTHESIS_SRC@
+SYNTHESIS_SUBDIR = @SYNTHESIS_SUBDIR@
+TRANSPORT_CFLAGS = @TRANSPORT_CFLAGS@
+TRANSPORT_LIBS = @TRANSPORT_LIBS@
+UNIQUE_CFLAGS = @UNIQUE_CFLAGS@
+UNIQUE_LIBS = @UNIQUE_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XMLRPC_CFLAGS = @XMLRPC_CFLAGS@
+XMLRPC_LIBS = @XMLRPC_LIBS@
+XSLT = @XSLT@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+backenddir = @backenddir@
+backendsearchdir = @backendsearchdir@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+xmldir = $(datadir)/syncevolution/xml
+nobase_dist_xml_DATA = $(shell cd $(srcdir) && find * -name '*.xml') update-samples.pl
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in:  $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+               && exit 0; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu  src/syncevo/configs/Makefile'; \
+       cd $(top_srcdir) && \
+         $(AUTOMAKE) --gnu  src/syncevo/configs/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure:  $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4):  $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+mostlyclean-libtool:
+       -rm -f *.lo
+
+clean-libtool:
+       -rm -rf .libs _libs
+install-nobase_dist_xmlDATA: $(nobase_dist_xml_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(xmldir)" || $(MKDIR_P) "$(DESTDIR)$(xmldir)"
+       @$(am__vpath_adj_setup) \
+       list='$(nobase_dist_xml_DATA)'; for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         $(am__vpath_adj) \
+         echo " $(nobase_dist_xmlDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(xmldir)/$$f'"; \
+         $(nobase_dist_xmlDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(xmldir)/$$f"; \
+       done
+
+uninstall-nobase_dist_xmlDATA:
+       @$(NORMAL_UNINSTALL)
+       @$(am__vpath_adj_setup) \
+       list='$(nobase_dist_xml_DATA)'; for p in $$list; do \
+         $(am__vpath_adj) \
+         echo " rm -f '$(DESTDIR)$(xmldir)/$$f'"; \
+         rm -f "$(DESTDIR)$(xmldir)/$$f"; \
+       done
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+       list='$(DISTFILES)'; \
+         dist_files=`for file in $$list; do echo $$file; done | \
+         sed -e "s|^$$srcdirstrip/||;t" \
+             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+       case $$dist_files in \
+         */*) $(MKDIR_P) `echo "$$dist_files" | \
+                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+                          sort -u` ;; \
+       esac; \
+       for file in $$dist_files; do \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         if test -d $$d/$$file; then \
+           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+           fi; \
+           cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+         else \
+           test -f $(distdir)/$$file \
+           || cp -p $$d/$$file $(distdir)/$$file \
+           || exit 1; \
+         fi; \
+       done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+       for dir in "$(DESTDIR)$(xmldir)"; do \
+         test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+       done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+       -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-nobase_dist_xmlDATA
+
+install-dvi: install-dvi-am
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-nobase_dist_xmlDATA
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+       distclean distclean-generic distclean-libtool distdir dvi \
+       dvi-am html html-am info info-am install install-am \
+       install-data install-data-am install-dvi install-dvi-am \
+       install-exec install-exec-am install-html install-html-am \
+       install-info install-info-am install-man \
+       install-nobase_dist_xmlDATA install-pdf install-pdf-am \
+       install-ps install-ps-am install-strip installcheck \
+       installcheck-am installdirs maintainer-clean \
+       maintainer-clean-generic mostlyclean mostlyclean-generic \
+       mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \
+       uninstall-nobase_dist_xmlDATA
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/syncevo/configs/README b/src/syncevo/configs/README
new file mode 100644 (file)
index 0000000..ea01f5c
--- /dev/null
@@ -0,0 +1,22 @@
+The sample configs contain common elements (datatypes, scripts, remote
+rules, debug settings) which are maintained as separate files in the
+corresponding directories. When modifying those common elements, run
+"update-samples.pl" in this directory to update the sample configs.
+
+The complete samples are under version control for several reasons:
+1. avoid dependency on Perl unless common elements need to be updated
+2. effect of changes on complete config show up in patches
+3. the file layout and unshared parts (<client> and <server>) are
+   determined by the sample configs
+
+The naming of common elements determines the order in which they get
+inserted. Files not ending in .xml are ignored. Elements that only
+apply to a client or server are stored in the corresponding sub
+directories, while the shared elements are in the
+"debug/scripting/datatypes/remoterules".
+
+It is a somewhat subjective choice which elements are stored in one
+file and which ones are split up. The three elements of a datatype
+definition (field list, profile, datatype) where split up because
+there might be multiple different profiles using the same field list
+and some users of these files might want to replace the default one.
diff --git a/src/syncevo/configs/datatypes/00vcard-fieldlist.xml b/src/syncevo/configs/datatypes/00vcard-fieldlist.xml
new file mode 100644 (file)
index 0000000..0f899b3
--- /dev/null
@@ -0,0 +1,109 @@
+    <!-- list of internal fields representing vCard data -->
+    <fieldlist name="contacts">
+      <field name="SYNCLVL" type="integer" compare="never"/>
+      <field name="REV" type="timestamp" compare="never" age="yes"/>
+
+      <!-- Name elements -->
+      <field name="N_LAST" type="string" compare="always"/>
+      <field name="N_FIRST" type="string" compare="always"/>
+      <field name="N_MIDDLE" type="string" compare="always"/>
+      <field name="N_PREFIX" type="string" compare="conflict"/>
+      <field name="N_SUFFIX" type="string" compare="conflict"/>
+      <field name="NICKNAME" type="string" compare="conflict"/>
+      <field name="TITLE" type="string" compare="conflict" merge="fillempty"/>
+
+      <field name="FN" type="string" compare="conflict" merge="fillempty"/>
+      <field name="FILE-AS" type="string" compare="conflict" merge="fillempty"/>
+
+      <field name="GENDER" type="string" compare="conflict" merge="fillempty"/>
+
+      <!-- categories and classification -->
+      <field name="CATEGORIES" array="yes" type="string" compare="conflict"/>
+
+      <!-- organisation -->
+      <field name="ORG_NAME" type="string" compare="slowsync" merge="fillempty"/>
+      <field name="ORG_DIVISION" type="string" compare="conflict" merge="fillempty"/>
+      <field name="ORG_OFFICE" type="string" compare="conflict" merge="fillempty"/>
+      <field name="ORG_TEAM" type="string" compare="conflict" merge="fillempty"/>
+      <field name="ROLE" type="string" compare="conflict" merge="fillempty"/>
+
+      <!-- birthday and anniversary (not necessarily the same) -->
+      <field name="BDAY" type="date" compare="conflict" merge="fillempty"/>
+      <field name="ANNIVERSARY" type="date" compare="conflict" merge="fillempty"/>
+
+      <!-- telephone numbers -->
+      <field name="TEL"         array="yes" type="telephone" compare="conflict"/>
+      <field name="TEL_FLAGS"   array="yes" type="integer"   compare="conflict"/> <!-- offset 0 -->
+      <field name="TEL_LABEL"   array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
+      <field name="TEL_ID"      array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
+      <field name="TEL_SLOT"    array="yes" type="integer"   compare="never"/>    <!-- offset 3 -->
+
+      <!-- emails -->
+      <field name="EMAIL"       array="yes" type="multiline" compare="conflict"/>
+      <field name="EMAIL_FLAGS" array="yes" type="integer"   compare="conflict"/> <!-- offset 0 -->
+      <field name="EMAIL_LABEL" array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
+      <field name="EMAIL_ID"    array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
+      <field name="EMAIL_SLOT"  array="yes" type="integer"   compare="never"/>    <!-- offset 3 -->
+
+      <!-- web addresses -->
+      <field name="WEB"         array="yes" type="string" compare="conflict"/>
+      <field name="WEB_FLAGS"   array="yes" type="integer"   compare="conflict"/> <!-- offset 0 -->
+      <field name="WEB_LABEL"   array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
+      <field name="WEB_ID"      array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
+
+      <!-- would be nicer to have as part of WEB, but parser/encoder does not support mapping
+           with more than one property per field -->
+      <field name="CALURI"      array="yes" type="string" compare="conflict"/>
+      <field name="FBURL"       array="yes" type="string" compare="conflict"/>
+      <field name="BLOGURL"     array="yes" type="string" compare="conflict"/>
+      <field name="VIDEOURL"    array="yes" type="string" compare="conflict"/>
+
+      <!-- related persons: should be turned into array, like WEB and CALURI/FBURL -->
+      <field name="MANAGER"    type="string" compare="conflict"/>
+      <field name="ASSISTANT"  type="string" compare="conflict"/>
+      <field name="SPOUSE"     type="string" compare="conflict"/>
+
+      <!-- does this person want HTML mails? Valid values are TRUE/FALSE; a "boolean"
+           type would be useful, maybe add that later. -->
+      <field name="WANTS_HTML"  type="string" compare="conflict"/>
+
+      <!-- chat handles: should be turned into one array, like WEB and CALURI/FBURL -->
+      <field name="AIM_HANDLE"        array="yes" type="string" compare="conflict"/>
+      <field name="AIM_SLOT"          array="yes" type="string" compare="conflict"/>
+      <field name="GADUGADU_HANDLE"        array="yes" type="string" compare="conflict"/>
+      <field name="GADUGADU_SLOT"          array="yes" type="string" compare="conflict"/>
+      <field name="GROUPWISE_HANDLE"        array="yes" type="string" compare="conflict"/>
+      <field name="GROUPWISE_SLOT"          array="yes" type="string" compare="conflict"/>
+      <field name="ICQ_HANDLE"        array="yes" type="string" compare="conflict"/>
+      <field name="ICQ_SLOT"          array="yes" type="string" compare="conflict"/>
+      <field name="JABBER_HANDLE"        array="yes" type="string" compare="conflict"/>
+      <field name="JABBER_SLOT"          array="yes" type="string" compare="conflict"/>
+      <field name="MSN_HANDLE"        array="yes" type="string" compare="conflict"/>
+      <field name="MSN_SLOT"          array="yes" type="string" compare="conflict"/>
+      <field name="YAHOO_HANDLE"        array="yes" type="string" compare="conflict"/>
+      <field name="YAHOO_SLOT"          array="yes" type="string" compare="conflict"/>
+      <field name="SKYPE_HANDLE"      array="yes" type="string" compare="conflict"/>
+      <field name="SKYPE_SLOT"        array="yes" type="string" compare="conflict"/>
+      <field name="SIP_HANDLE"        array="yes" type="string" compare="conflict"/>
+      <field name="SIP_SLOT"          array="yes" type="string" compare="conflict"/>
+
+      <!-- home address -->
+      <field name="ADR_STREET"        array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_ADDTL"         array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_STREET_FLAGS"  array="yes" type="integer"   compare="conflict"/> <!-- offset 0 (from ADR_STREET_FLAGS) -->
+      <field name="ADR_STREET_LABEL"  array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
+      <field name="ADR_STREET_ID"     array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
+      <field name="ADR_POBOX"         array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_CITY"          array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_REG"           array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_ZIP"           array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_COUNTRY"       array="yes" type="multiline" compare="conflict"/>
+
+      <!-- Note -->
+      <field name="NOTE" type="multiline" compare="conflict" merge="lines"/>
+
+      <!-- Photo -->
+      <field name="PHOTO" type="blob" compare="never" merge="fillempty"/>
+      <field name="PHOTO_TYPE" type="integer" compare="never" merge="fillempty"/>
+
+    </fieldlist>
diff --git a/src/syncevo/configs/datatypes/01vcard-profile.xml b/src/syncevo/configs/datatypes/01vcard-profile.xml
new file mode 100644 (file)
index 0000000..db3e852
--- /dev/null
@@ -0,0 +1,285 @@
+    <!-- vCard profile -->
+    <mimeprofile name="vCard" fieldlist="contacts">
+
+      <profile name="VCARD" nummandatory="0"> <!-- we allow records without "N" as Address book can store them -->
+        <property name="VERSION">
+          <value conversion="version"/>
+        </property>
+
+        <property onlyformode="standard" name="PRODID" mandatory="no">
+          <value conversion="prodid"/>
+        </property>
+
+        <property name="REV">
+          <value field="REV"/>
+        </property>
+
+        <property name="N" values="5" mandatory="yes"> <!-- Note: makes N parse and generate even if not in remote's CTCap -->
+          <value index="0" field="N_LAST"/>
+          <value index="1" field="N_FIRST"/>
+          <value index="2" field="N_MIDDLE"/>
+          <value index="3" field="N_PREFIX"/>
+          <value index="4" field="N_SUFFIX"/>
+        </property>
+
+        <property name="FN">
+          <value field="FN"/>
+        </property>
+
+        <property name="X-EVOLUTION-FILE-AS">
+          <value field="FILE-AS"/>
+        </property>
+
+        <property name="X-GENDER">
+          <value field="GENDER"/>
+        </property>
+
+        <!-- onlyformode="standard": not part of vCard 2.1, but some
+             peers (like the Funambol server) accept it anyway in
+             vCard 2.1 -->
+        <property name="NICKNAME">
+          <value field="NICKNAME"/>
+        </property>
+
+        <property name="TITLE">
+          <value field="TITLE"/>
+        </property>
+
+        <property name="CATEGORIES" values="list" valueseparator="," altvalueseparator=";" > <!-- non-standard, but 1:1 as in vCard 3.0 (NOT like in vCalendar 1.0, where separator is ";") -->
+          <value field="CATEGORIES"/>
+          <position field="CATEGORIES" repeat="array" increment="1" minshow="0"/>
+        </property>
+
+        <property name="ORG" values="4">
+          <value index="0" field="ORG_NAME"/>
+          <value index="1" field="ORG_DIVISION"/>
+          <value index="2" field="ORG_OFFICE"/>
+          <value index="3" field="ORG_TEAM"/>
+        </property>
+
+        <property name="ROLE">
+          <value field="ROLE"/>
+        </property>
+
+        <property name="TEL">
+          <value field="TEL"/>
+          <position field="TEL" repeat="array" increment="1" minshow="1"/>
+          <parameter name="TYPE" default="yes" positional="no" show="yes">
+            <value field="TEL_FLAGS" conversion="multimix" combine=",">
+              <enum name="HOME"     value="B0"/>
+              <enum name="WORK"     value="B1"/>
+              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
+              <enum name="VOICE"    value="B3"/>
+              <enum name="CELL"     value="B4"/>
+              <enum name="FAX"      value="B5"/>
+              <enum name="PAGER"    value="B6"/>
+              <enum name="PREF"     value="B7"/>
+              <enum name="CAR"      value="B8"/>
+              <enum name="X-EVOLUTION-CALLBACK" value="B9"/>
+              <enum name="X-EVOLUTION-RADIO" value="B10"/>
+              <enum name="X-EVOLUTION-TELEX" value="B11"/>
+              <enum name="X-EVOLUTION-TTYTDD" value="B12"/>
+
+              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+            </value>
+          </parameter>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="TEL_SLOT"/>
+          </parameter>
+        </property>
+
+        <property name="EMAIL">
+          <value field="EMAIL"/>
+          <position field="EMAIL" repeat="array" increment="1" minshow="1"/>
+          <parameter name="TYPE" default="yes" positional="no" show="yes">
+            <value field="EMAIL_FLAGS" conversion="multimix" combine=",">
+              <enum name="HOME"     value="B0"/>
+              <enum name="WORK"     value="B1"/>
+              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
+              <enum name="INTERNET" value="B3"/>
+
+              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+            </value>
+          </parameter>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="EMAIL_SLOT"/>
+          </parameter>
+        </property>
+
+        <property name="URL">
+          <value field="WEB"/>
+          <position field="WEB" repeat="array" increment="1" minshow="1"/>
+          <parameter name="TYPE" default="yes" positional="no" show="yes">
+            <value field="WEB_FLAGS" conversion="multimix" combine=",">
+              <enum name="HOME"     value="B0"/>
+              <enum name="WORK"     value="B1"/>
+              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
+              <enum name="PREF"     value="B3"/>
+
+              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+            </value>
+          </parameter>
+        </property>
+
+        <property name="CALURI" suppressempty="yes">
+          <value field="CALURI" show="yes"/>
+        </property>
+        <property name="FBURL" suppressempty="yes">
+          <value field="FBURL" show="yes"/>
+        </property>
+        <property name="X-EVOLUTION-BLOG-URL" suppressempty="yes">
+          <value field="BLOGURL" show="yes"/>
+        </property>
+        <property name="X-EVOLUTION-VIDEO-URL" suppressempty="yes">
+          <value field="VIDEOURL" show="yes"/>
+        </property>
+
+        <!-- item for SyncML server: EVOLUTION rule not active,
+             both X-EVOLUTION-MANAGER and X-MANAGER are sent.
+
+             item from SyncML server: EVOLUTION rule not active,
+             both X-EVOLUTION-MANAGER and X-MANAGER are checked,
+             but X-EVOLUTION-MANAGER later so that it overwrites
+             a value set earlier by X-MANAGER (if any). This is
+             a more or less arbitrary priority, chosen because
+             servers that know about SyncEvolution (ScheduleWorld,
+             Memotoo) use the X-EVOLUTION variant.
+
+             item to/from Evolution: EVOLUTION rule is active,
+             only X-EVOLUTION-MANAGER is used. -->
+        <property name="X-EVOLUTION-MANAGER" suppressempty="yes" delayedparsing="1">
+          <value field="MANAGER" show="yes"/>
+        </property>
+        <property name="X-MANAGER" suppressempty="yes" rule="EVOLUTION"/> <!-- disables the X-MANAGER for EVOLUTION -->
+        <property name="X-MANAGER" suppressempty="yes" rule="other">
+          <value field="MANAGER" show="yes"/>
+        </property>
+
+        <property name="X-EVOLUTION-ASSISTANT" suppressempty="yes" delayedparsing="1">
+          <value field="ASSISTANT" show="yes"/>
+        </property>
+        <property name="X-ASSISTANT" suppressempty="yes" rule="EVOLUTION"/>
+        <property name="X-ASSISTANT" suppressempty="yes" rule="other">
+          <value field="ASSISTANT" show="yes"/>
+        </property>
+
+        <property name="X-EVOLUTION-SPOUSE" suppressempty="yes" delayedparsing="1">
+          <value field="SPOUSE" show="yes"/>
+        </property>
+        <property name="X-SPOUSE" suppressempty="yes" rule="EVOLUTION"/>
+        <property name="X-SPOUSE" suppressempty="yes" rule="other">
+          <value field="SPOUSE" show="yes"/>
+        </property>
+
+        <property name="X-EVOLUTION-ANNIVERSARY" suppressempty="yes" delayedparsing="1">
+          <value field="ANNIVERSARY" show="yes"/>
+        </property>
+        <property name="X-ANNIVERSARY" suppressempty="yes" rule="EVOLUTION"/>
+        <property name="X-ANNIVERSARY" suppressempty="yes" rule="other">
+          <value field="ANNIVERSARY" show="yes"/>
+        </property>
+
+        <property name="X-MOZILLA-HTML">
+          <value field="WANTS_HTML" show="yes"/>
+        </property>
+
+        <property name="X-AIM" suppressempty="yes">
+          <value field="AIM_HANDLE"/>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="AIM_SLOT"/>
+          </parameter>
+        </property>
+        <property name="X-GADUGADU" suppressempty="yes">
+          <value field="GADUGADU_HANDLE"/>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="GADUGADU_SLOT"/>
+          </parameter>
+        </property>
+        <property name="X-GROUPWISE" suppressempty="yes">
+          <value field="GROUPWISE_HANDLE"/>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="GROUPWISE_SLOT"/>
+          </parameter>
+        </property>
+        <property name="X-ICQ" suppressempty="yes">
+          <value field="ICQ_HANDLE"/>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="ICQ_SLOT"/>
+          </parameter>
+        </property>
+        <property name="X-JABBER" suppressempty="yes">
+          <value field="JABBER_HANDLE"/>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="JABBER_SLOT"/>
+          </parameter>
+        </property>
+        <property name="X-MSN" suppressempty="yes">
+          <value field="MSN_HANDLE"/>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="MSN_SLOT"/>
+          </parameter>
+        </property>
+        <property name="X-YAHOO" suppressempty="yes">
+          <value field="YAHOO_HANDLE"/>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="YAHOO_SLOT"/>
+          </parameter>
+        </property>
+
+        <property name="X-SKYPE" suppressempty="yes"> 
+          <value field="SKYPE_HANDLE"/>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="SKYPE_SLOT"/>
+          </parameter>
+        </property>
+
+        <property name="X-SIP" suppressempty="yes">
+          <value field="SIP_HANDLE"/>
+          <parameter name="X-EVOLUTION-UI-SLOT" positional="no" show="yes">
+            <value field="SIP_SLOT"/>
+          </parameter>
+        </property>
+
+        <property name="ADR" values="7">
+          <value index="0" field="ADR_POBOX"/>
+          <value index="1" field="ADR_ADDTL"/>
+          <value index="2" field="ADR_STREET"/>
+          <value index="3" field="ADR_CITY"/>
+          <value index="4" field="ADR_REG"/>
+          <value index="5" field="ADR_ZIP"/>
+          <value index="6" field="ADR_COUNTRY"/>
+          <position field="ADR_POBOX" repeat="array" increment="1" minshow="1"/>
+          <parameter name="TYPE" default="yes" positional="no" show="yes">
+            <value field="ADR_STREET_FLAGS" conversion="multimix" combine=",">
+              <enum name="HOME"     value="B0"/>
+              <enum name="WORK"     value="B1"/>
+              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
+
+              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+            </value>
+          </parameter>
+        </property>
+
+        <property name="BDAY">
+          <value field="BDAY"/>
+        </property>
+
+        <property name="NOTE" filter="no">
+          <value field="NOTE"/>
+        </property>
+
+        <property name="PHOTO" filter="no">
+          <value field="PHOTO" conversion="BLOB_B64"/>
+          <parameter name="TYPE" default="no" show="yes">
+            <value field="PHOTO_TYPE">
+              <enum name="JPEG" value="0"/>
+            </value>
+          </parameter>
+        </property>
+
+      </profile>
+    </mimeprofile>
diff --git a/src/syncevo/configs/datatypes/02vcard-types.xml b/src/syncevo/configs/datatypes/02vcard-types.xml
new file mode 100644 (file)
index 0000000..9af4bb6
--- /dev/null
@@ -0,0 +1,19 @@
+
+
+    <!-- vCard 2.1 datatype, using vCard profile defined above -->
+    <datatype name="vCard21" basetype="vcard">
+      <version>2.1</version>
+      <use mimeprofile="vCard"/>
+     <incomingscript><![CDATA[
+        $VCARD_INCOMING_NAMECHANGE_SCRIPT
+      ]]></incomingscript>
+    </datatype>
+
+    <!-- vCard 3.0 datatype, using vCard profile defined above -->
+    <datatype name="vCard30" basetype="vcard">
+      <version>3.0</version>
+      <use mimeprofile="vCard"/>
+      <incomingscript><![CDATA[
+        $VCARD_INCOMING_NAMECHANGE_SCRIPT
+      ]]></incomingscript>
+    </datatype>
diff --git a/src/syncevo/configs/datatypes/10calendar-fieldlist.xml b/src/syncevo/configs/datatypes/10calendar-fieldlist.xml
new file mode 100644 (file)
index 0000000..8b1f82a
--- /dev/null
@@ -0,0 +1,72 @@
+    <!-- common field list for events and todos (both represented by vCalendar/iCalendar) -->
+    <fieldlist name="calendar">
+      <field name="SYNCLVL" type="integer" compare="never"/>
+      <field name="ISEVENT" type="integer" compare="always"/>
+
+      <field name="DMODIFIED" type="timestamp" compare="never" age="yes"/>
+      <field name="DCREATED" type="timestamp" compare="never"/>
+
+      <field name="DGENERATED" type="timestamp" compare="never"/>
+
+      <field name="UID" type="string" compare="never"/>
+
+      <field name="CATEGORIES" array="yes" type="string" compare="conflict" merge="fillempty"/>
+      <field name="CLASS" type="integer" compare="conflict" merge="fillempty"/>
+      <field name="TRANSP" type="integer" compare="conflict" merge="fillempty"/>
+
+      <field name="SUMMARY" type="multiline" compare="always"/>
+      <field name="DESCRIPTION" type="multiline" compare="slowsync" merge="lines"/>
+      <field name="LOCATION" type="multiline" compare="slowsync" merge="lines"/>
+      <field name="URL" type="url" compare="conflict"/>
+
+      <!-- recurrence rule block, fields must be in that order, including
+           DTSTART as last field !! -->
+      <field name="RR_FREQ" type="string" compare="conflict"/>
+      <field name="RR_INTERVAL" type="integer" compare="conflict"/>
+      <field name="RR_FMASK" type="integer" compare="conflict"/>
+      <field name="RR_LMASK" type="integer" compare="conflict"/>
+      <field name="RR_END" type="timestamp" compare="conflict"/>
+
+      <!-- Note: DTSTART/DTEND are compared in the <comparescript>,
+                 therefore compare is set no "never" here -->
+      <field name="DTSTART" type="timestamp" compare="never"/>
+      <field name="DTEND" type="timestamp" compare="never"/>
+      <field name="DURATION" type="timestamp" compare="never"/>
+      <field name="COMPLETED" type="timestamp" compare="never"/>
+      <field name="DUE" type="timestamp" compare="never"/>
+
+      <field name="GEO_LAT" type="string" compare="never"/>
+      <field name="GEO_LONG" type="string" compare="never"/>
+
+      <field name="PRIORITY" type="integer" compare="conflict"/>
+      <field name="STATUS" type="integer" compare="conflict" merge="fillempty"/>
+      <field name="PERCENT_COMPLETE" type="integer" compare="conflict"/>
+
+      <field name="ALARM_TIME" type="timestamp" compare="conflict"/>
+      <field name="ALARM_SNOOZE" type="string" compare="conflict"/>
+      <field name="ALARM_REPEAT" type="string" compare="conflict"/>
+      <field name="ALARM_MSG" type="string" compare="conflict"/>
+      <field name="ALARM_ACTION" type="string" compare="conflict"/>
+      <field name="ALARM_REL" type="integer" compare="never"/>
+      <field name="ALARM_UID" type="string" compare="conflict"/>
+
+      <!-- non-standard -->
+      <field name="PARENT_UID" type="string" compare="never"/>
+
+      <!-- for events -->
+      <field name="EXDATES" array="yes" type="timestamp" compare="never"/>
+
+      <field name="ORIGSTART" array="no" type="timestamp" compare="never"/>
+      <field name="SEQNO" array="no" type="integer" compare="never"/>
+
+      <field name="ATTENDEES" array="yes" type="string" compare="never"/>
+      <field name="ATTENDEE_CNS" array="yes" type="string" compare="never"/>
+      <field name="ATTENDEE_PARTSTATS" array="yes" type="integer" compare="never"/>
+      <field name="ATTENDEE_ROLE" array="yes" type="integer" compare="never"/>
+      <field name="ATTENDEE_RSVP" array="yes" type="integer" compare="never"/>
+      <field name="ATTENDEE_LANG" array="yes" type="string" compare="never"/>
+      <field name="ATTENDEE_CUTYPE" array="yes" type="integer" compare="never"/>
+      <field name="ORGANIZER" array="no" type="string" compare="never"/>
+      <field name="ORGANIZER_CN" array="no" type="string" compare="never"/>
+
+    </fieldlist>
diff --git a/src/syncevo/configs/datatypes/11calendar-profile.xml b/src/syncevo/configs/datatypes/11calendar-profile.xml
new file mode 100644 (file)
index 0000000..f27f20f
--- /dev/null
@@ -0,0 +1,528 @@
+    <!-- vCalendar with VTODO and VEVENT variants -->
+    <mimeprofile name="vCalendar" fieldlist="calendar">
+
+      <vtimezonegenmode>current</vtimezonegenmode>
+      <tzidgenmode>olson</tzidgenmode>
+
+      <profile name="VCALENDAR" nummandatory="1">
+
+        <property name="VERSION" mandatory="yes">
+          <value conversion="version"/>
+        </property>
+
+        <property onlyformode="standard" name="PRODID" mandatory="no">
+          <value conversion="prodid"/>
+        </property>
+
+        <property onlyformode="old" name="TZ" filter="false" suppressempty="yes">
+          <value field="DTSTART" conversion="tz"/>
+        </property>
+
+        <property onlyformode="old" name="DAYLIGHT" mode="daylight" filter="false" suppressempty="yes">
+          <value field="DTSTART" conversion="daylight"/>
+        </property>
+
+        <property name="GEO" values="2" suppressempty="yes" onlyformode="old" valueseparator=",">
+          <!-- LON,LAT in vCalendar 1.0 -->
+          <value index="0" field="GEO_LAT"/>
+          <value index="1" field="GEO_LONG"/>
+        </property>
+
+        <subprofile onlyformode="standard" name="VTIMEZONE" mode="vtimezones"/>
+
+        <!-- sub-profile for tasks -->
+        <subprofile name="VTODO" nummandatory="1" showifselectedonly="yes" field="ISEVENT" value="0">
+
+          <property name="LAST-MODIFIED" suppressempty="yes">
+            <value field="DMODIFIED"/>
+          </property>
+
+          <property name="DTSTAMP" suppressempty="yes" onlyformode="standard">
+            <value field="DGENERATED"/>
+          </property>
+
+          <property name="DCREATED" suppressempty="yes" onlyformode="old">
+            <value field="DCREATED"/>
+          </property>
+          <property name="CREATED" suppressempty="yes" onlyformode="standard">
+            <value field="DCREATED"/>
+          </property>
+
+          <property name="UID" suppressempty="yes">
+            <value field="UID"/>
+          </property>
+
+          <property name="SEQUENCE" suppressempty="yes">
+            <value field="SEQNO"/>
+          </property>
+
+          <property name="GEO" values="2" suppressempty="yes" onlyformode="standard" valueseparator=";">
+            <!-- LAT;LON in iCalendar 2.0 -->
+            <value index="0" field="GEO_LONG"/>
+            <value index="1" field="GEO_LAT"/>
+          </property>
+
+          <property onlyformode="standard" name="CATEGORIES" values="list" valueseparator="," suppressempty="yes">
+            <value field="CATEGORIES" />
+            <position field="CATEGORIES" repeat="array" minshow="0"/>
+          </property>
+
+          <property onlyformode="old" name="CATEGORIES" values="list" valueseparator=";" altvalueseparator="," suppressempty="yes">
+            <value field="CATEGORIES" />
+            <position field="CATEGORIES" repeat="array" minshow="0"/>
+          </property>
+
+          <property name="CLASS" suppressempty="yes">
+            <value field="CLASS">
+              <enum name="PUBLIC"       value="0"/>
+              <enum name="PRIVATE"      value="1"/>
+              <enum name="CONFIDENTIAL" value="2"/>
+            </value>
+          </property>
+
+          <property name="SUMMARY" mandatory="yes">
+            <value field="SUMMARY"/>
+          </property>
+
+          <!-- DESCRIPTION is an optional property and libical does not like
+               empty properties, so suppress it here. However, in the scripts
+               we ensure that the DESCRIPTION field should never be empty. -->
+          <property name="DESCRIPTION" suppressempty="yes" mandatory="no">
+            <value field="DESCRIPTION"/>
+          </property>
+
+          <property name="LOCATION" suppressempty="yes" mandatory="no">
+            <value field="LOCATION"/>
+          </property>
+
+          <property name="URL" suppressempty="yes" mandatory="no">
+            <value field="URL"/>
+          </property>
+
+          <property name="DTSTART" suppressempty="yes" delayedparsing="1">
+            <value field="DTSTART" conversion="autodate"/>
+            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+              <value field="DTSTART" conversion="TZID"/>
+            </parameter>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+              <value field="DTSTART" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="COMPLETED" suppressempty="yes" delayedparsing="1">
+            <value field="COMPLETED" conversion="autoenddate"/>
+            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+              <value field="COMPLETED" conversion="TZID"/>
+            </parameter>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+              <value field="COMPLETED" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="DUE" suppressempty="yes" delayedparsing="1">
+            <value field="DUE" conversion="autodate"/>
+            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+              <value field="DUE" conversion="TZID"/>
+            </parameter>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+              <value field="DUE" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="PRIORITY" suppressempty="yes">
+            <value field="PRIORITY"/>
+          </property>
+
+          <property name="STATUS" onlyformode="standard" suppressempty="yes">
+            <value field="STATUS" conversion="emptyonly">
+              <enum name="COMPLETED"      value="0"/>
+              <enum name="NEEDS-ACTION"   value="1"/>
+              <enum name="IN-PROCESS"     value="2"/>
+              <enum name="CANCELLED"      value="3"/>
+              <enum name="ACCEPTED"       value="4"/>
+              <enum name="TENTATIVE"      value="5"/>
+              <enum name="DELEGATED"      value="6"/>
+              <enum name="DECLINED"       value="7"/>
+              <enum name="SENT"           value="8"/>
+              <enum name="CONFIRMED"      value="9"/>
+              <enum name="DRAFT"          value="10"/>
+              <enum name="FINAL"          value="11"/>
+            </value>
+          </property>
+
+          <property name="STATUS" onlyformode="old" suppressempty="yes">
+            <value field="STATUS" conversion="emptyonly">
+              <enum name="COMPLETED"      value="0"/>
+              <enum name="NEEDS ACTION"   value="1"/>
+              <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
+              <enum name="IN PROCESS"     value="2"/>
+              <enum name="CANCELLED"      value="3"/>
+              <enum name="ACCEPTED"       value="4"/>
+              <enum name="TENTATIVE"      value="5"/>
+              <enum name="DELEGATED"      value="6"/>
+              <enum name="DECLINED"       value="7"/>
+              <enum name="SENT"           value="8"/>
+              <enum name="CONFIRMED"      value="9"/>
+              <enum name="DRAFT"          value="10"/>
+              <enum name="FINAL"          value="11"/>
+            </value>
+          </property>
+
+          <property name="PERCENT-COMPLETE" onlyformode="standard" suppressempty="yes">
+            <value field="PERCENT_COMPLETE"/>
+          </property>          
+
+          <!-- AALARM and DALARM both use the same fields -->
+          <property name="AALARM" onlyformode="old" values="4" suppressempty="yes">
+            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+          </property>
+          <property name="DALARM" onlyformode="old" values="4" suppressempty="yes">
+            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+          </property>
+
+          <subprofile onlyformode="standard" name="VALARM" nummandatory="1" field="ALARM_TIME">
+            <property name="TRIGGER" suppressempty="no" mandatory="yes">
+              <value field="ALARM_TIME"/>
+              <parameter name="VALUE" default="no" show="yes">
+                <value field="ALARM_TIME" conversion="FULLVALUETYPE"/>
+              </parameter>
+              <parameter name="RELATED" default="no" show="yes">
+                <value field="ALARM_REL">
+                  <enum mode="ignore" value="0"/>
+                  <enum name="START" value="1"/>
+                  <enum name="END"   value="2"/>
+                </value>
+              </parameter>
+            </property>
+            <property name="ACTION" suppressempty="yes" mandatory="yes">
+              <value field="ALARM_ACTION"/>
+            </property>
+            <property name="DESCRIPTION" suppressempty="yes">
+              <value field="ALARM_MSG"/>
+            </property>
+            <property name="REPEAT" suppressempty="yes">
+              <value field="ALARM_REPEAT"/>
+            </property>
+            <property name="X-EVOLUTION-ALARM-UID" suppressempty="yes">
+              <value field="ALARM_UID"/>
+            </property>
+          </subprofile>
+
+          <property onlyformode="old" name="RELATED-TO" suppressempty="yes">
+            <value field="PARENT_UID"/>
+          </property>
+
+          <property onlyformode="standard" name="RELATED-TO" suppressempty="yes">
+            <value field="PARENT_UID"/>
+            <parameter onlyformode="standard" name="RELTYPE" default="no" positional="yes" show="yes">
+              <value>
+                <enum name="PARENT"/>
+                <enum mode="defaultvalue" name="other"/>
+              </value>
+              <position hasnot="other" shows="PARENT" field="PARENT_UID"/>
+            </parameter>
+          </property>
+
+        </subprofile>
+
+        <!-- sub-profile for event -->
+        <subprofile name="VEVENT" nummandatory="1" showifselectedonly="yes" field="ISEVENT" value="1">
+
+            <property name="STATUS"  suppressempty="yes" onlyformode="old">
+                <value field="STATUS" conversion="emptyonly">
+                    <enum name="COMPLETED"      value="0"/>
+                    <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
+                    <enum name="NEEDS ACTION"   value="1"/>
+                    <enum name="CANCELLED"      value="3"/>
+                    <enum name="ACCEPTED"       value="4"/>
+                    <enum name="TENTATIVE"      value="5"/>
+                    <enum name="DELEGATED"      value="6"/>
+                    <enum name="DECLINED"       value="7"/>
+                    <enum name="SENT"           value="8"/>
+                    <enum name="CONFIRMED"      value="9"/>
+                    <enum name="FINAL"          value="11"/>
+                </value>
+            </property>
+
+            <property name="STATUS" suppressempty="yes" onlyformode="standard">
+                <value field="STATUS" conversion="emptyonly">
+                    <enum name="CANCELLED" value="3"/>
+                    <enum name="TENTATIVE" value="5"/>
+                    <enum name="CONFIRMED" value="9"/>
+                </value>
+            </property>
+
+          <property name="LAST-MODIFIED" suppressempty="yes">
+            <value field="DMODIFIED"/>
+          </property>
+
+          <property name="DTSTAMP" suppressempty="yes" onlyformode="standard">
+            <value field="DGENERATED"/>
+          </property>
+
+          <property name="DCREATED" suppressempty="yes" onlyformode="old">
+            <value field="DCREATED"/>
+          </property>
+          <property name="CREATED" suppressempty="yes" onlyformode="standard">
+            <value field="DCREATED"/>
+          </property>
+
+
+          <property name="UID" suppressempty="yes">
+            <value field="UID"/>
+          </property>
+
+          <property name="SEQUENCE" suppressempty="yes">
+            <value field="SEQNO"/>
+          </property>
+
+          <property name="GEO" values="2" suppressempty="yes" onlyformode="standard" valueseparator=";">
+            <!-- LAT;LON in iCalendar 2.0 -->
+            <value index="0" field="GEO_LONG"/>
+            <value index="1" field="GEO_LAT"/>
+          </property>
+
+          <property onlyformode="standard" name="CATEGORIES" values="list" valueseparator="," suppressempty="yes">
+            <value field="CATEGORIES" />
+            <position field="CATEGORIES" repeat="array" minshow="0"/>
+          </property>
+
+          <property onlyformode="old" name="CATEGORIES" values="list" valueseparator=";" altvalueseparator="," suppressempty="yes">
+            <value field="CATEGORIES" />
+            <position field="CATEGORIES" repeat="array" minshow="0"/>
+          </property>
+
+          <property name="CLASS" suppressempty="yes">
+            <value field="CLASS">
+              <enum name="PUBLIC"       value="0"/>
+              <enum name="PRIVATE"      value="1"/>
+              <enum name="CONFIDENTIAL" value="2"/>
+            </value>
+          </property>
+
+
+          <property name="TRANSP" suppressempty="yes" onlyformode="standard">
+            <value field="TRANSP">
+              <enum name="OPAQUE"       value="0"/>
+              <enum name="TRANSPARENT"  value="1"/>
+              <enum name="TENTATIVE"     value="2"/> <!-- according to Oracle de facto usage in vCalendar 1.0 -->
+              <enum name="OUT_OF_OFFICE" value="3"/> <!-- according to Oracle de facto usage in vCalendar 1.0 -->
+              <enum mode="defaultvalue" value="0"/>
+            </value>
+          </property>
+          <property name="TRANSP" suppressempty="yes" onlyformode="old">
+            <value field="TRANSP"/> <!-- directly numeric in vCalendar 1.0 -->
+          </property>
+
+
+          <property name="PRIORITY" suppressempty="yes">
+            <value field="PRIORITY"/>
+          </property>
+
+          <property name="SUMMARY" mandatory="yes">
+            <value field="SUMMARY"/>
+          </property>
+
+          <!-- DESCRIPTION is an optional property and libical does not like
+               empty properties, so suppress it here. However, in the scripts
+               we ensure that the DESCRIPTION field should never be empty. -->
+          <property name="DESCRIPTION" suppressempty="yes" mandatory="no">
+            <value field="DESCRIPTION"/>
+          </property>
+
+          <property name="LOCATION" suppressempty="yes" mandatory="no">
+            <value field="LOCATION"/>
+          </property>
+
+          <property name="DTSTART" suppressempty="yes" delayedparsing="1">
+            <value field="DTSTART" conversion="autodate"/>
+            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+              <value field="DTSTART" conversion="TZID"/>
+            </parameter>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+              <value field="DTSTART" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <!-- recurrence rule (with delayed parsing, as it is dependent on DTSTART) -->
+          <property name="RRULE" suppressempty="yes" delayedparsing="2">
+            <!-- Note: RR_FREQ is the beginning of a block of fields
+                 suitable for the "rrule" conversion mode -->
+            <value field="RR_FREQ" conversion="rrule"/>
+          </property>
+
+          <!-- Symbian uses this, so it might make the client work with symbian-prepared servers better -->
+          <property name="X-RECURRENCE-ID" suppressempty="yes" onlyformode="old">
+            <value field="ORIGSTART" conversion="autodate"/>
+          </property>
+
+          <property name="RECURRENCE-ID" suppressempty="yes" onlyformode="standard" delayedparsing="1">
+            <value field="ORIGSTART" conversion="autodate"/>
+            <parameter name="TZID" default="no" show="yes">
+              <value field="ORIGSTART" conversion="TZID"/>
+            </parameter>
+            <parameter name="VALUE" default="no" show="yes">
+              <value field="ORIGSTART" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <!-- ScheduleWorld has a problem (bugzilla.moblin.org #2226)
+               with the EXDATE:value1,value2 format (correct in iCalendar 2.0):
+               as a workaround, accept all valid formats plus ; but
+               generate separate properties with one value each. -->
+          <property name="EXDATE" values="expandedlist" suppressempty="yes" onlyformode="standard" delayedparsing="1" valueseparator="," altvalueseparator=";">
+            <value field="EXDATES"/>
+            <position field="EXDATES" repeat="array" increment="1" minshow="0"/>
+            <parameter name="TZID" default="no" show="yes">
+              <value field="EXDATES" conversion="TZID"/>
+            </parameter>
+          </property>
+
+          <property name="EXDATE" values="list" suppressempty="yes" onlyformode="old" delayedparsing="1" valueseparator=";" altvalueseparator=",">
+            <value field="EXDATES"/>
+            <position field="EXDATES" repeat="array" increment="1" minshow="0"/>
+          </property>
+
+
+          <property name="DTEND" suppressempty="yes" delayedparsing="1">
+            <value field="DTEND" conversion="autoenddate"/>
+            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+              <value field="DTEND" conversion="TZID"/>
+            </parameter>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+              <value field="DTEND" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="DURATION" suppressempty="yes" delayedparsing="1" onlyformode="standard">
+            <value field="DURATION"/>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="no">
+              <value field="DURATION" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="ATTENDEE" suppressempty="yes" onlyformode="old">
+            <value field="ATTENDEES"/>
+            <parameter name="ROLE" default="no" positional="yes" show="yes">
+              <value>
+                <enum name="ORGANIZER"/>
+              </value>
+              <position has="ORGANIZER" field="ORGANIZER" overwriteempty="yes"/>
+              <position hasnot="ORGANIZER" field="ATTENDEES" repeat="array" increment="1" overwriteempty="yes"/>
+            </parameter>
+            <parameter name="STATUS" default="no" show="yes">
+              <value field="ATTENDEE_PARTSTATS">
+                <enum name="NEEDS ACTION"   value="1"/>
+                <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
+                <enum name="ACCEPTED"       value="4"/>
+                <enum name="DECLINED"       value="7"/>
+                <enum name="TENTATIVE"      value="5"/>
+                <enum name="DELEGATED"      value="6"/>
+              </value>
+            </parameter>
+          </property>
+
+          <property name="ATTENDEE" suppressempty="yes" onlyformode="standard">
+            <value field="ATTENDEES" conversion="mailto"/>
+            <position field="ATTENDEES" repeat="array" increment="1" minshow="0"/>
+            <parameter name="CN" default="no" show="yes" shownonempty="yes">
+              <value field="ATTENDEE_CNS"/>
+            </parameter>
+            <parameter name="PARTSTAT" default="no" show="yes">
+              <value field="ATTENDEE_PARTSTATS">
+                <enum name="NEEDS-ACTION"   value="1"/>
+                <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
+                <enum name="ACCEPTED"       value="4"/>
+                <enum name="DECLINED"       value="7"/>
+                <enum name="TENTATIVE"      value="5"/>
+                <enum name="DELEGATED"      value="6"/>
+              </value>
+            </parameter>
+            <parameter name="ROLE" default="no" show="yes">
+              <value field="ATTENDEE_ROLE">
+                <enum name="CHAIR"            value="1"/>
+                <enum name="REQ-PARTICIPANT"  value="2"/>
+                <enum name="OPT-PARTICIPANT"  value="3"/>
+                <enum name="NON-PARTICIPANT"  value="4"/>
+              </value>
+            </parameter>
+            <parameter name="RSVP" default="no" show="yes">
+              <value field="ATTENDEE_RSVP">
+                <enum name="TRUE"             value="1"/>
+                <enum name="FALSE"            value="0"/>
+              </value>
+            </parameter>
+            <parameter name="LANGUAGE" show="yes">
+              <value field="ATTENDEE_LANG"/>
+            </parameter>
+            <parameter name="CUTYPE" default="no" show="yes">
+              <value field="ATTENDEE_CUTYPE">
+                <enum name="INDIVIDUAL"  value="1"/>
+                <enum name="GROUP"       value="2"/>
+                <enum name="RESOURCE"    value="3"/>
+                <enum name="ROOM"        value="4"/>
+                <enum name="UNKNOWN"     value="5"/>
+              </value>
+            </parameter>
+          </property>
+
+          <property name="ORGANIZER" suppressempty="yes" onlyformode="standard">
+            <value field="ORGANIZER" conversion="mailto"/>
+            <parameter name="CN" default="no" show="yes">
+              <value field="ORGANIZER_CN"/>
+            </parameter>
+          </property>
+
+          <!-- AALARM and DALARM both use the same fields -->
+          <property name="AALARM" onlyformode="old" values="4" suppressempty="yes">
+            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+          </property>
+          <property name="DALARM" onlyformode="old" values="4" suppressempty="yes">
+            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+          </property>
+
+          <subprofile onlyformode="standard" name="VALARM" nummandatory="1" field="ALARM_TIME">
+            <property name="TRIGGER" suppressempty="no" mandatory="yes">
+              <value field="ALARM_TIME"/>
+              <parameter name="VALUE" default="no" show="yes">
+                <value field="ALARM_TIME" conversion="FULLVALUETYPE"/>
+              </parameter>
+              <parameter name="RELATED" default="no" show="yes">
+                <value field="ALARM_REL">
+                  <enum mode="ignore" value="0"/>
+                  <enum name="START" value="1"/>
+                  <enum name="END"   value="2"/>
+                </value>
+              </parameter>
+            </property>
+            <property name="ACTION" suppressempty="yes" mandatory="yes">
+              <value field="ALARM_ACTION"/>
+            </property>
+            <property name="DESCRIPTION" suppressempty="yes">
+              <value field="ALARM_MSG"/>
+            </property>
+            <property name="REPEAT" suppressempty="yes">
+              <value field="ALARM_REPEAT"/>
+            </property>
+            <property name="X-EVOLUTION-ALARM-UID" suppressempty="yes">
+              <value field="ALARM_UID"/>
+            </property>
+          </subprofile>
+
+        </subprofile>
+
+      </profile>
+    </mimeprofile>
+
diff --git a/src/syncevo/configs/datatypes/12calendar-types.xml b/src/syncevo/configs/datatypes/12calendar-types.xml
new file mode 100644 (file)
index 0000000..15c64c0
--- /dev/null
@@ -0,0 +1,31 @@
+    <!-- vCalendar 1.0 datatype, using vCalendar profile defined above -->
+    <datatype name="vCalendar10" basetype="vcalendar">
+      <version>1.0</version>
+      <use mimeprofile="vCalendar"/>
+
+      <incomingscript><![CDATA[
+        $VCALENDAR_INCOMING_SCRIPT
+      ]]></incomingscript>
+
+      <outgoingscript><![CDATA[
+        $VCALENDAR_OUTGOING_SCRIPT
+      ]]></outgoingscript>
+
+    </datatype>
+
+
+    <!-- iCalendar 2.0 datatype, using vCalendar profile defined above -->
+    <datatype name="iCalendar20" basetype="vcalendar">
+      <version>2.0</version>
+      <use mimeprofile="vCalendar"/>
+
+      <incomingscript><![CDATA[
+        $VCALENDAR_INCOMING_SCRIPT
+      ]]></incomingscript>
+
+      <outgoingscript><![CDATA[
+        $VCALENDAR_OUTGOING_SCRIPT
+      ]]></outgoingscript>
+
+    </datatype>
+
diff --git a/src/syncevo/configs/datatypes/20note-fieldlist.xml b/src/syncevo/configs/datatypes/20note-fieldlist.xml
new file mode 100644 (file)
index 0000000..8eda2a5
--- /dev/null
@@ -0,0 +1,7 @@
+    <!-- list of internal fields representing plain text note data -->
+    <fieldlist name="Note">
+      <field name="SYNCLVL" type="integer" compare="never"/>
+      <field name="SUBJECT" type="multiline" compare="always"/>
+      <field name="TEXT" type="multiline" compare="conflict" merge="lines"/>
+    </fieldlist>
+
diff --git a/src/syncevo/configs/datatypes/21note-profile.xml b/src/syncevo/configs/datatypes/21note-profile.xml
new file mode 100644 (file)
index 0000000..8f5eb8c
--- /dev/null
@@ -0,0 +1,14 @@
+    <textprofile name="Note" fieldlist="Note">
+      <linemap field="SUBJECT">
+        <numlines>1</numlines>
+        <inheader>false</inheader>
+        <allowempty>true</allowempty>
+        <filterkeyword>SUBJECT</filterkeyword>
+      </linemap>
+      <linemap field="TEXT">
+        <numlines>0</numlines>
+        <inheader>false</inheader>
+        <allowempty>true</allowempty>
+      </linemap>
+    </textprofile>
+
diff --git a/src/syncevo/configs/datatypes/22notes-types.xml b/src/syncevo/configs/datatypes/22notes-types.xml
new file mode 100644 (file)
index 0000000..ebd1dfd
--- /dev/null
@@ -0,0 +1,12 @@
+    <datatype name="note10" basetype="text">
+      <use profile="Note"/>
+      <typestring>text/plain</typestring>
+      <versionstring>1.0</versionstring>
+    </datatype>
+
+    <datatype name="note11" basetype="text">
+      <use profile="Note"/>
+      <typestring>text/plain</typestring>
+      <versionstring>1.1</versionstring>
+    </datatype>
+
diff --git a/src/syncevo/configs/datatypes/30bookmark-fieldlist.xml b/src/syncevo/configs/datatypes/30bookmark-fieldlist.xml
new file mode 100644 (file)
index 0000000..cde7c53
--- /dev/null
@@ -0,0 +1,20 @@
+    <!-- list of internal fields representing vBookmark data -->
+    <fieldlist name="bookmarks">
+      <field name="REV" type="timestamp" compare="never" age="yes"/>
+      <field name="SYNCLVL" type="integer" compare="never"/>
+
+      <!-- Name -->
+      <field name="TITLE" type="string" compare="always"/>
+
+      <!-- categories and classification -->
+      <field name="CATEGORIES" type="string" compare="conflict" merge="fillempty"/>
+      <field name="CLASS" type="string" compare="conflict" merge="fillempty"/>
+
+      <!-- web addresses -->
+      <field name="URL" type="url" compare="slowsync" merge="fillempty"/>
+
+      <!-- Note -->
+      <field name="NOTE" type="multiline" compare="conflict" merge="lines"/>
+
+    </fieldlist>
+
diff --git a/src/syncevo/configs/datatypes/31bookmark-profile.xml b/src/syncevo/configs/datatypes/31bookmark-profile.xml
new file mode 100644 (file)
index 0000000..1f9b73a
--- /dev/null
@@ -0,0 +1,38 @@
+    <!-- vBookmark profile -->
+    <mimeprofile name="vBookmark" fieldlist="bookmarks">
+
+      <profile name="VBKM" nummandatory="0">
+        <property name="VERSION">
+          <value conversion="version"/>
+        </property>
+
+        <property name="X-LAST-MODIFIED">
+          <value field="REV"/>
+        </property>
+
+        <property name="TITLE">
+          <value field="TITLE"/>
+        </property>
+
+        <property name="URL">
+          <value field="URL"/>
+        </property>
+
+        <!-- non-standard properties -->
+
+        <!-- inherit CATEGORIES from vCard 3.0, i.e. comma separated -->
+        <property name="CATEGORIES" values="list" valueseparator="," altvalueseparator=";">
+          <value field="CATEGORIES" combine=","/>
+        </property>
+
+        <property name="CLASS" suppressempty="yes">
+          <value field="CLASS"/>
+        </property>
+
+        <property name="NOTE" filter="no">
+          <value field="NOTE"/>
+        </property>
+
+      </profile>
+    </mimeprofile>
+
diff --git a/src/syncevo/configs/datatypes/32bookmark-type.xml b/src/syncevo/configs/datatypes/32bookmark-type.xml
new file mode 100644 (file)
index 0000000..e7142ac
--- /dev/null
@@ -0,0 +1,7 @@
+    <!-- vBookmark datatype, using vBookmark profile defined above -->
+    <datatype name="vBookmark10" basetype="mimedir">
+      <typestring>text/x-vbookmark</typestring>
+      <versionstring>1.0</versionstring>
+      <use profile="vBookmark"/>
+    </datatype>
+
diff --git a/src/syncevo/configs/datatypes/server/40email-fieldlist.xml b/src/syncevo/configs/datatypes/server/40email-fieldlist.xml
new file mode 100644 (file)
index 0000000..e3fb6a3
--- /dev/null
@@ -0,0 +1,24 @@
+    <!-- list of internal fields representing email data -->
+    <fieldlist name="email">
+      <field name="SYNCLVL" type="integer" compare="never"/>
+      <field name="MODIFIED" type="timestamp" compare="never" age="yes"/>
+      <field name="SENDER" type="multiline" compare="always"/>
+      <field name="RECEIVER" type="multiline" compare="always"/>
+      <field name="CARBONCOPY" type="multiline" compare="always"/>
+      <field name="BLINDCARBONCOPY" type="multiline" compare="always"/>
+      <field name="REPLY_TO" type="multiline" compare="never"/>
+      <field name="SUBJECT" type="multiline" compare="always"/>
+      <field name="PRIORITY" type="integer" compare="never"/>
+      <field name="MAILDATE" type="timestamp" compare="never"/>
+      <field name="STATUS" type="string" compare="never"/>
+      <field name="FOLDER" type="string" compare="conflict"/>
+      <field name="ISREAD" type="string" compare="never"/>
+      <field name="LIMIT" type="integer" compare="never"/>
+      <field name="BODY" type="multiline" compare="never"/>
+      <field name="ATT_COUNT" type="integer" compare="never"/>
+      <field name="ATT_NAMES" array="yes" type="string" compare="never"/>
+      <field name="ATT_MIMETYPES" array="yes" type="string" compare="never"/>
+      <field name="ATT_SIZES" array="yes" type="integer" compare="never"/>
+      <field name="ATT_CONTENTS" array="yes" type="blob" compare="never"/>
+    </fieldlist>
+
diff --git a/src/syncevo/configs/datatypes/server/41email-profile.xml b/src/syncevo/configs/datatypes/server/41email-profile.xml
new file mode 100644 (file)
index 0000000..9f52d7b
--- /dev/null
@@ -0,0 +1,96 @@
+    <!-- this is the text profile used to generate and decode RFC2822/MIME-Multipart
+         email messages. -->
+    <textprofile name="rfc2822_email" fieldlist="email">
+
+      <mimemail>true</mimemail>
+      <!-- attachment configuration -->
+      <maxattachments>100</maxattachments>
+      <attachmentcountfield>ATT_COUNT</attachmentcountfield>
+      <attachmentmimetypesfield>ATT_MIMETYPES</attachmentmimetypesfield>
+      <attachmentsfield>ATT_CONTENTS</attachmentsfield>
+      <attachmentsizesfield>ATT_SIZES</attachmentsizesfield>
+      <attachmentnamesfield>ATT_NAMES</attachmentnamesfield>
+      <sizelimitfield>LIMIT</sizelimitfield>
+
+
+      <linemap field="SENDER">
+        <headertag>From:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+        <filterkeyword>FROM</filterkeyword>
+      </linemap>
+
+      <linemap field="RECEIVER">
+        <headertag>To:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+        <filterkeyword>TO</filterkeyword>
+      </linemap>
+
+      <linemap field="CARBONCOPY">
+        <headertag>Cc:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+        <filterkeyword>CC</filterkeyword>
+      </linemap>
+
+      <linemap field="BLINDCARBONCOPY">
+        <headertag>Bcc:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+        <filterkeyword>BCC</filterkeyword>
+      </linemap>
+
+      <linemap field="REPLY_TO">
+        <headertag>Reply-To:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="SUBJECT">
+        <headertag>Subject:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+        <filterkeyword>SUBJECT</filterkeyword>
+      </linemap>
+
+      <linemap field="PRIORITY">
+        <headertag>X-Priority:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="MAILDATE">
+        <valuetype>date</valuetype>
+        <headertag>Date:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="STATUS">
+        <headertag>Status:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="FOLDER">
+        <headertag>X-Sync-Parent-Folder:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="ISREAD">
+        <headertag>X-Sync-Message-Read:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="MODIFIED">
+        <!-- note that this is a ISO8601 date -->
+        <headertag>X-Sync-Lastmodified:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="BODY">
+        <valuetype>body</valuetype>
+        <numlines>0</numlines>
+        <inheader>false</inheader>
+        <allowempty>true</allowempty>
+      </linemap>
+    </textprofile>
+
diff --git a/src/syncevo/configs/datatypes/server/42email-type-zipped.xml b/src/syncevo/configs/datatypes/server/42email-type-zipped.xml
new file mode 100644 (file)
index 0000000..c560f0d
--- /dev/null
@@ -0,0 +1,43 @@
+    <!-- Note: This is a proprietary extension datatype for Synthesis AG Windows Mobile SyncML clients.
+         This format is a compressed form of the standard RFC2822 format. For one, the entire data
+         is compressed using the zip algorithm (<zippedbindata>), and secondly attachments are included
+         in binary form in the RFC2822 data stream rather than bandwidth wasting B64, adding a
+         "Content-Length:" header for each MIME part (<binaryparts>). -->
+
+    <datatype name="email_zipbin" basetype="text">
+      <use profile="rfc2822_email"/>
+      <typestring>application/x-zip-message</typestring> <!-- our own private zipped binary optimized format -->
+      <versionstring>1.1</versionstring>
+      <binaryparts>yes</binaryparts>
+      <zippedbindata>yes</zippedbindata>
+      <zipcompressionlevel>9</zipcompressionlevel> <!-- -1=default, 0=no compression, 1=fast & least effective ... 9=slow and most effective -->
+
+      <initscript><![CDATA[
+        $EMAIL_INIT_SCRIPT
+      ]]></initscript>
+
+      <processitemscript><![CDATA[
+        $EMAIL_PROCESSITEM_SCRIPT
+      ]]></processitemscript>
+
+
+      <mergescript><![CDATA[
+        $EMAIL_MERGE_SCRIPT
+      ]]></mergescript>
+
+
+      <outgoingscript><![CDATA[
+        $EMAIL_OUTGOING_SCRIPT
+      ]]></outgoingscript>
+
+      <filterinitscript><![CDATA[
+        $EMAIL_FILTERINIT_SCRIPT
+      ]]></filterinitscript>
+
+
+      <filterscript><![CDATA[
+        $EMAIL_FILTER_SCRIPT
+      ]]></filterscript>
+
+    </datatype>
+
diff --git a/src/syncevo/configs/datatypes/server/42email-type.xml b/src/syncevo/configs/datatypes/server/42email-type.xml
new file mode 100644 (file)
index 0000000..1dcdf74
--- /dev/null
@@ -0,0 +1,34 @@
+    <datatype name="email" basetype="text">
+      <use profile="rfc2822_email"/>
+      <typestring>text/message</typestring> <!-- this is P800-like -->
+      <versionstring>1.0</versionstring>
+
+      <initscript><![CDATA[
+        $EMAIL_INIT_SCRIPT
+      ]]></initscript>
+
+      <processitemscript><![CDATA[
+        $EMAIL_PROCESSITEM_SCRIPT
+      ]]></processitemscript>
+
+
+      <mergescript><![CDATA[
+        $EMAIL_MERGE_SCRIPT
+      ]]></mergescript>
+
+
+      <outgoingscript><![CDATA[
+        $EMAIL_OUTGOING_SCRIPT
+      ]]></outgoingscript>
+
+      <filterinitscript><![CDATA[
+        $EMAIL_FILTERINIT_SCRIPT
+      ]]></filterinitscript>
+
+
+      <filterscript><![CDATA[
+        $EMAIL_FILTER_SCRIPT
+      ]]></filterscript>
+
+    </datatype>
+
diff --git a/src/syncevo/configs/datatypes/server/43email-sonyericsson.xml b/src/syncevo/configs/datatypes/server/43email-sonyericsson.xml
new file mode 100644 (file)
index 0000000..af492df
--- /dev/null
@@ -0,0 +1,34 @@
+    <datatype name="email_sonyericsson" basetype="text">
+      <use profile="rfc2822_email"/>
+      <typestring>message/rfc822</typestring> <!-- this is M600i/P990-like -->
+      <versionstring>1.0</versionstring>
+
+      <initscript><![CDATA[
+        $EMAIL_INIT_SCRIPT
+      ]]></initscript>
+
+      <processitemscript><![CDATA[
+        $EMAIL_PROCESSITEM_SCRIPT
+      ]]></processitemscript>
+
+
+      <mergescript><![CDATA[
+        $EMAIL_MERGE_SCRIPT
+      ]]></mergescript>
+
+
+      <outgoingscript><![CDATA[
+        $EMAIL_OUTGOING_SCRIPT
+      ]]></outgoingscript>
+
+      <filterinitscript><![CDATA[
+        $EMAIL_FILTERINIT_SCRIPT
+      ]]></filterinitscript>
+
+
+      <filterscript><![CDATA[
+        $EMAIL_FILTER_SCRIPT
+      ]]></filterscript>
+
+    </datatype>
+
diff --git a/src/syncevo/configs/datatypes/server/44email-nokia9500.xml b/src/syncevo/configs/datatypes/server/44email-nokia9500.xml
new file mode 100644 (file)
index 0000000..e9f1750
--- /dev/null
@@ -0,0 +1,135 @@
+    <datatype name="email_nokia9500" basetype="text">
+      <use profile="rfc2822_email"/>
+      <typestring>message/x-rfc822</typestring> <!-- this is Nokia 9500/9300-like -->
+      <versionstring>1.0</versionstring>
+
+      <initscript><![CDATA[
+        INTEGER ITEMLIMIT;
+        // default limit is limit of session
+        ITEMLIMIT = SIZELIMIT();
+      ]]></initscript>
+
+      <processitemscript><![CDATA[
+        INTEGER n;
+
+        // For Nokia Email, we must derive folder ID from source LocURI
+        // which has form "./somestrangenumber/folder/itemid"
+        // where folder can be "Inbox" or "Outbox"
+        if (FIND(REMOTEID(),"Inbox",0)!=UNASSIGNED) {
+          FOLDER="INBOX";
+        }
+        else if (FIND(REMOTEID(),"Outbox",0)!=UNASSIGNED) {
+          FOLDER="OUTBOX";
+        }
+        // pre-process item
+        if (UPPERCASE(FOLDER)=="INBOX") {
+          // In any case, prevent adding to inbox (delete remote items instead)
+          PREVENTADD();
+          // server always wins for inbox
+          CONFLICTSTRATEGY("server-wins");
+          if (SLOWSYNC()) {
+            // also prevent modifications in server
+            IGNOREUPDATE();
+          }
+          else {
+            // normal sync items going to inbox from client need special treatment
+            if (SYNCOP()=="add" || SYNCOP()=="replace") {
+              // make sure that existing server item will conflict with this item
+              if (LIMIT!=EMPTY && (LIMIT<0 || LIMIT>SIZELIMIT())) {
+                // force conflict only if this is a reload
+                FORCECONFLICT();
+              }
+              // make sure we never overwrite a body in the inbox
+              BODY = UNASSIGNED;
+              // delete always wins over replace in inbox (to avoid adds to inbox)
+              DELETEWINS();
+            }
+          }
+        }
+        else if (UPPERCASE(FOLDER)=="OUTBOX") {
+          // never try to change something in outbox
+          IGNOREUPDATE();
+          if (SYNCOP()!="delete") {
+            // - date of mail is NOW, set it such that a correct date is written to the DB
+            MAILDATE = DBNOW();
+            // MAILDATE = (INTEGER)DBNOW() - TIMEUNITS(120); // %%% backdate it 2 mins to make sure it does not get retransmitted
+            // - echo item as delete (this causes that it is moved to the "sent" folder in the 9500)
+            ECHOITEM("delete");
+          }
+          CONFLICTSTRATEGY("client-wins");
+        }
+        else {
+          // Other folder
+          // - silently discard incoming item for other folder than the above
+          //   except if it is a delete
+          if (SYNCOP()!="delete")
+            REJECTITEM(0);
+        }
+      ]]></processitemscript>
+
+
+      <mergescript><![CDATA[
+        // pre-process item
+        if (UPPERCASE(LOOSING.FOLDER)!="OUTBOX") {
+          // non-outbox (especially inbox) needs special merge to accomplish reload feature
+          // - loosing item is client's, winning is server's
+          if (LOOSING.LIMIT!=EMPTY) {
+            // loosing (remote) item specifies a new limit, override winning's default
+            WINNING.LIMIT=LOOSING.LIMIT;
+            SETWINNINGCHANGED(TRUE);
+          }
+          // make sure winning has right folder
+          WINNING.FOLDER=LOOSING.FOLDER;
+          // make sure a set read-flag gets propagated to server
+          if (LOOSING.ISREAD=="true") WINNING.ISREAD="true";
+          // merge other fields normally
+          MERGEFIELDS();
+          // make sure body does not get re-written to local DB even if merge would cause local update
+          LOOSING.BODY=UNASSIGNED;
+        }
+        else {
+          // normal merging in other folders
+          MERGEFIELDS();
+        }
+      ]]></mergescript>
+
+
+      <outgoingscript><![CDATA[
+        // we can only send to inbox or outbox
+        // - If we have no remote ID (=add command) prepare special Target item ID
+        //   containing target folder.
+        if (REMOTEID()==EMPTY) {
+        if (UPPERCASE(FOLDER)=="INBOX") {
+            SETREMOTEID(REMOTEDBNAME()+"/Inbox/");
+        }
+        else if (UPPERCASE(FOLDER)=="OUTBOX") {
+            SETREMOTEID(REMOTEDBNAME()+"/Outbox/");
+          }
+        }
+      ]]></outgoingscript>
+
+      <filterinitscript><![CDATA[
+        // check if we need to filter
+        INTEGER NEEDFILTER;
+
+        NEEDFILTER =
+          !DBHANDLESOPTS() && // only if DB cannot handle it
+          (STARTDATE()!=EMPTY); // and only if a start date is set (end date not needed as there are never future emails today)
+        SETFILTERALL(NEEDFILTER);
+        RETURN NEEDFILTER;
+      ]]></filterinitscript>
+
+
+      <filterscript><![CDATA[
+        INTEGER PASSES;
+
+        // check if item passes filter
+        PASSES=FALSE;
+        // Filter out anything not for Inbox or Outbox
+        if (UPPERCASE(FOLDER)!="INBOX" && UPPERCASE(FOLDER)!="OUTBOX") RETURN FALSE;
+        // Emails pass if they have a MAILDATE on or later than start date
+        PASSES = MAILDATE>=STARTDATE();
+        RETURN PASSES;
+      ]]></filterscript>
+
+    </datatype>
diff --git a/src/syncevo/configs/debug/00default.xml b/src/syncevo/configs/debug/00default.xml
new file mode 100644 (file)
index 0000000..5b24c4c
--- /dev/null
@@ -0,0 +1,31 @@
+    <!-- path where logfiles are stored -->
+    <!-- <logpath platform="linux">/your/log/directory</logpath> -->
+    <logflushmode>buffered</logflushmode> <!-- buffered is fastest mode, but may loose data on process abort. Other options: "flush" (after every line) or "openclose" (safest, slowest, like in 2.x server) -->
+    <!-- per session log -->
+    <sessionlogs>yes</sessionlogs> <!-- by default, create a session log file for every sync session (might be disabled for special users/devices in scripts) -->
+    <!-- debug format options -->
+    <logformat>html</logformat> <!-- html is nicely colored and easily viewable with a web browser. Other options: "xml", "text" -->
+    <folding>auto</folding> <!-- dynamic folding of blocks enabled, automatically expanded or collapsed default. Other options: "none", "expanded", "collapsed" -->
+    <timestamp>yes</timestamp> <!-- show timestamps for structure elements in log -->
+    <timestampall>no</timestampall> <!-- don't show timestamp for every log line -->
+    <timedsessionlognames>yes</timedsessionlognames> <!-- session logs also have the session start timestamp in the filename - makes them more easily sortable -->
+    <!-- thread logging mode -->
+    <subthreadmode>separate</subthreadmode> <!-- write log info from subthreads into separate log files. Other options: "suppress" -->
+    <!-- basic debug level selection -->
+    <enable option="extended"/> <!-- "extended" is a good choice for start testing. For production, use "normal" or "minimal" -->
+    <!-- <enable option="normal"/> --> <!-- "normal" provides rich debug info, but still in reasonable size  -->
+    <!-- <enable option="minimal"/> --> <!-- "minimal" just shows basic flow and error. Not suitable for debugging -->
+    <!-- <enable option="maximal"/> --> <!-- "maximal" can create VERY LARGE logs and cause HEAVY SLOWDOWN. Only for detail debugging  -->
+    <!-- <enable option="all"/> --> <!-- "all" shows EVERYTHING possible, and way too much for any normal situation. For hardcore debugging ONLY! -->
+    <!-- additional debug info switches -->
+    <enable option="userdata"/> <!-- Make this <disable ...> if you don't want user data in the logs -->
+    <disable option="scripts"/> <!-- Make this <enable ...> to show script execution in logs  -->
+    <disable option="match"/> <!-- Make this <enable ...> to show slow sync matching. CAN PRODUCE ENORMOUS LOGS and HEAVILY IMPACT PERFORMANCE for large slow syncs - use with care!  -->
+    <disable option="exotic"/> <!-- Make this <enable ...> to include very in-detail info. CAN PRODUCE ENORMOUS LOGS and HEAVILY IMPACT PERFORMANCE for large slow syncs - use with care!  -->
+    <!-- see manual for more debug info switches -->
+    <!-- global log options -->
+    <globallogs>no</globallogs> <!-- by default, do not log global session dispatching, creation etc. (not useful in multi-user operation) -->
+    <singlegloballog>no</singlegloballog> <!-- a new global log will be started for every start of the server/application -->
+    <!-- SyncML message dumping options -->
+    <msgdump>no</msgdump> <!-- do not dump syncml traffic 1:1 to files -->
+    <xmltranslate>no</xmltranslate> <!-- do not try to translate syncml traffic into XML (DO NOT SET THIS OPTION IN PRODUCTIVE SERVERS!) -->
diff --git a/src/syncevo/configs/remoterules/client/00zyb.xml b/src/syncevo/configs/remoterules/client/00zyb.xml
new file mode 100644 (file)
index 0000000..40fd61b
--- /dev/null
@@ -0,0 +1,7 @@
+      <remoterule name="ZYB">
+          <manufacturer>ZYB</manufacturer>
+          <model>ZYB</model>
+          <!-- information to disable anchors checking -->
+          <lenientmode>yes</lenientmode>
+      </remoterule>
+
diff --git a/src/syncevo/configs/remoterules/evolution.xml b/src/syncevo/configs/remoterules/evolution.xml
new file mode 100644 (file)
index 0000000..7645e49
--- /dev/null
@@ -0,0 +1,3 @@
+    <remoterule name="EVOLUTION">
+      <deviceid>none - this rule is activated via its name in MAKE/PARSETEXTWITHPROFILE() macro calls</deviceid>
+    </remoterule>
diff --git a/src/syncevo/configs/remoterules/server/00_t39m.xml b/src/syncevo/configs/remoterules/server/00_t39m.xml
new file mode 100644 (file)
index 0000000..b2f8807
--- /dev/null
@@ -0,0 +1,11 @@
+    <remoterule name="t39m">
+      <!-- Rule for Ericsson T39m client -->
+      <manufacturer>Ericsson</manufacturer>
+      <software>R1A</software>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <inputcharset>ANSI</inputcharset>
+      <descriptivename>Ericsson T39m</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/01_t68.xml b/src/syncevo/configs/remoterules/server/01_t68.xml
new file mode 100644 (file)
index 0000000..03bc6c5
--- /dev/null
@@ -0,0 +1,11 @@
+    <remoterule name="t68">
+      <!-- Rule for Ericsson T68 client -->
+      <manufacturer>Ericsson</manufacturer>
+      <software>R1B</software>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <inputcharset>ANSI</inputcharset>
+      <descriptivename>Ericsson T68</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/02_V3.xml b/src/syncevo/configs/remoterules/server/02_V3.xml
new file mode 100644 (file)
index 0000000..952b13b
--- /dev/null
@@ -0,0 +1,14 @@
+    <remoterule name="V3">
+      <!-- Rule for Motorola V3 -->
+      <manufacturer>Motorola*</manufacturer>
+      <model>V3</model>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <quote8bitcontent>yes</quote8bitcontent>
+      <nocontentfolding>yes</nocontentfolding>
+      <outputcharset>ANSI</outputcharset>
+      <inputcharset>ANSI</inputcharset>
+      <descriptivename>Motorola V3</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/03_V3i.xml b/src/syncevo/configs/remoterules/server/03_V3i.xml
new file mode 100644 (file)
index 0000000..f85500c
--- /dev/null
@@ -0,0 +1,13 @@
+    <remoterule name="V3i">
+      <!-- Rule for Motorola V3i -->
+      <manufacturer>Motorola*</manufacturer>
+      <model>V3i</model>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <quote8bitcontent>yes</quote8bitcontent>
+      <nocontentfolding>yes</nocontentfolding>
+      <outputcharset>ANSI</outputcharset>
+      <descriptivename>Motorola V3i</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/04_6230.xml b/src/syncevo/configs/remoterules/server/04_6230.xml
new file mode 100644 (file)
index 0000000..d31334c
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="6230">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6230</model>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6230</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/05_9210.xml b/src/syncevo/configs/remoterules/server/05_9210.xml
new file mode 100644 (file)
index 0000000..ef1b3cf
--- /dev/null
@@ -0,0 +1,14 @@
+    <remoterule name="9210">
+      <manufacturer>NOKIA</manufacturer>
+      <model>9210</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 9210</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/06_9210i.xml b/src/syncevo/configs/remoterules/server/06_9210i.xml
new file mode 100644 (file)
index 0000000..5925ebb
--- /dev/null
@@ -0,0 +1,14 @@
+    <remoterule name="9210i">
+      <manufacturer>NOKIA</manufacturer>
+      <model>9210i</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 9210</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/07_3220.xml b/src/syncevo/configs/remoterules/server/07_3220.xml
new file mode 100644 (file)
index 0000000..65968f8
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3220">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3220</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3220</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/08_3230.xml b/src/syncevo/configs/remoterules/server/08_3230.xml
new file mode 100644 (file)
index 0000000..ac7fe01
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3230">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3230</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3230</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/09_3600.xml b/src/syncevo/configs/remoterules/server/09_3600.xml
new file mode 100644 (file)
index 0000000..db3f2aa
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3600">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3600</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3600</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/10_3620.xml b/src/syncevo/configs/remoterules/server/10_3620.xml
new file mode 100644 (file)
index 0000000..1cd67f3
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3620">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3620</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3620</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/11_3650.xml b/src/syncevo/configs/remoterules/server/11_3650.xml
new file mode 100644 (file)
index 0000000..e06016a
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3650">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3650</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3650</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/12_3660.xml b/src/syncevo/configs/remoterules/server/12_3660.xml
new file mode 100644 (file)
index 0000000..54b5d75
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3660">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3660</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3660</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/13_6260.xml b/src/syncevo/configs/remoterules/server/13_6260.xml
new file mode 100644 (file)
index 0000000..62a59ce
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="6260">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6260</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6260</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/14_6600.xml b/src/syncevo/configs/remoterules/server/14_6600.xml
new file mode 100644 (file)
index 0000000..954902b
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="6600">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6600</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6600</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/15_6620.xml b/src/syncevo/configs/remoterules/server/15_6620.xml
new file mode 100644 (file)
index 0000000..580a93e
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="6620">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6620</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6620</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/16_6630.xml b/src/syncevo/configs/remoterules/server/16_6630.xml
new file mode 100644 (file)
index 0000000..f64994a
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="6630">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6630</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6630</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/17_6670.xml b/src/syncevo/configs/remoterules/server/17_6670.xml
new file mode 100644 (file)
index 0000000..ef3bfdf
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="6670">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6670</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6670</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/18_7250.xml b/src/syncevo/configs/remoterules/server/18_7250.xml
new file mode 100644 (file)
index 0000000..6f896e7
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="7250">
+      <manufacturer>NOKIA</manufacturer>
+      <model>7250</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 7250</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/19_7250i.xml b/src/syncevo/configs/remoterules/server/19_7250i.xml
new file mode 100644 (file)
index 0000000..7a50e2a
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="7250i">
+      <manufacturer>NOKIA</manufacturer>
+      <model>7250i</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 7250i</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/20_7260.xml b/src/syncevo/configs/remoterules/server/20_7260.xml
new file mode 100644 (file)
index 0000000..2fbf3ce
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="7260">
+      <manufacturer>NOKIA</manufacturer>
+      <model>7260</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 7260</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/21_7610.xml b/src/syncevo/configs/remoterules/server/21_7610.xml
new file mode 100644 (file)
index 0000000..c7a74c5
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="7610">
+      <manufacturer>NOKIA</manufacturer>
+      <model>7610</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 7610</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/22_7650.xml b/src/syncevo/configs/remoterules/server/22_7650.xml
new file mode 100644 (file)
index 0000000..e97bd6d
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="7650">
+      <manufacturer>NOKIA</manufacturer>
+      <model>7650</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 7650</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/23_N-Gage.xml b/src/syncevo/configs/remoterules/server/23_N-Gage.xml
new file mode 100644 (file)
index 0000000..e2e1c42
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="N-Gage">
+      <manufacturer>NOKIA</manufacturer>
+      <model>N-Gage</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia N-Gage</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/24_N-Gage_QD.xml b/src/syncevo/configs/remoterules/server/24_N-Gage_QD.xml
new file mode 100644 (file)
index 0000000..73c299a
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="N-Gage QD">
+      <manufacturer>NOKIA</manufacturer>
+      <model>N-Gage QD</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia N-Gage QD</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/25_9300.xml b/src/syncevo/configs/remoterules/server/25_9300.xml
new file mode 100644 (file)
index 0000000..9dd949a
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="9300">
+      <manufacturer>NOKIA</manufacturer>
+      <model>9300</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <treatasutc>yes</treatasutc> <!-- needs 2.1.1 or later server -->
+      <descriptivename>Nokia 9300</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/26_9500.xml b/src/syncevo/configs/remoterules/server/26_9500.xml
new file mode 100644 (file)
index 0000000..38a94a4
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="9500">
+      <manufacturer>NOKIA</manufacturer>
+      <model>9500</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <treatasutc>yes</treatasutc> <!-- needs 2.1.1 or later server -->
+      <descriptivename>Nokia 9500</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/27_E90.xml b/src/syncevo/configs/remoterules/server/27_E90.xml
new file mode 100644 (file)
index 0000000..c9250f4
--- /dev/null
@@ -0,0 +1,14 @@
+    <remoterule name="E90">
+      <manufacturer>NOKIA</manufacturer>
+      <model>E90</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <ignoredevinfmaxsize>yes</ignoredevinfmaxsize>
+      <descriptivename>Nokia E90</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/28_X.xml b/src/syncevo/configs/remoterules/server/28_X.xml
new file mode 100644 (file)
index 0000000..3977eed
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="X">
+      <manufacturer>Sendo</manufacturer>
+      <model>X</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Sendo X</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/29_SX1.xml b/src/syncevo/configs/remoterules/server/29_SX1.xml
new file mode 100644 (file)
index 0000000..b30183a
--- /dev/null
@@ -0,0 +1,17 @@
+    <remoterule name="SX1">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>SX1</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <!-- Note: SX1 crashes on contacts with empty properties -->
+      <noemptyproperties>yes</noemptyproperties>
+      <descriptivename>Siemens SX1</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/30_M55.xml b/src/syncevo/configs/remoterules/server/30_M55.xml
new file mode 100644 (file)
index 0000000..f9a6e8f
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="M55">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>M55</model>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>Siemens M55</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/31_SL55.xml b/src/syncevo/configs/remoterules/server/31_SL55.xml
new file mode 100644 (file)
index 0000000..13ce45d
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="SL55">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>SL55</model>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>Siemens SL55</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/32_S55.xml b/src/syncevo/configs/remoterules/server/32_S55.xml
new file mode 100644 (file)
index 0000000..9f898c9
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="S55">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>S55</model>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>Siemens S55</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/33_S65.xml b/src/syncevo/configs/remoterules/server/33_S65.xml
new file mode 100644 (file)
index 0000000..b58e65e
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="S65">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>S65</model>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>Siemens S65</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/34_SL65.xml b/src/syncevo/configs/remoterules/server/34_SL65.xml
new file mode 100644 (file)
index 0000000..7119285
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="SL65">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>SL65</model>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>Siemens SL65</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/35_K700.xml b/src/syncevo/configs/remoterules/server/35_K700.xml
new file mode 100644 (file)
index 0000000..45959fc
--- /dev/null
@@ -0,0 +1,14 @@
+    <remoterule name="K700">
+      <!-- Rule for SonyEricsson K700 (aka SEMC Phone) client -->
+      <manufacturer>SonyEricsson</manufacturer>
+      <model>SEMC Phone</model>
+      <software>R3B</software>
+
+      <!-- is a 1.1 client and claims UTC support, but it seems not to work ok
+      <forcelocaltime>yes</forcelocaltime>
+      -->
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>SonyEricsson K700</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/36_T610_T630.xml b/src/syncevo/configs/remoterules/server/36_T610_T630.xml
new file mode 100644 (file)
index 0000000..6cc5177
--- /dev/null
@@ -0,0 +1,25 @@
+    <remoterule name="T610/T630">
+      <!-- Rule for SonyEricsson T610/T630 client -->
+      <manufacturer>SonyEricsson</manufacturer>
+      <software>R2B</software>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>SonyEricsson T610/T630</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in M600i vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your M600i is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your M600i has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         M600i devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the M600i is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/syncevo/configs/remoterules/server/37_M600i.xml b/src/syncevo/configs/remoterules/server/37_M600i.xml
new file mode 100644 (file)
index 0000000..ea086dc
--- /dev/null
@@ -0,0 +1,32 @@
+    <remoterule name="M600i">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>M600i</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson M600i</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in P800 vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your P800 is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your P800 has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         P800 devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the P800 is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/syncevo/configs/remoterules/server/38_P800.xml b/src/syncevo/configs/remoterules/server/38_P800.xml
new file mode 100644 (file)
index 0000000..49297f9
--- /dev/null
@@ -0,0 +1,32 @@
+    <remoterule name="P800">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>P800</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson P800</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in P900 vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your P900 is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your P900 has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         P900 devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the P900 is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/syncevo/configs/remoterules/server/39_P900.xml b/src/syncevo/configs/remoterules/server/39_P900.xml
new file mode 100644 (file)
index 0000000..eaa84ea
--- /dev/null
@@ -0,0 +1,32 @@
+    <remoterule name="P900">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>P900</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson P900</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in P910 vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your P910 is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your P910 has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         P910 devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the P910 is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/syncevo/configs/remoterules/server/40_P910.xml b/src/syncevo/configs/remoterules/server/40_P910.xml
new file mode 100644 (file)
index 0000000..1b186b1
--- /dev/null
@@ -0,0 +1,32 @@
+    <remoterule name="P910">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>P910</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson P910</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in P910i vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your P910i is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your P910i has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         P910i devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the P910i is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/syncevo/configs/remoterules/server/41_P910i.xml b/src/syncevo/configs/remoterules/server/41_P910i.xml
new file mode 100644 (file)
index 0000000..8b582ce
--- /dev/null
@@ -0,0 +1,32 @@
+    <remoterule name="P910i">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>P910i</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson P910i</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in P990i vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your P990i is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your P990i has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         P990i devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the P990i is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/syncevo/configs/remoterules/server/42_P990i.xml b/src/syncevo/configs/remoterules/server/42_P990i.xml
new file mode 100644 (file)
index 0000000..8b1549c
--- /dev/null
@@ -0,0 +1,17 @@
+    <remoterule name="P990i">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>P990i</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson P990i</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/43_t68i.xml b/src/syncevo/configs/remoterules/server/43_t68i.xml
new file mode 100644 (file)
index 0000000..5d09334
--- /dev/null
@@ -0,0 +1,11 @@
+    <remoterule name="t68i">
+      <!-- Rule for SonyEricsson T68i client -->
+      <manufacturer>SonyEricsson</manufacturer>
+      <software>R2A</software>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <inputcharset>ANSI</inputcharset>
+      <descriptivename>SonyEricsson T68i</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/44_Funambol_Outlook.xml b/src/syncevo/configs/remoterules/server/44_Funambol_Outlook.xml
new file mode 100644 (file)
index 0000000..e343069
--- /dev/null
@@ -0,0 +1,9 @@
+    <remoterule name="Funambol_Outlook">
+      <!-- Rule for Funambol Outlook Sync Client -->
+      <model>Funambol Outlook Sync Client</model>
+
+      <lenientmode>yes</lenientmode> <!-- some status messages are missing at end of session -->
+      <descriptivename>Funambol Outlook Sync Client</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/remoterules/server/45_SyncJe_Outlook.xml b/src/syncevo/configs/remoterules/server/45_SyncJe_Outlook.xml
new file mode 100644 (file)
index 0000000..63c5e09
--- /dev/null
@@ -0,0 +1,9 @@
+    <remoterule name="SyncJe_Outlook">
+      <!-- Rule for NextHaus SyncJe Outlook Edition -->
+      <model>SyncJe Outlook Edition</model>
+
+      <ignorectcap>yes</ignorectcap> <!-- can do contact photo sync, but is missing in CTCap -->
+      <descriptivename>NextHaus SyncJe Outlook Client</descriptivename>
+    </remoterule>
+
+
diff --git a/src/syncevo/configs/scripting/05vcard-evolution.xml b/src/syncevo/configs/scripting/05vcard-evolution.xml
new file mode 100644 (file)
index 0000000..be79998
--- /dev/null
@@ -0,0 +1,30 @@
+   <macro name="VCARD_BEFOREWRITE_SCRIPT_EVOLUTION"><![CDATA[
+     // a wordaround for cellphone in evolution. for incoming contacts, if there is only one CELL,
+     // strip the HOME or WORK flag from it. Evolution then should show it. */
+     INTEGER i, wanted, cell_phones;
+     i = 0;
+     cell_phones = 0;
+     while(i < SIZE(TEL_FLAGS)) {
+       // 0x10 is the flag of 'cell' type of telephone
+       if(TEL_FLAGS[i] & 0x10) {
+         cell_phones = cell_phones + 1;
+         wanted = i;
+       }
+       i = i + 1;
+     }
+     if(cell_phones == 1) {
+       TEL_FLAGS[wanted] = 0x10;
+     }
+
+     // Google sends TYPE=WORK and TYPE=HOME when it means
+     // normal VOICE phone numbers. Add that flag when
+     // importing into Evolution, because Evolution does not
+     // display the numbers without VOICE.
+     i = 0;
+     while(i < SIZE(TEL_FLAGS)) {
+       if(TEL_FLAGS[i] == 1 || TEL_FLAGS[i] == 2) {
+         TEL_FLAGS[i] = TEL_FLAGS[i] | 8;
+       }
+       i = i + 1;
+     }
+   ]]></macro>
diff --git a/src/syncevo/configs/scripting/06todo-priorities.xml b/src/syncevo/configs/scripting/06todo-priorities.xml
new file mode 100644 (file)
index 0000000..5d03e82
--- /dev/null
@@ -0,0 +1,30 @@
+   <macro name="VCALENDAR_20TO10_PRIORITY_CONVERSION"><![CDATA[
+     //vCalendar10 has different interpretation from iCalendar20 in 'priority'.
+     //see mappings:  
+     //  Category      vCalendar1.0     iCalendar2.0
+     //   undefined         0               0
+     //   high              1             1 ~ 4
+     //   normal            2               5
+     //   low               3             6 ~ bigger
+     if(PRIORITY<5 && PRIORITY>0) {
+       PRIORITY=1;
+     }else if(PRIORITY==5){
+       PRIORITY=2;
+     }else if(PRIORITY>5){
+       PRIORITY=3;
+     } // 0 is undefined and remains unchanged
+   ]]></macro>
+   <macro name="VCALENDAR_10TO20_PRIORITY_CONVERSION"><![CDATA[
+     if(PRIORITY==2) {
+       PRIORITY=5;
+     }else if(PRIORITY==3){
+       PRIORITY=7;
+     } //others remain unchanged
+   ]]></macro>
+   <macro name="VCALENDAR10_BEFOREWRITE_SCRIPT"><![CDATA[
+     $VCALENDAR_20TO10_PRIORITY_CONVERSION;
+   ]]></macro>
+   <macro name="VCALENDAR10_AFTERREAD_SCRIPT"><![CDATA[
+     $VCALENDAR_10TO20_PRIORITY_CONVERSION;
+   ]]></macro>
+
diff --git a/src/syncevo/configs/scripting/06vcard-fullname.xml b/src/syncevo/configs/scripting/06vcard-fullname.xml
new file mode 100644 (file)
index 0000000..2c8288b
--- /dev/null
@@ -0,0 +1,21 @@
+    <macro name="VCARD_INCOMING_NAMECHANGE_SCRIPT"><![CDATA[
+      STRING tmp;
+      tmp=NORMALIZED(FN);
+      if (tmp==EMPTY){
+        tmp=N_FIRST;
+        if (N_MIDDLE != EMPTY) {
+          if (tmp != EMPTY) {
+            tmp = tmp + " ";
+          }
+          tmp = tmp + N_MIDDLE;
+        }
+        if (N_LAST != EMPTY) {
+          if (tmp != EMPTY) {
+            tmp = tmp + " ";
+          }
+          tmp = tmp + N_LAST;
+        }
+        FN = tmp;
+      }
+    ]]></macro>
+
diff --git a/src/syncevo/configs/scripting/10newuid.xml b/src/syncevo/configs/scripting/10newuid.xml
new file mode 100644 (file)
index 0000000..6974d87
--- /dev/null
@@ -0,0 +1,8 @@
+    <function><![CDATA[
+      // create a UID
+      string newuid() {
+        return "syuid" + NUMFORMAT(RANDOM(1000000),6,"0") + "." + (string)MILLISECONDS(NOW());
+      }
+    ]]></function>
+
+
diff --git a/src/syncevo/configs/scripting/11calendar.xml b/src/syncevo/configs/scripting/11calendar.xml
new file mode 100644 (file)
index 0000000..53c6852
--- /dev/null
@@ -0,0 +1,152 @@
+    <!-- define script macros for scripts that are used by both vCalendar 1.0 and iCalendar 2.0 -->
+
+    <macro name="VCALENDAR_INCOMING_SCRIPT"><![CDATA[
+      STRING MATCHES[];
+      STRING CAT,CN,EM;
+      INTEGER i;
+      // make sure we have all trailing and leading spaces eliminated
+      DESCRIPTION=NORMALIZED(DESCRIPTION);
+      SUMMARY=NORMALIZED(SUMMARY);
+      // make sure that we have a DESCRIPTION
+      if (DESCRIPTION==EMPTY) DESCRIPTION=SUMMARY;
+      // calendar or todo
+      if (ISEVENT) {
+        // VEVENT
+        // - handle duration cases
+        if (ISDURATION(DURATION)) {
+          if (DTEND==EMPTY) DTEND = DTSTART + DURATION;
+          if (DTSTART==EMPTY) DTSTART = DTEND - DURATION;
+        }
+        // - detect alldays in vCalendar 1.0 (0:00-0:00 or 23:59 localtime)
+        i = ALLDAYCOUNT(DTSTART,DTEND,TRUE);
+        if (ITEMDATATYPE()=="vCalendar10" && i>0) {
+          // DTSTART and DTEND represent allday event, make them date-only values
+          // - convert start to user zone (or floating) so it represents midnight
+          DTSTART = CONVERTTOUSERZONE(DTSTART);
+          MAKEALLDAY(DTSTART,DTEND,i);
+        }
+        else {
+          // iCalendar 2.0 - only if DTSTART is a date-only value this really is an allday
+          if (ISDATEONLY(DTSTART)) {
+            // reshape to make sure we don't have invalid zero-duration alldays (old OCS 9 servers)
+            MAKEALLDAY(DTSTART,DTEND,i);
+          }
+        }
+
+        // Make sure that all EXDATE times are in the same timezone as the start
+        // time. Some servers send them as UTC, which is all fine and well, but
+        // only if the timezone definition doesn't change. Also, libical does not
+        // handle such UTC EXDATEs, so let's convert it while the UTC and
+        // time zone definition (hopefully) are in sync.
+        if (TIMEZONE(DTSTART) != "UTC" && !ISFLOATING(DTSTART)) {
+          i = 0;
+          timestamp exdate;
+          while (i<SIZE(EXDATES)) {
+            exdate = EXDATES[i];
+            if (!ISDATEONLY(exdate) &&
+                (TIMEZONE(exdate) == "UTC" || ISFLOATING(exdate))) {
+              // "unfloat" floating time stamps: not sure whether that occcurs
+              // in practice, but it looks as wrong as UTC EXDATEs
+              EXDATES[i] = CONVERTTOZONE(exdate,DTSTART,TRUE);
+            }
+            i=i+1;
+          }
+        }
+        // If vcalendar1.0, rrule is not secondly, minutely, or hourly, we strip time information
+        // and only reserve date information
+        if (ITEMDATATYPE()=="vCalendar10" && RR_FREQ!="h" && RR_FREQ!="m" && RR_FREQ!="s") {
+          timestamp exdate;
+          i = 0;
+          while (i<SIZE(EXDATES)) {
+            exdate = EXDATES[i];
+            if (!ISDATEONLY(exdate)) {
+              EXDATES[i] = DATEONLY(exdate);
+            }
+            i=i+1;
+          }  
+        }
+
+        // - shape attendees (and make sure ATTENDEES[] is assigned even for empty email addresses)
+        i=0;
+        while(i<SIZE(ATTENDEES) || i<SIZE(ATTENDEE_CNS)) {
+          PARSEEMAILSPEC(ATTENDEES[i], CN, EM);
+          ATTENDEES[i] = EM; // pure email address
+          // in case we have no specific common name, use the one extracted from the email
+          // This catches the vCalendar 1.0 case and eventually ill-formed iCalendar 2.0 as well
+          if (ATTENDEE_CNS[i]==EMPTY)
+            ATTENDEE_CNS[i]=CN;
+          // default participation status to needs-action
+          if (ATTENDEE_PARTSTATS[i]==EMPTY)
+            ATTENDEE_PARTSTATS[i]=1; // 1=needs action
+          i=i+1;
+        }
+        // - shape organizer
+        PARSEEMAILSPEC(ORGANIZER, CN, EM);
+        ORGANIZER = EM; // pure email address
+        if (ORGANIZER_CN==EMPTY)
+          ORGANIZER_CN=CN;
+      }
+      else {
+        // VTODO
+        // - make sure we have at least a summary
+        if (SUMMARY==EMPTY) SUMMARY=DESCRIPTION; // use description if we don't have a summary
+        if (SUMMARY==EMPTY) SUMMARY="unnamed"; // set dummy summary if we still don't have one
+        // due shaping for non-iCalendar 2.0
+        if (ITEMDATATYPE()=="vCalendar10" && ALLDAYCOUNT(DUE,DUE,TRUE,TRUE)>0) {
+          DUE = DATEONLY(DUE);
+        }
+        if (ITEMDATATYPE()=="vCalendar10") {
+          $VCALENDAR_10TO20_PRIORITY_CONVERSION;
+        }
+      }
+      // a workaround for funambol: adding 'action' for 'alarm'
+      if (ITEMDATATYPE()=="iCalendar20") {
+        if (ALARM_TIME!=EMPTY && ALARM_ACTION==EMPTY) {
+            ALARM_ACTION = "DISPLAY";
+        }
+      }
+    ]]></macro>
+
+
+    <macro name="VCALENDAR_OUTGOING_SCRIPT"><![CDATA[
+      // set UTC time of generation for iCalendar 2.0 DTSTAMP
+      DGENERATED = NOW();
+      // make sure we have all trailing and leading spaces eliminated
+      DESCRIPTION=NORMALIZED(DESCRIPTION);
+      SUMMARY=NORMALIZED(SUMMARY);
+      if (ISEVENT) {
+        // VEVENT
+        // - combine attendee email address and common name into single string for vCalendar 1.0
+        if (ITEMDATATYPE()=="vCalendar10") {
+          i=0;
+          while(i<SIZE(ATTENDEES)) {
+            ATTENDEES[i] = MAKEEMAILSPEC(ATTENDEE_CNS[i], ATTENDEES[i]);
+            i=i+1;
+          }
+          ORGANIZER = MAKEEMAILSPEC(ORGANIZER_CN, ORGANIZER);
+        }
+      }
+      else {
+        // VTODO 
+        // interal representation is iCalendar20
+        if (ITEMDATATYPE()=="vCalendar10") {
+          $VCALENDAR_20TO10_PRIORITY_CONVERSION;
+        }
+      }
+      // make sure we have at least a summary
+      if (SUMMARY==EMPTY) SUMMARY=SUBSTR(DESCRIPTION,0,32); // derive from description
+      if (SUMMARY==EMPTY) SUMMARY="unnamed"; // in case description is empty as well
+      // make sure that we have a DESCRIPTION
+      if (DESCRIPTION==EMPTY) DESCRIPTION=SUMMARY;
+      // do NOT send duration (some servers crash when doing so)
+      DURATION = UNASSIGNED;
+      // shape alarm
+      if (ALARM_TIME!=EMPTY) {
+        if (ITEMDATATYPE()=="iCalendar20") {
+          if (ALARM_ACTION==EMPTY) ALARM_ACTION = "AUDIO";
+        }
+        else {
+          if (ALARM_MSG==EMPTY) ALARM_MSG="alarm";
+        }
+      }
+    ]]></macro>
diff --git a/src/syncevo/configs/scripting/client/00timeout.xml b/src/syncevo/configs/scripting/client/00timeout.xml
new file mode 100644 (file)
index 0000000..c56df74
--- /dev/null
@@ -0,0 +1,2 @@
+    <looptimeout>5</looptimeout>
+
diff --git a/src/syncevo/configs/scripting/server/12email.xml b/src/syncevo/configs/scripting/server/12email.xml
new file mode 100644 (file)
index 0000000..5ca7286
--- /dev/null
@@ -0,0 +1,162 @@
+    <!-- The following email handling scripts are defined as script MACROS because they
+         are used in multiple <datatype> definitions below. This avoids duplicating these
+         script's source code in the config file -->
+
+    <macro name="EMAIL_INIT_SCRIPT"><![CDATA[
+      INTEGER ITEMLIMIT;
+      // default limit is limit of session
+      ITEMLIMIT = SIZELIMIT();
+    ]]></macro>
+
+
+    <macro name="EMAIL_PROCESSITEM_SCRIPT"><![CDATA[
+      // pre-process item
+      if (UPPERCASE(FOLDER)=="INBOX") {
+        // In any case, prevent adding to inbox (delete remote items instead)
+        PREVENTADD();
+        // server always wins for inbox
+        CONFLICTSTRATEGY("server-wins");
+        if (SLOWSYNC()) {
+          // also prevent modifications in server
+          IGNOREUPDATE();
+        }
+        else {
+          // normal sync items going to inbox from client need special treatment
+          if (SYNCOP()=="add" || SYNCOP()=="replace") {
+            // make sure that existing server item will conflict with this item
+            if (LIMIT!=EMPTY && (LIMIT<0 || LIMIT>SIZELIMIT())) {
+              // force conflict only if this is a reload
+              FORCECONFLICT();
+            }
+            // make sure we never overwrite a body in the inbox
+            BODY = UNASSIGNED;
+            // delete always wins over replace in inbox (to avoid adds to inbox)
+            DELETEWINS();
+          }
+        }
+      }
+      else if (UPPERCASE(FOLDER)=="OUTBOX") {
+        // never try to change something in outbox
+        IGNOREUPDATE();
+        if (SYNCOP()!="delete") {
+          // - date of mail is NOW, set it such that a correct date is written to the DB
+          MAILDATE = DBNOW();
+          // MAILDATE = (INTEGER)DBNOW() - TIMEUNITS(120); // %%% backdate it 2 mins to make sure it does not get retransmitted
+          // - echo item as replace (to force-move it to the sent folder)
+          ECHOITEM("replace");
+        }
+        CONFLICTSTRATEGY("client-wins");
+      }
+      else if (UPPERCASE(FOLDER)=="SENT") {
+        // never try to change something in sent folder
+        IGNOREUPDATE();
+        // Server has precedence in case of conflicts
+        CONFLICTSTRATEGY("server-wins");
+        // Implement reload capability for sent items as well
+        if (SLOWSYNC()) {
+          // do not add new sent items to the server in slowsync
+          PREVENTADD(); // causes extra sent items on the client to be deleted
+        }
+        else {
+          // make sure that existing server item will conflict with this item
+          if (SYNCOP()=="replace") {
+            if (LIMIT!=EMPTY && (LIMIT<0 || LIMIT>SIZELIMIT())) {
+              // force conflict only if this is a reload
+              FORCECONFLICT();
+              REJECTITEM(200); // but do not process the item further
+            }
+            else {
+              // silently ignore other types of changes
+              REJECTITEM(200);
+            }
+            // make sure we never overwrite a body in the sent folder
+            BODY = UNASSIGNED;
+          }
+        }
+      }
+      else {
+        // Other folder
+        // - silently discard incoming item for other folder than the above
+        //   except if it is a delete
+        if (SYNCOP()!="delete")
+          REJECTITEM(0);
+      }
+    ]]></macro>
+
+
+    <macro name="EMAIL_MERGE_SCRIPT"><![CDATA[
+      // pre-process item
+      if (UPPERCASE(LOOSING.FOLDER)!="OUTBOX") {
+        // non-outbox (especially inbox) needs special merge to accomplish reload feature
+        // - loosing item is client's, winning is server's
+        if (LOOSING.LIMIT!=EMPTY) {
+          // loosing (remote) item specifies a new limit, override winning's default
+          WINNING.LIMIT=LOOSING.LIMIT;
+          SETWINNINGCHANGED(TRUE);
+        }
+        // make sure winning has right folder
+        WINNING.FOLDER=LOOSING.FOLDER;
+        // make sure a set read-flag gets propagated to server
+        if (LOOSING.ISREAD=="true") WINNING.ISREAD="true";
+        // merge other fields normally
+        MERGEFIELDS();
+        // make sure body does not get re-written to local DB even if merge would cause local update
+        LOOSING.BODY=UNASSIGNED;
+      }
+      else {
+        // normal merging in other folders
+        MERGEFIELDS();
+      }
+    ]]></macro>
+
+    <macro name="EMAIL_OUTGOING_SCRIPT"><![CDATA[
+      // pre-process item
+      if (UPPERCASE(FOLDER)=="OUTBOX") {
+        // writing to outbox is always the ECHOITEM
+        // - cause item to move into "sent" folder
+        FOLDER = "sent";
+        if (!SESSIONVAR("retransfer_body")) {
+          // - prevent body retransfer, but not for dumb P800/P900/M600/P990 clients
+          BODY = UNASSIGNED; // prevent body transfer
+          ATT_COUNT = 0; // prevent attachment transfer
+          ATT_CONTENTS = UNASSIGNED;
+          // basically, this item is not limited (already complete on the client)
+          // even if contents are not sent
+          LIMIT = -1;
+          SETSIZELIMIT(-1);
+        }
+      }
+      else {
+        // outgoing item to any folder of of remote (inbox, sent...)
+        // - limit body to what is set in the LIMIT field
+        // %%% probably obsolete, as textitem will handle limit field automatically for >=V1.0.8.21
+        IF (LIMIT==EMPTY)
+          LIMIT = SIZELIMIT(); // if none set already, use default for this item (=default of datastore, if not SETSIZELIMIT() called before for this item generation)
+      }
+      // set limit for item generator
+      if (LIMIT!=EMPTY)
+        SETSIZELIMIT(LIMIT);
+    ]]></macro>
+
+
+    <macro name="EMAIL_FILTERINIT_SCRIPT"><![CDATA[
+      // check if we need to filter
+      INTEGER NEEDFILTER;
+
+      NEEDFILTER =
+        !DBHANDLESOPTS() && // only if DB cannot handle it
+        (STARTDATE()!=EMPTY); // and only if a start date is set (end date not needed as there are never future emails today)
+      SETFILTERALL(NEEDFILTER);
+      RETURN NEEDFILTER;
+    ]]></macro>
+
+
+    <macro name="EMAIL_FILTER_SCRIPT"><![CDATA[
+      INTEGER PASSES;
+
+      // check if item passes filter
+      PASSES=FALSE;
+      // Emails pass if they have a MAILDATE on or later than start date
+      PASSES = MAILDATE>=STARTDATE();
+      RETURN PASSES;
+    ]]></macro>
diff --git a/src/syncevo/configs/syncevolution.xml b/src/syncevo/configs/syncevolution.xml
new file mode 100644 (file)
index 0000000..84a582f
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<!-- SYNTHESIS SYNCML CLIENT Version 3.2 Configuration file -->
+
+<sysync_config version="1.0">
+
+  <configvar name="logpath" value="$(defout_path)"/>
+
+  <!-- this string is output to every session debug logfile to identify the config in use -->
+  <configidstring>SyncEvolution client config</configidstring>
+
+  <!-- information about maximum supported message and object size (in bytes) -->
+  <maxmsgsize/>
+  <maxobjsize/>
+
+  <!-- information for DevInf -->
+  <model/>
+  <manufacturer/>
+  <hardwareversion/>
+  <firmwareversion/>
+  <devicetype/>
+  <configdate/>
+
+  <debug/>
+
+  <transport type="xpt">
+    <!-- allow HTTP 1.1 kepp-alive (multiple request-answer-exchanges in single TCP connection) -->
+    <keepconnection>true</keepconnection>
+  </transport>
+
+  <scripting/>
+
+  <datatypes/>
+
+  <clientorserver/>
+</sysync_config>
diff --git a/src/syncevo/configs/update-samples.pl b/src/syncevo/configs/update-samples.pl
new file mode 100755 (executable)
index 0000000..58d54b2
--- /dev/null
@@ -0,0 +1,76 @@
+#! /usr/bin/env perl
+
+sub basename {
+    $_ = shift;
+    s;.*/;;;
+    return $_;
+}
+
+# Concatenate all files ending in .xml in the given directory
+# plus those in a specific subdirectory for client or server.
+# Order lexicographic ascending of the base filename.
+
+sub readfragments {
+    my $dir = shift;
+    my $subdir = shift;
+    my @res = ();
+    
+    my @files = ();
+    if (opendir(my $dh, $dir)) {
+        foreach (grep (/.*\.xml$/, readdir($dh))) {
+            push @files, "$dir/$_";
+        }
+        closedir($dh);
+
+        if (opendir(my $dh, "$dir/$subdir")) {
+            foreach (grep (/.*\.xml$/, readdir($dh))) {
+                push @files, "$dir/$subdir/$_";
+            }
+            closedir($dh);
+        }
+    }
+
+    @files = sort { basename($a) <=> basename($b) } @files;
+    foreach (@files) {
+        open(IN, "<$_") || die "cannot read $_: $!";
+        push @res, <IN>;
+        close(IN);
+    }
+
+    return join("", @res);
+}
+
+# replace content of <debug>, <scripting>, <datatypes> and all <remoterule>s
+# with the corresponding shared and/or client/server .xml fragments
+sub update {
+    my $file = shift;
+    my $subdir = shift;
+    my $write = shift;
+
+    open(IN, "<$file") || die "cannot read $file: $!";
+    $_ = join("", <IN>);
+    close(IN) || die "closing $file: $!";
+
+    s;(<debug>\n).*(\n *</debug>);$1 . readfragments("debug", $subdir) . $2;se;
+    s;(<scripting>\n).*(\n *</scripting>);$1 . readfragments("scripting", $subdir) . $2;se ||
+        s;(\n *)<scripting/>;$1 . "<scripting>\n" . readfragments("scripting", $subdir) . $1 . "</scripting>";se;
+    s;(<datatypes>\n).*(\n *</datatypes>);$1 . readfragments("datatypes", $subdir) . $2;se ||
+        s;(\n *)<datatypes/>;$1 . "<datatypes>\n" . readfragments("datatypes", $subdir) . $1 . "</datatypes>";se;
+    s;(\n *)<remoterule>.*</remoterule>;$1 . readfragments("remoterules", $subdir);se ||
+        s;(\n *)<remoterules/>;readfragments("remoterules", $subdir);se;
+
+    if ($write) {
+        open(OUT, ">$file") || die "cannot write $file: $!";
+        print OUT;
+        close(OUT) || die "closing $file: $!";
+    } else {
+        print;
+    }
+}
+
+if ($#ARGV == -1) {
+    update("syncclient_sample_config.xml", "client", 1);
+    update("syncserv_sample_config.xml", "server", 1);
+} else {
+    update($ARGV[0], $ARGV[1], 0);
+}
index 249584d..756822b 100644 (file)
@@ -35,6 +35,7 @@ std::string lookupDebug, lookupInfo;
 }
 
 int EDSAbiHaveEbook, EDSAbiHaveEcal, EDSAbiHaveEdataserver;
+int SyncEvoHaveLibbluetooth;
 
 #ifdef EVOLUTION_COMPATIBILITY
 
@@ -42,6 +43,12 @@ struct EDSAbiWrapper EDSAbiWrapperSingleton;
 
 namespace {
 
+enum {
+    FIND_SYMBOLS_NEED_ALL = 1<<0,              /**< all symbols must be present for findSymbols() to succeed */
+    FIND_SYMBOLS_LENIENT_MAX_VERSION = 1<<1    /**< also allow libs with higher version, with extra warning */
+};
+    
+
 /**
  * Opens a <basename>.<num> shared object with <num> coming from a
  * range of known compatible versions, falling back to even more
@@ -56,15 +63,19 @@ namespace {
  * @param libname   full name including .so suffix; .<num> gets appended
  * @param minver    first known compatible version
  * @param maxver    last known compatible version
+ * @param flags     controls specific features
+ * @retval realver  found version
  * @return dlhandle which must be kept or freed by caller
  */
-void *findSymbols(const char *libname, int minver, int maxver, ... /* function pointer address, name, ..., (void *)0 */)
+void *findSymbols(const char *libname, int minver, int maxver,
+                  int flags, int *realver, ... /* function pointer address, name, ..., (void *)0 */)
 {
     void *dlhandle = NULL;
     std::ostringstream debug, info;
+    int ver;
 
     if (!dlhandle) {
-        for (int ver = maxver;
+        for (ver = maxver;
              ver >= minver;
              --ver) {
             std::ostringstream soname;
@@ -77,8 +88,8 @@ void *findSymbols(const char *libname, int minver, int maxver, ... /* function p
         }
     }
 
-    if (!dlhandle) {
-        for (int ver = maxver + 1;
+    if (!dlhandle && (flags & FIND_SYMBOLS_LENIENT_MAX_VERSION)) {
+        for (ver = maxver + 1;
              ver < maxver + 50;
              ++ver) {
             std::ostringstream soname;
@@ -90,14 +101,18 @@ void *findSymbols(const char *libname, int minver, int maxver, ... /* function p
             }
         }
     }
-    
+
+    if (realver) {
+        *realver = ver;
+    }
+
     if (!dlhandle) {
         debug << libname << " not found (tried major versions " << minver << " to " << maxver + 49 << ")" << std::endl;
     } else {
         bool allfound = true;
 
         va_list ap;
-        va_start(ap, maxver);
+        va_start(ap, realver);
         void **funcptr = va_arg(ap, void **);
         const char *symname = NULL;
         while (funcptr && allfound) {
@@ -111,9 +126,9 @@ void *findSymbols(const char *libname, int minver, int maxver, ... /* function p
         }
         va_end(ap);
 
-        if (!allfound) {
+        if (!allfound && (flags & FIND_SYMBOLS_NEED_ALL)) {
             /* unusable, clear symbols and free handle */
-            va_start(ap, maxver);
+            va_start(ap, realver);
             funcptr = va_arg(ap, void **);
             while (funcptr) {
                 va_arg(ap, const char *);
@@ -143,12 +158,18 @@ void *findSymbols(const char *libname, int minver, int maxver, ... /* function p
 # ifdef ENABLE_ECAL
     void *ecalhandle;
 # endif
+# ifdef ENABLE_BLUETOOTH
+    void *libbluetoothhandle;
+    sdp_record_t *wrap_sdp_extract_pdu(const uint8_t *pdata, int bufsize, int *scanned) { return EDSAbiWrapperSingleton.sdp_extract_pdu(pdata, scanned); }
+    int wrap_sdp_extract_seqtype(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size) { return EDSAbiWrapperSingleton.sdp_extract_seqtype(buf, dtdp, size); }
+# endif
 
 }
 
 #endif // EVOLUTION_COMPATIBILITY
 
 extern "C" int EDSAbiHaveEbook, EDSAbiHaveEcal, EDSAbiHaveEdataserver;
+extern "C" int SyncEvoHaveLibbluetooth;
 
 extern "C" void EDSAbiWrapperInit()
 {
@@ -164,6 +185,7 @@ extern "C" void EDSAbiWrapperInit()
 # ifdef HAVE_EDS
     edshandle =
     findSymbols("libedataserver-1.2.so", 7, 11,
+                FIND_SYMBOLS_NEED_ALL|FIND_SYMBOLS_LENIENT_MAX_VERSION, NULL,
                 &EDSAbiWrapperSingleton.e_source_get_type, "e_source_get_type",
                 &EDSAbiWrapperSingleton.e_source_get_uri, "e_source_get_uri",
                 &EDSAbiWrapperSingleton.e_source_group_get_type, "e_source_group_get_type",
@@ -177,6 +199,7 @@ extern "C" void EDSAbiWrapperInit()
 # ifdef ENABLE_EBOOK
     ebookhandle =
     findSymbols("libebook-1.2.so", 5, 9,
+                FIND_SYMBOLS_NEED_ALL|FIND_SYMBOLS_LENIENT_MAX_VERSION, NULL,
                 &EDSAbiWrapperSingleton.e_book_add_contact, "e_book_add_contact",
                 &EDSAbiWrapperSingleton.e_book_authenticate_user, "e_book_authenticate_user",
                 &EDSAbiWrapperSingleton.e_book_commit_contact, "e_book_commit_contact",
@@ -210,6 +233,7 @@ extern "C" void EDSAbiWrapperInit()
 # ifdef ENABLE_ECAL
     ecalhandle =
     findSymbols("libecal-1.2.so", 3, 7,
+                FIND_SYMBOLS_NEED_ALL|FIND_SYMBOLS_LENIENT_MAX_VERSION, NULL,
                 &EDSAbiWrapperSingleton.e_cal_add_timezone, "e_cal_add_timezone",
                 &EDSAbiWrapperSingleton.e_cal_component_get_icalcomponent, "e_cal_component_get_icalcomponent",
                 &EDSAbiWrapperSingleton.e_cal_component_get_last_modified, "e_cal_component_get_last_modified",
@@ -270,7 +294,56 @@ extern "C" void EDSAbiWrapperInit()
                 &EDSAbiWrapperSingleton.icaltimezone_set_component, "icaltimezone_set_component",
                 (void *)0);
     EDSAbiHaveEcal = EDSAbiWrapperSingleton.e_cal_new != 0;
+    ecalhandle =
+    findSymbols("libecal-1.2.so", 3, 7,
+                FIND_SYMBOLS_LENIENT_MAX_VERSION, NULL,
+                &EDSAbiWrapperSingleton.icalcomponent_as_ical_string_r, "icalcomponent_as_ical_string_r",
+                &EDSAbiWrapperSingleton.icaltime_as_ical_string_r, "icaltime_as_ical_string_r",
+                (void *)0);
 # endif // ENABLE_ECAL
+
+# ifdef ENABLE_BLUETOOTH
+    int bluetooth_version;
+    libbluetoothhandle =
+    findSymbols("libbluetooth.so", 2, 3, 0, &bluetooth_version,
+                &EDSAbiWrapperSingleton.sdp_close, "sdp_close",
+                &EDSAbiWrapperSingleton.sdp_connect, "sdp_connect",
+                &EDSAbiWrapperSingleton.sdp_extract_pdu, "sdp_extract_pdu",
+                &EDSAbiWrapperSingleton.sdp_extract_pdu_safe, "sdp_extract_pdu_safe",
+                &EDSAbiWrapperSingleton.sdp_extract_seqtype, "sdp_extract_seqtype",
+                &EDSAbiWrapperSingleton.sdp_extract_seqtype_safe, "sdp_extract_seqtype_safe",
+                &EDSAbiWrapperSingleton.sdp_get_access_protos, "sdp_get_access_protos",
+                &EDSAbiWrapperSingleton.sdp_get_proto_port, "sdp_get_proto_port",
+                &EDSAbiWrapperSingleton.sdp_get_socket, "sdp_get_socket",
+                &EDSAbiWrapperSingleton.sdp_list_append, "sdp_list_append",
+                &EDSAbiWrapperSingleton.sdp_list_free, "sdp_list_free",
+                &EDSAbiWrapperSingleton.sdp_process, "sdp_process",
+                &EDSAbiWrapperSingleton.sdp_record_free, "sdp_record_free",
+                &EDSAbiWrapperSingleton.sdp_service_search_attr_async, "sdp_service_search_attr_async",
+                &EDSAbiWrapperSingleton.sdp_set_notify, "sdp_set_notify",
+                &EDSAbiWrapperSingleton.sdp_uuid128_create, "sdp_uuid128_create",
+                &EDSAbiWrapperSingleton.str2ba, "str2ba",
+                (void *)0);
+    if (bluetooth_version == 2) {
+        // libbluetooth.so.2's sdp_extract_pdu() and
+        // sdp_extract_seqtype() do not match our prototype (have no
+        // bufsize). Some versions have a _safe variant. If not, use
+        // wrappers which accept the bufsize parameter and drop it.
+        if (!EDSAbiWrapperSingleton.sdp_extract_pdu_safe) {
+            EDSAbiWrapperSingleton.sdp_extract_pdu_safe = wrap_sdp_extract_pdu;
+        }
+        if (!EDSAbiWrapperSingleton.sdp_extract_seqtype_safe) {
+            EDSAbiWrapperSingleton.sdp_extract_seqtype_safe = wrap_sdp_extract_seqtype;
+        }
+    } else {
+        // _safe variants were removed in favor of an API/ABI change. Redirect
+        // into normal versions. We know they have the right prototype because
+        // of that change.
+        EDSAbiWrapperSingleton.sdp_extract_pdu_safe = reinterpret_cast<typeof(EDSAbiWrapperSingleton.sdp_extract_pdu_safe)>(EDSAbiWrapperSingleton.sdp_extract_pdu);
+        EDSAbiWrapperSingleton.sdp_extract_seqtype_safe = reinterpret_cast<typeof(EDSAbiWrapperSingleton.sdp_extract_seqtype_safe)>(EDSAbiWrapperSingleton.sdp_extract_seqtype);
+    }
+    SyncEvoHaveLibbluetooth = EDSAbiWrapperSingleton.sdp_connect != 0;
+# endif
 #else // EVOLUTION_COMPATIBILITY
 # ifdef HAVE_EDS
     EDSAbiHaveEdataserver = true;
@@ -281,6 +354,9 @@ extern "C" void EDSAbiWrapperInit()
 # ifdef ENABLE_ECAL
     EDSAbiHaveEcal = true;
 # endif
+# ifdef ENABLE_BLUETOOTH
+    SyncEvoHaveLibbluetooth = true;
+# endif
 #endif // EVOLUTION_COMPATIBILITY
 }
 
index 527f98f..7410181 100644 (file)
@@ -18,7 +18,7 @@
  */
 
 /**
- * The purpose of this file is to separate SyncEvolution from
+ * The main purpose of this file is to separate SyncEvolution from
  * Evolution Dataserver ABI changes by never depending directly
  * on any symbol in its libraries. Instead all functions are
  * called via function pointers found via dlopen/dlsym.
  * may fail when the functions needed by SyncEvolution change.
  *
  * History shows that this has not happened for a long time whereas
- * the version of libs had to be bumped quite a few times due to other
+ * the versions of EDS libs had to be bumped quite a few times due to other
  * changes.
  *
+ * A similar problem came up with an API and ABI change in
+ * libbluetooth.so.3: the sdp_extract_seqtype() and sdp_extract_pdu()
+ * gained one more parameter (a buffer size for the buffer being
+ * written into). When compatibility mode is enabled, this header
+ * file provides the extended version and maps it back to the older
+ * version if necessary.
+ *
  * To use the wrappers, include this header file. It overrides
  * the normal C API functions with the function pointers via
  * defines.
 #include <libecal/e-cal.h>
 #endif
 #endif
+#ifdef ENABLE_BLUETOOTH
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -64,6 +75,9 @@ extern "C" {
 /** libebook, libecal, libedataserver available (currently checks for e_book_new/e_cal_new/e_source_group_peek_sources) */
 extern int EDSAbiHaveEbook, EDSAbiHaveEcal, EDSAbiHaveEdataserver;
 
+/** libbluetooth available (checks sdp_connect()) */
+extern int SyncEvoHaveLibbluetooth;
+
 #ifdef EVOLUTION_COMPATIBILITY
 
 /**
@@ -172,8 +186,36 @@ struct EDSAbiWrapper {
     char* (*icaltimezone_get_tzid) (icaltimezone *zone);
     icaltimezone *(*icaltimezone_new) (void);
     int (*icaltimezone_set_component) (icaltimezone *zone, icalcomponent *comp);
+
+    // optional variants which allocate the returned string for us
+    const char* (*icaltime_as_ical_string_r) (const struct icaltimetype tt);
+    char* (*icalcomponent_as_ical_string_r) (icalcomponent* component);
 # endif /* ENABLE_ECAL */
 
+# ifdef ENABLE_BLUETOOTH
+    int (*sdp_close)(sdp_session_t *session);
+    sdp_session_t *(*sdp_connect)(const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags);
+    /** libbluetooth.so.2 version of sdp_extract_pdu() */
+    sdp_record_t *(*sdp_extract_pdu)(const uint8_t *pdata, int *scanned);
+    /** alternate version of sdp_extract_pdu() only found in some releases of libbluetooth.so.2 */
+    sdp_record_t *(*sdp_extract_pdu_safe)(const uint8_t *pdata, int bufsize, int *scanned);
+    /** libbluetooth.so.2 version of sdp_extract_seqtype() */
+    int (*sdp_extract_seqtype)(const uint8_t *buf, uint8_t *dtdp, int *size);
+    /** alternate version of sdp_extract_seqtype() only found in some releases of libbluetooth.so.2 */
+    int (*sdp_extract_seqtype_safe)(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size);
+    int (*sdp_get_access_protos)(const sdp_record_t *rec, sdp_list_t **protos);
+    int (*sdp_get_proto_port)(const sdp_list_t *list, int proto);
+    int (*sdp_get_socket)(const sdp_session_t *session);
+    sdp_list_t *(*sdp_list_append)(sdp_list_t *list, void *d);
+    void        (*sdp_list_free)(sdp_list_t *list, sdp_free_func_t f);
+    int (*sdp_process)(sdp_session_t *session);
+    void (*sdp_record_free)(sdp_record_t *rec);
+    int (*sdp_service_search_attr_async)(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list);
+    int (*sdp_set_notify)(sdp_session_t *session, sdp_callback_t *func, void *udata);
+    uuid_t *(*sdp_uuid128_create)(uuid_t *uuid, const void *data);
+    int (*str2ba)(const char *str, bdaddr_t *ba);
+# endif /* ENABLE_BLUETOOTH */
+
     int initialized;
 };
 
@@ -241,7 +283,7 @@ extern struct EDSAbiWrapper EDSAbiWrapperSingleton;
 #   define e_cal_remove_object_with_mod EDSAbiWrapperSingleton.e_cal_remove_object_with_mod
 #   define e_cal_set_auth_func EDSAbiWrapperSingleton.e_cal_set_auth_func
 #   define icalcomponent_add_component EDSAbiWrapperSingleton.icalcomponent_add_component
-#   define icalcomponent_as_ical_string EDSAbiWrapperSingleton.icalcomponent_as_ical_string
+#   define icalcomponent_as_ical_string (EDSAbiWrapperSingleton.icalcomponent_as_ical_string_r ? EDSAbiWrapperSingleton.icalcomponent_as_ical_string_r : EDSAbiWrapperSingleton.icalcomponent_as_ical_string)
 #   define icalcomponent_free EDSAbiWrapperSingleton.icalcomponent_free
 #   define icalcomponent_get_first_component EDSAbiWrapperSingleton.icalcomponent_get_first_component
 #   define icalcomponent_get_first_property EDSAbiWrapperSingleton.icalcomponent_get_first_property
@@ -269,7 +311,7 @@ extern struct EDSAbiWrapper EDSAbiWrapperSingleton;
 #   define icalproperty_new_summary EDSAbiWrapperSingleton.icalproperty_new_summary
 #   define icalproperty_set_value_from_string EDSAbiWrapperSingleton.icalproperty_set_value_from_string
 #   define icalproperty_remove_parameter_by_kind EDSAbiWrapperSingleton.icalproperty_remove_parameter_by_kind
-#   define icaltime_as_ical_string EDSAbiWrapperSingleton.icaltime_as_ical_string
+#   define icaltime_as_ical_string (EDSAbiWrapperSingleton.icaltime_as_ical_string_r ? EDSAbiWrapperSingleton.icaltime_as_ical_string_r : EDSAbiWrapperSingleton.icaltime_as_ical_string)
 #   define icaltimezone_free EDSAbiWrapperSingleton.icaltimezone_free
 #   define icaltimezone_get_builtin_timezone EDSAbiWrapperSingleton.icaltimezone_get_builtin_timezone
 #   define icaltimezone_get_builtin_timezone_from_tzid EDSAbiWrapperSingleton.icaltimezone_get_builtin_timezone_from_tzid
@@ -278,8 +320,39 @@ extern struct EDSAbiWrapper EDSAbiWrapperSingleton;
 #   define icaltimezone_new EDSAbiWrapperSingleton.icaltimezone_new
 #   define icaltimezone_set_component EDSAbiWrapperSingleton.icaltimezone_set_component
 #  endif /* ENABLE_ECAL */
+#  ifdef ENABLE_BLUETOOTH
+#   define sdp_close EDSAbiWrapperSingleton.sdp_close
+#   define sdp_connect EDSAbiWrapperSingleton.sdp_connect
+#   define sdp_extract_pdu do_not_use_sdp_extract_pdu
+#   define sdp_extract_pdu_safe EDSAbiWrapperSingleton.sdp_extract_pdu_safe
+#   define sdp_extract_seqtype do_not_use_sdp_extract_seqtype
+#   define sdp_extract_seqtype_safe EDSAbiWrapperSingleton.sdp_extract_seqtype_safe
+#   define sdp_get_access_protos EDSAbiWrapperSingleton.sdp_get_access_protos
+#   define sdp_get_proto_port EDSAbiWrapperSingleton.sdp_get_proto_port
+#   define sdp_get_socket EDSAbiWrapperSingleton.sdp_get_socket
+#   define sdp_list_append EDSAbiWrapperSingleton.sdp_list_append
+#   define sdp_list_free EDSAbiWrapperSingleton.sdp_list_free
+#   define sdp_process EDSAbiWrapperSingleton.sdp_process
+#   define sdp_record_free EDSAbiWrapperSingleton.sdp_record_free
+#   define sdp_service_search_attr_async EDSAbiWrapperSingleton.sdp_service_search_attr_async
+#   define sdp_set_notify EDSAbiWrapperSingleton.sdp_set_notify
+#   define sdp_uuid128_create EDSAbiWrapperSingleton.sdp_uuid128_create
+#   define str2ba EDSAbiWrapperSingleton.str2ba
+#  endif /* ENABLE_BLUETOOTH */
 # endif /* EDS_ABI_WRAPPER_NO_REDEFINE */
 
+#else /* EVOLUTION_COMPATIBILITY */
+
+# if !defined(EDS_ABI_WRAPPER_NO_REDEFINE) && defined(HAVE_LIBICAL_R)
+#  ifdef ENABLE_ECAL
+#   ifndef LIBICAL_MEMFIXES
+     /* This changes the semantic of the normal functions the same way as libecal did. */
+#    define LIBICAL_MEMFIXES 1
+#    define icaltime_as_ical_string icaltime_as_ical_string_r
+#    define icalcomponent_as_ical_string icalcomponent_as_ical_string_r
+#   endif /* LIBICAL_MEMFIXES */
+#  endif /* ENABLE_ECAL */
+# endif /* EDS_ABI_WRAPPER_NO_REDEFINE */
 #endif /* EVOLUTION_COMPATIBILITY */
 
 /** initialize pointers to EDS functions, if necessary; can be called multiple times */
index c59ae93..656eb49 100644 (file)
 #include <sys/types.h>
 #include <dirent.h>
 
+#if USE_SHA256 == 1
+# include <glib.h>
+#elif USE_SHA256 == 2
+# include <nss/sechash.h>
+# include <nss/hasht.h>
+# include <nss.h>
+#endif
+
 #ifdef ENABLE_UNIT_TESTS
 #include "test.h"
 CPPUNIT_REGISTRY_ADD_TO_DEFAULT("SyncEvolution");
@@ -268,6 +276,54 @@ unsigned long Hash(const char *str)
     return hashval;
 }
 
+unsigned long Hash(const std::string &str)
+{
+    unsigned long hashval = 5381;
+
+    BOOST_FOREACH(int c, str) {
+        hashval = ((hashval << 5) + hashval) + c;
+    }
+
+    return hashval;
+}
+
+std::string SHA_256(const std::string &data)
+{
+#if USE_SHA256 == 1
+    GString hash(g_compute_checksum_for_data(G_CHECKSUM_SHA256, (guchar *)data.c_str(), data.size()),
+                 "g_compute_checksum_for_data() failed");
+    return std::string(hash.get());
+#elif USE_SHA256 == 2
+    std::string res;
+    unsigned char hash[SHA256_LENGTH];
+    static bool initialized;
+    if (!initialized) {
+        // https://wiki.mozilla.org/NSS_Shared_DB_And_LINUX has
+        // some comments which indicate that calling init multiple
+        // times works, but http://www.mozilla.org/projects/security/pki/nss/ref/ssl/sslfnc.html#1234224
+        // says it must only be called once. How that is supposed
+        // to work when multiple, independent libraries have to
+        // use NSS is beyond me. Bad design. At least let's do the
+        // best we can here.
+        NSS_NoDB_Init(NULL);
+       initialized = true;
+    }
+
+    if (HASH_HashBuf(HASH_AlgSHA256, hash, (unsigned char *)data.c_str(), data.size()) != SECSuccess) {
+        SE_THROW("NSS HASH_HashBuf() failed");
+    }
+    res.reserve(SHA256_LENGTH * 2);
+    BOOST_FOREACH(unsigned char value, hash) {
+        res += StringPrintf("%02x", value);
+    }
+    return res;
+#else
+    SE_THROW("Hash256() not implemented");
+    return "";
+#endif
+}
+
+
 std::string StringPrintf(const char *format, ...)
 {
     va_list ap;
@@ -416,5 +472,26 @@ std::vector<std::string> unescapeJoinedString (const std::string& src, char sep)
     return splitStrings;
 }
 
+ScopedEnvChange::ScopedEnvChange(const string &var, const string &value) :
+    m_var(var)
+{
+    const char *oldval = getenv(var.c_str());
+    if (oldval) {
+        m_oldvalset = true;
+        m_oldval = oldval;
+    } else {
+        m_oldvalset = false;
+    }
+    setenv(var.c_str(), value.c_str(), 1);
+}
+
+ScopedEnvChange::~ScopedEnvChange()
+{
+    if (m_oldvalset) {
+        setenv(m_var.c_str(), m_oldval.c_str(), 1);
+    } else {
+        unsetenv(m_var.c_str());
+    } 
+}
 
 SE_END_CXX
index 2ef48d3..4953262 100644 (file)
@@ -111,6 +111,13 @@ bool ReadFile(const string &filename, string &content);
  * Simple string hash function, derived from Dan Bernstein's algorithm.
  */
 unsigned long Hash(const char *str);
+unsigned long Hash(const std::string &str);
+
+/**
+ * SHA-256 implementation, returning hash as lowercase hex string (like sha256sum).
+ * Might not be available, in which case it raises an exception.
+ */
+std::string SHA_256(const std::string &in);
 
 /**
  * This is a simplified implementation of a class representing and calculating
@@ -241,6 +248,20 @@ inline string getHome() {
  * */
 std::vector<std::string> unescapeJoinedString (const std::string &src, char separator);
 
+/**
+ * Temporarily set env variable, restore old value on destruction.
+ * Useful for unit tests which depend on the environment.
+ */
+class ScopedEnvChange
+{
+ public:
+    ScopedEnvChange(const string &var, const string &value);
+    ~ScopedEnvChange();
+ private:
+    string m_var, m_oldval;
+    bool m_oldvalset;
+};
+
 /** throw a normal SyncEvolution Exception, including source information */
 #define SE_THROW(_what) \
     SE_THROW_EXCEPTION(Exception, _what)
index 062ed40..7ee7858 100644 (file)
@@ -1,6 +1,349 @@
 # Generated by configure.  Do no edit.
-# git revision 29763216a67787705ecdce113701a17542ab2721
-# git tag libsynthesis_3.2.0.35+syncevolution-0-9-1-beta1-150-g2976321
+# git revision 904d1fb47ecd39c58f349012fd4c343d6fcf63e9
+# git tag libsynthesis_3.4.0.5+syncevolution-1-0-beta-2
+
+2010-02-23  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/Makefile.am.in:
+
+       .so version bumped to 0.3.1 (minor revision increased)
+
+2010-02-18  Lukas Zeller  <luz@synthesis.ch>
+
+
+       Merge remote branch 'moblin/master' into luz
+
+2010-02-18  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/global_options.h:
+       * src/sysync/binfileimplclient.cpp:
+       * src/sysync/customimplds.h:
+       * src/sysync/localengineds.cpp:
+       * src/sysync/localengineds.h:
+       * src/sysync/stdlogicds.h:
+       * src/sysync/superdatastore.cpp:
+       * src/sysync/superdatastore.h:
+       * src/sysync/syncagent.cpp:
+
+       Engine 3.4.0.5: implemented client-side superdatastores according
+       to suggestion/draft implementation of Patrick
+
+2010-02-18  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/sysync/syncsession.cpp:
+
+       engine: now shows engine-determined OS/version as well as
+       configured hwv/fwv in log
+
+2010-02-16  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/sysync/syncappbase.cpp:
+
+       engine: added more configurable devInf data via config variables
+
+2010-02-13  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/sysync/syncagent.cpp:
+       * src/sysync/syncappbase.cpp:
+       * src/sysync/syncappbase.h:
+       * src/sysync/syncsession.cpp:
+
+       engine: added configurable device ID (via "customdeviceid" config
+       variable)
+
+2010-02-11  Chen Congwu  <congwu.chen@intel.com>
+
+       * src/syncml_tk/src/sml/lib/all/libstr.c:
+       * src/syncml_tk/src/sml/lib/inc/libstr.h:
+       * src/syncml_tk/src/sml/xlt/all/xltdecxml.c:
+       * src/syncml_tk/src/sml/xlt/all/xlttags.c:
+
+       xmltk fixes for Nokia ovi service
+
+2010-02-09  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/sysync_SDK/Sources/enginemodulebase.cpp:
+
+       TEngineModuleBase::Connect() - fixed compiler warning
+
+2010-02-09  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/sysync/synccommand.cpp:
+
+       do not treat 418 = "item exists" as an error
+
+2010-02-04  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/sysync/syncagent.cpp:
+       * src/sysync_SDK/Sources/syerror.h:
+
+       LOCERR_DATASTORE_ABORT: abort store, not session
+
+2010-02-05  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/global_options.h:
+       * src/sysync/localengineds.cpp:
+       * src/sysync/mimedirprofile.cpp:
+       * src/sysync/mimedirprofile.h:
+       * src/sysync/scriptcontext.cpp:
+       * src/sysync/syncsession.cpp:
+       * src/sysync/syncsession.h:
+
+       Engine 3.4.0.4: enhanced remote rules with subrules and multiple
+       active rules per session
+
+2010-02-05  Lukas Zeller  <luz@synthesis.ch>
+
+
+       Merge remote branch 'moblin/master' into luz
+
+2010-02-03  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/Targets/clientEngine_dbg/target_options.h:
+
+       target_options for basic DBG engine: cosmetic cleanup, not
+       relevant for normal libsynthesis builds
+
+2010-02-03  Beat Forster  <bfo@synthesis.ch>
+
+       * src/platform_adapters/linux/configfiles.cpp:
+
+       Android: configfiles: avoid double slash in path name
+
+2010-02-03  Beat Forster  <bfo@synthesis.ch>
+
+       * src/sysync_SDK/Sources/SDK_util.c:
+
+       SDK_util.c : cosmetic - renamed define
+
+2010-02-03  Beat Forster  <bfo@synthesis.ch>
+
+       * src/sysync_SDK/Sources/sync_dbapidef.h:
+
+       sync_dbapidef.h:
+
+2010-02-01  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/sysync_SDK/configs/README:
+       * src/sysync_SDK/configs/datatypes/00vcard-fieldlist.xml:
+       * src/sysync_SDK/configs/datatypes/01vcard-profile.xml:
+       * src/sysync_SDK/configs/datatypes/02vcard-types.xml:
+       * src/sysync_SDK/configs/datatypes/10calendar-fieldlist.xml:
+       * src/sysync_SDK/configs/datatypes/11calendar-profile.xml:
+       * src/sysync_SDK/configs/datatypes/12calendar-types.xml:
+       * src/sysync_SDK/configs/datatypes/20note-fieldlist.xml:
+       * src/sysync_SDK/configs/datatypes/21note-profile.xml:
+       * src/sysync_SDK/configs/datatypes/22notes-types.xml:
+       * src/sysync_SDK/configs/datatypes/30bookmark-fieldlist.xml:
+       * src/sysync_SDK/configs/datatypes/31bookmark-profile.xml:
+       * src/sysync_SDK/configs/datatypes/32bookmark-type.xml:
+       * src/sysync_SDK/configs/datatypes/server/40email-fieldlist.xml:
+       * src/sysync_SDK/configs/datatypes/server/41email-profile.xml:
+       * src/sysync_SDK/configs/datatypes/server/42email-type-zipped.xml:
+       * src/sysync_SDK/configs/datatypes/server/42email-type.xml:
+       * src/sysync_SDK/configs/datatypes/server/43email-sonyericsson.xml:
+       * src/sysync_SDK/configs/datatypes/server/44email-nokia9500.xml:
+       * src/sysync_SDK/configs/debug/00default.xml:
+       * src/sysync_SDK/configs/remoterules/server/00_t39m.xml:
+       * src/sysync_SDK/configs/remoterules/server/01_t68.xml:
+       * src/sysync_SDK/configs/remoterules/server/02_V3.xml:
+       * src/sysync_SDK/configs/remoterules/server/03_V3i.xml:
+       * src/sysync_SDK/configs/remoterules/server/04_6230.xml:
+       * src/sysync_SDK/configs/remoterules/server/05_9210.xml:
+       * src/sysync_SDK/configs/remoterules/server/06_9210i.xml:
+       * src/sysync_SDK/configs/remoterules/server/07_3220.xml:
+       * src/sysync_SDK/configs/remoterules/server/08_3230.xml:
+       * src/sysync_SDK/configs/remoterules/server/09_3600.xml:
+       * src/sysync_SDK/configs/remoterules/server/10_3620.xml:
+       * src/sysync_SDK/configs/remoterules/server/11_3650.xml:
+       * src/sysync_SDK/configs/remoterules/server/12_3660.xml:
+       * src/sysync_SDK/configs/remoterules/server/13_6260.xml:
+       * src/sysync_SDK/configs/remoterules/server/14_6600.xml:
+       * src/sysync_SDK/configs/remoterules/server/15_6620.xml:
+       * src/sysync_SDK/configs/remoterules/server/16_6630.xml:
+       * src/sysync_SDK/configs/remoterules/server/17_6670.xml:
+       * src/sysync_SDK/configs/remoterules/server/18_7250.xml:
+       * src/sysync_SDK/configs/remoterules/server/19_7250i.xml:
+       * src/sysync_SDK/configs/remoterules/server/20_7260.xml:
+       * src/sysync_SDK/configs/remoterules/server/21_7610.xml:
+       * src/sysync_SDK/configs/remoterules/server/22_7650.xml:
+       * src/sysync_SDK/configs/remoterules/server/23_N-Gage.xml:
+       * src/sysync_SDK/configs/remoterules/server/24_N-Gage_QD.xml:
+       * src/sysync_SDK/configs/remoterules/server/25_9300.xml:
+       * src/sysync_SDK/configs/remoterules/server/26_9500.xml:
+       * src/sysync_SDK/configs/remoterules/server/27_E90.xml:
+       * src/sysync_SDK/configs/remoterules/server/28_X.xml:
+       * src/sysync_SDK/configs/remoterules/server/29_SX1.xml:
+       * src/sysync_SDK/configs/remoterules/server/30_M55.xml:
+       * src/sysync_SDK/configs/remoterules/server/31_SL55.xml:
+       * src/sysync_SDK/configs/remoterules/server/32_S55.xml:
+       * src/sysync_SDK/configs/remoterules/server/33_S65.xml:
+       * src/sysync_SDK/configs/remoterules/server/34_SL65.xml:
+       * src/sysync_SDK/configs/remoterules/server/35_K700.xml:
+       * src/sysync_SDK/configs/remoterules/server/36_T610_T630.xml:
+       * src/sysync_SDK/configs/remoterules/server/37_M600i.xml:
+       * src/sysync_SDK/configs/remoterules/server/38_P800.xml:
+       * src/sysync_SDK/configs/remoterules/server/39_P900.xml:
+       * src/sysync_SDK/configs/remoterules/server/40_P910.xml:
+       * src/sysync_SDK/configs/remoterules/server/41_P910i.xml:
+       * src/sysync_SDK/configs/remoterules/server/42_P990i.xml:
+       * src/sysync_SDK/configs/remoterules/server/43_t68i.xml:
+       * src/sysync_SDK/configs/remoterules/server/44_Funambol_Outlook.xml:
+       * src/sysync_SDK/configs/remoterules/server/45_SyncJe_Outlook.xml:
+       * src/sysync_SDK/configs/scripting/10newuid.xml:
+       * src/sysync_SDK/configs/scripting/11calendar.xml:
+       * src/sysync_SDK/configs/scripting/client/00timeout.xml:
+       * src/sysync_SDK/configs/scripting/server/12email.xml:
+       * src/sysync_SDK/configs/syncclient_sample_config.xml:
+       * src/sysync_SDK/configs/syncserv_sample_config.xml:
+       * src/sysync_SDK/configs/update-samples.pl:
+
+       XML config samples: split up into individual fragments
+
+2010-02-02  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/sysync/stdlogicds.cpp:
+
+       Added check such that if <datastoreinitscript> calls
+       ABORTDATASTORE(), the abort gets propagated
+
+2010-02-01  Patrick Ohly  <patrick.ohly@intel.com>
+
+       * src/sysync_SDK/configs/syncserv_sample_config.xml:
+
+       syncserv_sample_config.xml: removed <CA> white space
+
+2010-01-28  BeatForster  <elisabethegli@bfo-MacBookPro.local>
+
+       * src/sysync/tz_table.h:
+
+       tz_table.h: Argentina changed to America/Buenos_Aires
+
+2010-01-25  BeatForster  <elisabethegli@bfo-MacBookPro.local>
+
+       * src/sysync/tz_table.h:
+
+       ART/ARST timezone definitions for Buenos Aires, Argentina added
+
+2009-12-23  BeatForster  <elisabethegli@bfo-MacBookPro.local>
+
+       * src/DB_interfaces/api_db/dbapi.cpp:
+       * src/DB_interfaces/api_db/dbapi.h:
+       * src/sysync_SDK/Sources/SDK_support.cpp:
+       * src/sysync_SDK/Sources/SDK_support.h:
+       * src/sysync_SDK/Sources/sync_dbapidef.h:
+
+       SDK: - create list of built-in plugins (plugin_info/logger) -
+       plugins can be accessed via #1..#X - break recursion at the
+       logger - dbapi: <fPlugin> added - Definition for CA_Plugin
+
+2009-12-23  BeatForster  <elisabethegli@bfo-MacBookPro.local>
+
+       * src/sysync_SDK/Sources/SDK_util.c:
+       * src/sysync_SDK/Sources/sync_dbapidef.h:
+
+       SDK: - Version changed to V1.7.0 - at least callbackVersion=2
+       must be supported
+
+2009-12-20  BeatForster  <elisabethegli@bfo-MacBookPro.local>
+
+       * src/client_engine_linux.mk:
+
+       Linux makefiles, several tabs/spaces replaced, did not work any
+       more like this
+
+2009-12-17  Beat Forster  <elisabethegli@beat-forsters-macbook-pro.local>
+
+       * src/DB_interfaces/api_db/dbapi.cpp:
+       * src/DB_interfaces/api_db/dbapi_include.h:
+       * src/DB_interfaces/api_db/sync_dbapiconnect.cpp:
+       * src/sysync_SDK/Sources/sync_dbapidef.h:
+
+       DBAPi: Old variant of Module_PluginParams no longer supported
+
+2009-12-03  Beat Forster  <elisabethegli@beat-forsters-macbook-pro.local>
+
+       * src/DB_interfaces/api_db/dbapi.cpp:
+       * src/DB_interfaces/api_db/dbapi.h:
+       * src/DB_interfaces/api_db/dbapi_include.h:
+       * src/DB_interfaces/api_db/sync_dbapiconnect.cpp:
+       * src/sysync_SDK/Sources/SDK_support.cpp:
+       * src/sysync_SDK/Sources/sync_dbapidef.h:
+
+       JNI/Java: Several bug fixes and enhancements - Preparing
+       com/sysync package for V1.6.2 - Old BLOB signature supported
+       until V1.6.1 - ModSubName() method added to TDB_Api_Config - both
+       syntax versions for package path supported - multiple connection
+       problems fixed - correct error for non-existing packages -
+       sysytest output layout adapted for packages
+
+2009-12-02  Beat Forster  <elisabethegli@beat-forsters-macbook-pro.local>
+
+       * src/sysync/engineinterface.cpp:
+       * src/sysync_SDK/Sources/enginemodulebase.cpp:
+
+       SDK: enginemodulebase applies older callbackVersion correctly now
+
+2010-01-27  Lukas Zeller  <luz@synthesis.ch>
+
+       * doc/SySync_script_call_flow.numbers/Contents/PkgInfo:
+       * doc/SySync_script_call_flow.numbers/QuickLook/Thumbnail.jpg:
+       * doc/SySync_script_call_flow.numbers/SySync diagram_plugin_small.jpg:
+       * doc/SySync_script_call_flow.numbers/document-thumbnail.tiff:
+       * doc/SySync_script_call_flow.numbers/index.xml.gz:
+       * doc/SySync_script_call_flow.pdf:
+
+       Doc: useful table showing the call flow of scripts within a
+       server session
+
+2010-01-27  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/global_options.h:
+
+       New Engine Version 3.4.0.3 started to show fact that moblin
+       patches are now included
+
+2010-01-27  Lukas Zeller  <luz@synthesis.ch>
+
+
+       Merge remote branch 'moblin/master' into luz_tmp
+
+2010-01-27  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/global_options.h:
+       * src/sysync/localengineds.cpp:
+
+       Engine 3.4.0.2: fixed long standing bug parsing CGI filters with
+       paranthesized subexpressions
+
+2010-01-27  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/sysync/syncappbase.cpp:
+       * src/sysync/syserial.h:
+
+       NEVER_EXPIRES_IS_OK define is now required in target_options.h to
+       build targets without any licensing or expiry date
+
+2010-01-27  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/sysync/syncagent.cpp:
+       * src/sysync/syncsession.cpp:
+
+       bugfix for unilib regression: there must be no requestmaxtime
+       limit for clients!
+
+2010-01-27  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/sysync/rrules.cpp:
+
+       rrules: recurrence expansion bug with weekly recurrences fixed
+
+2010-01-27  Lukas Zeller  <luz@synthesis.ch>
+
+       * src/sysync/debuglogger.cpp:
+
+       debuglogger: avoid the need to create an empty va_list (which is
+       not cleanly possible)
 
 2010-01-18  Ove Kaaven  <ovek@debian.org>
 
diff --git a/src/synthesis/doc/SySync_script_call_flow.numbers/Contents/PkgInfo b/src/synthesis/doc/SySync_script_call_flow.numbers/Contents/PkgInfo
new file mode 100644 (file)
index 0000000..b0bc8e0
--- /dev/null
@@ -0,0 +1 @@
+????????
\ No newline at end of file
diff --git a/src/synthesis/doc/SySync_script_call_flow.numbers/QuickLook/Thumbnail.jpg b/src/synthesis/doc/SySync_script_call_flow.numbers/QuickLook/Thumbnail.jpg
new file mode 100644 (file)
index 0000000..fb24626
Binary files /dev/null and b/src/synthesis/doc/SySync_script_call_flow.numbers/QuickLook/Thumbnail.jpg differ
diff --git a/src/synthesis/doc/SySync_script_call_flow.numbers/SySync diagram_plugin_small.jpg b/src/synthesis/doc/SySync_script_call_flow.numbers/SySync diagram_plugin_small.jpg
new file mode 100644 (file)
index 0000000..d78abdc
Binary files /dev/null and b/src/synthesis/doc/SySync_script_call_flow.numbers/SySync diagram_plugin_small.jpg differ
diff --git a/src/synthesis/doc/SySync_script_call_flow.numbers/document-thumbnail.tiff b/src/synthesis/doc/SySync_script_call_flow.numbers/document-thumbnail.tiff
new file mode 100644 (file)
index 0000000..8a9e872
Binary files /dev/null and b/src/synthesis/doc/SySync_script_call_flow.numbers/document-thumbnail.tiff differ
diff --git a/src/synthesis/doc/SySync_script_call_flow.numbers/index.xml.gz b/src/synthesis/doc/SySync_script_call_flow.numbers/index.xml.gz
new file mode 100644 (file)
index 0000000..811fff4
Binary files /dev/null and b/src/synthesis/doc/SySync_script_call_flow.numbers/index.xml.gz differ
diff --git a/src/synthesis/doc/SySync_script_call_flow.pdf b/src/synthesis/doc/SySync_script_call_flow.pdf
new file mode 100644 (file)
index 0000000..af053fb
Binary files /dev/null and b/src/synthesis/doc/SySync_script_call_flow.pdf differ
index 863ce6a..fefaa2b 100755 (executable)
@@ -422,6 +422,18 @@ void TDB_Api_Blk::DisposeBlk()
 } // DisposeBlk
 
 
+// Returns true if <ps> is name or #<n>
+static bool BuiltIn( string &ps, int &n, cAppCharP name )
+{
+  if (strucmp( ps.c_str(), name      )==0)             return true;
+
+  string                   s= "#" + IntStr( n++ );
+  if (strucmp( ps.c_str(), s.c_str() )==0) { ps= name; return true; }
+
+  return false;
+} // BuiltIn
+
+
 
 /* --- connect to library ------------------------------------------------- */
 /*! These are the built-in linked libraries, which can be accessed directly
@@ -432,98 +444,94 @@ void TDB_Api_Blk::DisposeBlk()
  *  The SDK concept allows to have some routines unimplemented,
  *  they will be provided by the "no_dbapi" built-in module.
  */
-static TSyError DBApi_LibAssign( appPointer aMod, cAppCharP aName, appPointer aField, int aFSize,
+static TSyError DBApi_LibAssign( appPointer aMod, string &ps, appPointer aField, int aFSize,
                                                   cAppCharP aKey= "" )
 {
   TSyError err= DB_NotFound;
-
-  string        p= Plugin_MainName( aName );
-                p= NoBracks( p );
-  cAppCharP ps= p.c_str();
+  int n= 0; // incremental counter starting with 0
 
                        // the blind adapter is always available and acts as default as well
-  if        (strucmp( ps,""           )==0 ||
-             strucmp( ps,"no_dbapi"   )==0) err=    no_dbapi::AssignMethods( aMod, aField,aFSize, aKey );
+  if        (strucmp( ps.c_str(),   ""   )==0 ||
+             BuiltIn( ps,n, "no_dbapi"   )) err=    no_dbapi::AssignMethods( aMod, aField,aFSize, aKey );
 
   #ifdef DBAPI_DEMO    // demo (C) adapter, which will be delivered with SDK
-    else if (strucmp( ps,"SDK_demodb" )==0) err=  SDK_demodb::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "SDK_demodb" )) err=  SDK_demodb::AssignMethods( aMod, aField,aFSize, aKey );
   #endif
 
   #ifdef DBAPI_EXAMPLE // other (C++) linked versions of the demo adapter (for test)
-    else if (strucmp( ps,"example1"   )==0) err=    example1::AssignMethods( aMod, aField,aFSize, aKey );
-    else if (strucmp( ps,"example2"   )==0) err=    example2::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "example1"   )) err=    example1::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "example2"   )) err=    example2::AssignMethods( aMod, aField,aFSize, aKey );
   #endif
 
   #ifdef DBAPI_SILENT  // a silent adapter, which always says, everthing is ok
-    else if (strucmp( ps,"silent"     )==0) err=      silent::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "silent"     )) err=      silent::AssignMethods( aMod, aField,aFSize, aKey );
   #endif
 
   #ifdef DBAPI_TEXT    // "text_db" implementation, which emulates the "textdb"
-    else if (strucmp( ps,"SDK_textdb" )==0) err=  SDK_textdb::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "SDK_textdb" )) err=  SDK_textdb::AssignMethods( aMod, aField,aFSize, aKey );
   #endif
 
   #ifdef DBAPI_SNOWWHITE // "oceanblue/snowwhite" implementation
-    else if (strucmp( ps,"snowwhite"  )==0) err=   oceanblue::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "snowwhite"  )) err=   oceanblue::AssignMethods( aMod, aField,aFSize, aKey );
   #endif
 
   #ifdef PLUGIN_DLL    // plugin bridges
     #ifdef    JNI_SUPPORT  // the Java (JNI) adapter
-      else if (strucmp( ps,"JNI"      )==0) err=     SDK_jni::AssignMethods( aMod, aField,aFSize, aKey );
+      else if (BuiltIn( ps,n, "JNI"      )) err=     SDK_jni::AssignMethods( aMod, aField,aFSize, aKey );
     #endif
 
     #ifdef CSHARP_SUPPORT  // the C# adapter
-      else if (strucmp( ps,"CSHARP"   )==0) err=  SDK_csharp::AssignMethods( aMod, aField,aFSize, aKey );
+      else if (BuiltIn( ps,n, "CSHARP"   )) err=  SDK_csharp::AssignMethods( aMod, aField,aFSize, aKey );
     #endif
 
     #ifdef   DBAPI_TUNNEL  // direct access to internal adapters
-      else if (strucmp( ps,"tunnel"   )==0) err=  SDK_tunnel::AssignMethods( aMod, aField,aFSize, aKey );
+      else if (BuiltIn( ps,n, "tunnel"   )) err=  SDK_tunnel::AssignMethods( aMod, aField,aFSize, aKey );
     #endif
 
     #ifdef   DBAPI_LOGGER  // dbapi logger bridge
-      else if (strucmp( ps,"logger"   )==0) err=      logger::AssignMethods( aMod, aField,aFSize, aKey );
+      else if (BuiltIn( ps,n, "logger"   )) err=      logger::AssignMethods( aMod, aField,aFSize, aKey );
     #endif
   #endif
 
   #ifdef DBAPI_FILEOBJ     // dbapi fileobj
-    else if (strucmp( ps,"FILEOBJ"    )==0) err= SDK_fileobj::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "FILEOBJ"    )) err= SDK_fileobj::AssignMethods( aMod, aField,aFSize, aKey );
   #endif
 
   #ifdef ADAPTITEM_SUPPORT // "script-like" adapt item
-    else if (strucmp( ps,"ADAPTITEM"  )==0) err=   SDK_adapt::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "ADAPTITEM"  )) err=   SDK_adapt::AssignMethods( aMod, aField,aFSize, aKey );
   #endif
 
   #ifdef UIAPI_DEMO   // demo (C++) UI interface adapter, which will be delivered with SDK
-    else if (strucmp( ps,"SDK_ui"     )==0) err=      SDK_ui::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "SDK_ui"     )) err=      SDK_ui::AssignMethods( aMod, aField,aFSize, aKey );
   #endif
 
   #ifdef IPHONE_PLUGINS_STATIC   // iPhone OS does not allow DLL plugins at this time, so we must link them statically
     #ifdef HARDCODED_CONTACTS
-    else if (strucmp( ps,"iPhone_addressbook")==0) err= iPhone_addressbook::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "iPhone_addressbook")) err= iPhone_addressbook::AssignMethods( aMod, aField,aFSize, aKey );
     #endif
     #ifdef HARDCODED_CALENDAR
-    else if (strucmp( ps,"iPhone_calendar"   )==0) err=    iPhone_calendar::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "iPhone_calendar"   )) err=    iPhone_calendar::AssignMethods( aMod, aField,aFSize, aKey );
     #endif
     #ifdef HARDCODED_TODO
-    else if (strucmp( ps,"iPhone_todos"      )==0) err=       iPhone_todos::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "iPhone_todos"      )) err=       iPhone_todos::AssignMethods( aMod, aField,aFSize, aKey );
     #endif
     #ifdef HARDCODED_NOTES
-    else if (strucmp( ps,"iPhone_notes"      )==0) err=       iPhone_notes::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "iPhone_notes"      )) err=       iPhone_notes::AssignMethods( aMod, aField,aFSize, aKey );
     #endif
     #ifdef HARDCODED_EMAILS
-    else if (strucmp( ps,"iPhone_emails"     )==0) err=      iPhone_emails::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "iPhone_emails"     )) err=      iPhone_emails::AssignMethods( aMod, aField,aFSize, aKey );
     #endif
 
     #ifdef HARDCODED_CUSTOM
-    else if (strucmp( ps,"iPhone_db0" )==0) err=  iPhone_db0::AssignMethods( aMod, aField,aFSize, aKey );
-    else if (strucmp( ps,"iPhone_db1" )==0) err=  iPhone_db1::AssignMethods( aMod, aField,aFSize, aKey );
-    else if (strucmp( ps,"iPhone_db2" )==0) err=  iPhone_db2::AssignMethods( aMod, aField,aFSize, aKey );
-    else if (strucmp( ps,"iPhone_db3" )==0) err=  iPhone_db3::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "iPhone_db0" )) err=  iPhone_db0::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "iPhone_db1" )) err=  iPhone_db1::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "iPhone_db2" )) err=  iPhone_db2::AssignMethods( aMod, aField,aFSize, aKey );
+    else if (BuiltIn( ps,n, "iPhone_db3" )) err=  iPhone_db3::AssignMethods( aMod, aField,aFSize, aKey );
     #endif
   #endif
 
-
-
-  else ModuleConnectionError( NULL, aName );
+  else  if  (strucmp( ps.c_str(), "#" )==0) err= LOCERR_UNKSUBSYSTEM;
+  else  if (!BuiltIn( ps,n, "" )) ModuleConnectionError( NULL, AddBracks( ps ).c_str() );
 
   return err;
 } // DBApi_LibAssign
@@ -554,31 +562,33 @@ TDB_Api_Config::~TDB_Api_Config()
 
 static void connect_no_dbapi( appPointer &aMod, API_Methods &m )
 {
+  string no_dbapi= "";
+
   DisconnectModule( aMod    ); // avoid memory leak
   ConnectModule   ( aMod,"" ); // default: no db_api -> all methods return false
 //---- module --------------------------------
-  DBApi_LibAssign ( aMod,"", &m.start,        sizeof(m.start),        Plugin_Start );
-  DBApi_LibAssign ( aMod,"", &m.param,        sizeof(m.param),        Plugin_Param );
-  DBApi_LibAssign ( aMod,"", &m,              sizeof(m) );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.start,        sizeof(m.start),        Plugin_Start );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.param,        sizeof(m.param),        Plugin_Param );
+  DBApi_LibAssign ( aMod,no_dbapi, &m,              sizeof(m) );
 
 //---- session -------------------------------
-  DBApi_LibAssign ( aMod,"", &m.se,           sizeof(m.se),           Plugin_Session   );
-  DBApi_LibAssign ( aMod,"", &m.se.seAdapt,   sizeof(m.se.seAdapt),   Plugin_SE_Adapt  );
-  DBApi_LibAssign ( aMod,"", &m.se.seAuth,    sizeof(m.se.seAuth),    Plugin_SE_Auth   );
-  DBApi_LibAssign ( aMod,"", &m.se.dvAdmin,   sizeof(m.se.dvAdmin),   Plugin_DV_Admin  );
-  DBApi_LibAssign ( aMod,"", &m.se.dvTime,    sizeof(m.se.dvTime),    Plugin_DV_DBTime );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.se,           sizeof(m.se),           Plugin_Session     );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.se.seAdapt,   sizeof(m.se.seAdapt),   Plugin_SE_Adapt    );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.se.seAuth,    sizeof(m.se.seAuth),    Plugin_SE_Auth     );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.se.dvAdmin,   sizeof(m.se.dvAdmin),   Plugin_DV_Admin    );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.se.dvTime,    sizeof(m.se.dvTime),    Plugin_DV_DBTime   );
 
 //---- datastore -----------------------------
-  DBApi_LibAssign ( aMod,"", &m.ds,           sizeof(m.ds),           Plugin_Datastore   );
-  DBApi_LibAssign ( aMod,"", &m.ds.dsg,       sizeof(m.ds.dsg),       Plugin_DS_General  );
-  DBApi_LibAssign ( aMod,"", &m.ds.dsAdapt,   sizeof(m.ds.dsAdapt),   Plugin_DS_Adapt    );
-  DBApi_LibAssign ( aMod,"", &m.ds.dsAdmin,   sizeof(m.ds.dsAdmin),   Plugin_DS_Admin    );
-  DBApi_LibAssign ( aMod,"", &m.ds.dsData,    sizeof(m.ds.dsData),    Plugin_DS_Data     );
-  DBApi_LibAssign ( aMod,"", &m.ds.dsData.str,sizeof(m.ds.dsData.str),Plugin_DS_Data_Str );
-  DBApi_LibAssign ( aMod,"", &m.ds.dsData.key,sizeof(m.ds.dsData.key),Plugin_DS_Data_Key );
-  DBApi_LibAssign ( aMod,"", &m.ds.dsBlob,    sizeof(m.ds.dsBlob),    Plugin_DS_Blob     );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.ds,           sizeof(m.ds),           Plugin_Datastore   );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.ds.dsg,       sizeof(m.ds.dsg),       Plugin_DS_General  );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.ds.dsAdapt,   sizeof(m.ds.dsAdapt),   Plugin_DS_Adapt    );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.ds.dsAdmin,   sizeof(m.ds.dsAdmin),   Plugin_DS_Admin    );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.ds.dsData,    sizeof(m.ds.dsData),    Plugin_DS_Data     );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.ds.dsData.str,sizeof(m.ds.dsData.str),Plugin_DS_Data_Str );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.ds.dsData.key,sizeof(m.ds.dsData.key),Plugin_DS_Data_Key );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.ds.dsBlob,    sizeof(m.ds.dsBlob),    Plugin_DS_Blob     );
 //---- ui context ----------------------------
-  DBApi_LibAssign ( aMod,"", &m.ui,           sizeof(m.ui),           Plugin_UI );
+  DBApi_LibAssign ( aMod,no_dbapi, &m.ui,           sizeof(m.ui),           Plugin_UI );
   DisconnectModule( aMod    ); // no longer used, avoid memory leak
 } // connect_no_dbapi
 
@@ -649,15 +659,15 @@ TSyError TDB_Api_Config::DBApi_Assign( cAppCharP aItem, appPointer aField,
   TSyError err= LOCERR_OK;
 
   if (FlagOK( aItem,aKey )) {
-    if (is_lib) err= DBApi_LibAssign( fMod, fModName.c_str(), aField,aFieldSize, aKey );
+    if (is_lib) err= DBApi_LibAssign( fMod, fPlugin, aField,aFieldSize, aKey );
     else {      err= DB_Forbidden; // only allowed, if PLUGIN_DLL is active
       #ifdef PLUGIN_DLL
-                err= DBApi_DLLAssign( fMod,                   aField,aFieldSize, aKey, false );
+                err= DBApi_DLLAssign( fMod,          aField,aFieldSize, aKey, false );
       #endif
     } // if
 
     DEBUG_Exotic_INT( &fCB.Callback,MyDB, "DBApi_Assign", "aKey='%s' (size=%d) err=%d",
-                                                           aKey, aFieldSize, err );
+                                                           aKey, aFieldSize,   err );
   } // if
 
   return err;
@@ -756,6 +766,7 @@ TSyError TDB_Api_Config::Connect( cAppCharP aModName, CContext &globContext,
     NextToken      ( fOptions, fModMain, " " ); // separate <mOptions>
     fModName=                  fModMain;
                     CutBracks( fModMain );
+    fPlugin =                  fModMain;
 
     if (in_bracks) fModName = AddBracks( fModName );
 
@@ -792,8 +803,11 @@ TSyError TDB_Api_Config::Connect( cAppCharP aModName, CContext &globContext,
     fSDKversion= pv( 0 );      // get the plugin's version before making the first tests
     DEBUG_INT( mCB,MyDB, "Connect", "fSDKversion=%s", VersionStr( fSDKversion ).c_str() );
 
+    string sn= fModSub;
+    if    (sn=="[#]" && !(fPlugin=="logger")) sn= ""; // remove the recursion breaker
+
     CreateM_Func p=  (CreateM_Func)m.start.Module_CreateContext;
-        err=     p( &mContext, fModMain.c_str(),fModSub.c_str(), mContextName, mCB );
+        err=     p( &mContext, fModMain.c_str(), sn.c_str(), mContextName, mCB );
     if (err==LOCERR_ALREADY) err= LOCERR_OK; // this is not an error, just avoid multiple assignment
     DEBUG_INT( mCB,MyDB, "Connect", "mContext=%08X err=%d", mContext,err );
 
@@ -816,13 +830,14 @@ TSyError TDB_Api_Config::Connect( cAppCharP aModName, CContext &globContext,
   // !Supported( VP_EngineVersionParam ): only JNI signature changes
   // !Supported( VP_MD5_Nonce_IN       ): only JNI signature changes
 
-    cAppCharP                           vda= Plugin_DS_Admin;
-    if (!Supported( VP_InsertMapItem )) vda= Plugin_DS_Admin_OLD;
-    cAppCharP                           vdd= Plugin_DS_Data;
-    if (!Supported( VP_FLI_DSS       )) vdd= Plugin_DS_Data_OLD2;
-    if (!Supported( VP_ResumeToken   )) vdd= Plugin_DS_Data_OLD1;
-    cAppCharP                           vdb= Plugin_DS_Blob;
-    if (!Supported( VP_DeleteBlob    )) vdb= Plugin_DS_Blob_OLD;
+    cAppCharP                             vda= Plugin_DS_Admin;
+    if (!Supported( VP_InsertMapItem   )) vda= Plugin_DS_Admin_OLD;
+    cAppCharP                             vdd= Plugin_DS_Data;
+    if (!Supported( VP_FLI_DSS         )) vdd= Plugin_DS_Data_OLD2;
+    if (!Supported( VP_ResumeToken     )) vdd= Plugin_DS_Data_OLD1;
+    cAppCharP                             vdb= Plugin_DS_Blob;
+    if (!Supported( VP_BLOB_JSignature )) vdb= Plugin_DS_Blob_OLD2; // new BLOB signature
+    if (!Supported( VP_DeleteBlob      )) vdb= Plugin_DS_Blob_OLD1;
 
   //---- module ---------------------------------
     if (!err) err=     DBApi_Assign( "", &m.param,        sizeof(m.param),        Plugin_Param );
@@ -948,12 +963,13 @@ TSyError TDB_Api_Config::PluginParams( cAppCharP mConfigParams )
 {
   typedef TSyError     (*PlugProc)( CContext mContext,
                                    cAppCharP mConfigParams,  CVersion engineVersion );
-  typedef TSyError (*OLD_PlugProc)( CContext mContext,
-                                   cAppCharP mConfigParams ); // w/o <engineVersion>
+//typedef TSyError (*OLD_PlugProc)( CContext mContext,
+//                                 cAppCharP mConfigParams ); // w/o <engineVersion>
 
   TSyError err;
   if (!fConnected) return DB_Error;
 
+  /*
   // new param supported for Plugin Version >= 1.0.X.4
   if (Supported( VP_EngineVersionParam )) {
     PlugProc     p=     (PlugProc)m.param.Module_PluginParams;
@@ -963,7 +979,10 @@ TSyError TDB_Api_Config::PluginParams( cAppCharP mConfigParams )
     OLD_PlugProc p= (OLD_PlugProc)m.param.Module_PluginParams; // w/o the SDK version parameter
     err=         p( mContext, mConfigParams );
   } // if
+  */
 
+  PlugProc    p= (PlugProc)m.param.Module_PluginParams;
+         err= p( mContext, mConfigParams, EngineSDKVersion() );
   if    (err==LOCERR_ALREADY) err= LOCERR_OK;
   return err;
 } // PluginParams
index 3cf00d2..e2c8b44 100755 (executable)
@@ -218,8 +218,11 @@ class TDB_Api_Config
     bool             is_lib; // flag: as internal library
     TDB_Api_Callback fCB;    // Callback wrapper
 
-    cAppCharP ModName()    { return fModName.c_str(); } // the <moduleName>
-    cAppCharP ModOptions() { return fOptions.c_str(); } // the module's parameters
+    cAppCharP ModName()     { return fModName.c_str(); } // the <moduleName>
+    cAppCharP ModOptions()  { return fOptions.c_str(); } // the module's parameters
+    cAppCharP ModPlugin()   { return fPlugin.c_str();  } // the main module's name
+    cAppCharP ModMainName() { return fModMain.c_str(); } // the main module's name (with optional extension ":..."
+    cAppCharP ModSubName()  { return fModSub.c_str();  } // the sub  module's name (with params)
 
     void DisposeStr( TDB_Api_Str &s );
   private:
@@ -236,7 +239,8 @@ class TDB_Api_Config
 
     string             fModName;     // local copy of <aModName>
     string             fOptions;     // local copy of module's paramters
-    string             fModMain;     // main part  of <aModName> w/o brackets
+    string             fPlugin;      // main part  of <aModName> w/o brackets  w/o extension
+    string             fModMain;     // main part  of <aModName> w/o brackets, with optional extension
     string             fModSub;      // sub  part  of <aModName> w/o brackets
     string             fDesc;        // module's description
 
index d5cc0c0..24c0327 100755 (executable)
@@ -42,8 +42,7 @@ static TSyError AssignMethods( appPointer aMod, appPointer aField, memSize aFiel
                       NULL );
   } // if
 
-  if (strcmp( aKey,Plugin_Param     )==0 || // new AND old
-      strcmp( aKey,Plugin_Param_OLD )==0) {
+  if (strcmp( aKey,Plugin_Param )==0) {
     return ConnectFunctions( aMod, aField,aFieldSize, false,
               // ---- plugin params
                       Module_PluginParams,
@@ -209,9 +208,10 @@ static TSyError AssignMethods( appPointer aMod, appPointer aField, memSize aFiel
     #endif
   #endif
 
-  #if !defined DISABLE_PLUGIN_DATASTOREADMIN || !defined DISABLE_PLUGIN_DATASTOREDATA
-    if (strcmp( aKey,Plugin_DS_Blob     )==0 || // new AND old
-        strcmp( aKey,Plugin_DS_Blob_OLD )==0) {
+  #if !defined DISABLE_PLUGIN_DATASTOREADMIN  || !defined DISABLE_PLUGIN_DATASTOREDATA
+    if (strcmp( aKey,Plugin_DS_Blob      )==0 || // new AND old
+        strcmp( aKey,Plugin_DS_Blob_OLD1 )==0 ||
+        strcmp( aKey,Plugin_DS_Blob_OLD2 )==0) {
       return ConnectFunctions( aMod, aField,aFieldSize, false,
               // ---- BLOBs ----
                       ReadBlob,
index 489352e..160be1a 100755 (executable)
@@ -105,7 +105,7 @@ TSyError DBApi_DLLAssign( appPointer aMod, appPointer aField, memSize aFieldSize
              NULL );
   } // if
 
-
+  /*
   // compatibility to older version ( w/o <engineVersion> )
   keyOld= strcmp( aKey.c_str(),Plugin_Param_OLD )==0;
   keyCur= strcmp( aKey.c_str(),Plugin_Param     )==0;
@@ -114,6 +114,11 @@ TSyError DBApi_DLLAssign( appPointer aMod, appPointer aField, memSize aFieldSize
     // additional param for newer version
     if (keyCur) js1= j.SgnS_X( jt + "I" ); // "(ILjava/lang/String;I)S"
     else        js1=           jsT;        // "(ILjava/lang/String;)S"
+  */
+
+  if (strcmp( aKey.c_str(),Plugin_Param )==0) {
+    // additional param for newer version
+    js1= j.SgnS_X( jt + "I" ); // "(ILjava/lang/String;I)S"
 
     return ConnectFunctions( aMod, aField,aFieldSize, true,
           // ---- start of plugin connection
@@ -320,22 +325,26 @@ TSyError DBApi_DLLAssign( appPointer aMod, appPointer aField, memSize aFieldSize
              NULL );
   } // if
 
-  keyOld= strcmp( aKey.c_str(),Plugin_DS_Blob_OLD  )==0;
-  keyCur= strcmp( aKey.c_str(),Plugin_DS_Blob      )==0;
+  keyOld = strcmp( aKey.c_str(),Plugin_DS_Blob_OLD1 )==0;
+  keyOld2= strcmp( aKey.c_str(),Plugin_DS_Blob_OLD2 )==0;
+  keyCur = strcmp( aKey.c_str(),Plugin_DS_Blob      )==0;
+
+  if (keyCur || keyOld || keyOld2) {
+    cAppCharP    proc_delB= "";
+    if  (keyCur) proc_delB= Da_DB;
 
-  if (keyCur || keyOld) {
-    const char* proc_delB= "";
-    if (keyCur) proc_delB= Da_DB;
+            string bvsz= j.fvr; string bsz= j.fr;
+    if (!keyCur) { bvsz=   jvi;        bsz=  "I"; }
 
-    js1 = j.SgnS_X( jii                                     // "(ILItemID;Ljava/lang/String;LVAR_byteArray; ...
-                  + jt                                      // ... LVAR_int;LVAR_int;ZLVAR_boolean;)S"
+    js1 = j.SgnS_X( jii                                   // "(ILItemID;Ljava/lang/String;LVAR_byteArray; ...
+                  + jt                                    // ... LVAR_int;LVAR_int;ZLVAR_boolean;)S"
                   + LCP( jP, c_VAR_byteArray )
-                  + j.fvr
-                  + j.fvr + "Z"
+                  + bvsz
+                  + bvsz + "Z"
                   + LCP( jP, c_VAR_bool ) );
 
-    js2 = j.SgnS_X( jii + jt + "[B" + j.fr + j.fr + "ZZ" ); // "(ILItemID;Ljava/lang/String;[BIIZZ)S"
-    js3 = j.SgnS_X( jii + jt                             ); // "(ILItemID;Ljava/lang/String;)S"
+    js2 = j.SgnS_X( jii + jt + "[B" + bsz + bsz + "ZZ" ); // "(ILItemID;Ljava/lang/String;[BIIZZ)S"
+    js3 = j.SgnS_X( jii + jt                           ); // "(ILItemID;Ljava/lang/String;)S"
 
     return ConnectFunctions( aMod, aField,aFieldSize, true,
           // ---- datastore data ----
index 3d155b9..7c4cc95 100644 (file)
@@ -22,7 +22,7 @@ $(srcdir)/Makefile.am: $(srcdir)/Makefile.am.in $(srcdir)/gen-makefile-am.sh
 # zero. When fixing something without interface change, increment
 # REVISION.
 ENGINE_CURRENT = 3
-ENGINE_REVISION = 0
+ENGINE_REVISION = 1
 ENGINE_AGE = 3
 
 CLEANFILES =
index b02cf74..0a8afe6 100644 (file)
@@ -22,7 +22,7 @@ $(srcdir)/Makefile.am: $(srcdir)/Makefile.am.in $(srcdir)/gen-makefile-am.sh
 # zero. When fixing something without interface change, increment
 # REVISION.
 ENGINE_CURRENT = 3
-ENGINE_REVISION = 0
+ENGINE_REVISION = 1
 ENGINE_AGE = 3
 
 CLEANFILES =
index 84eaa74..1f11838 100644 (file)
@@ -488,7 +488,7 @@ top_srcdir = @top_srcdir@
 # zero. When fixing something without interface change, increment
 # REVISION.
 ENGINE_CURRENT = 3
-ENGINE_REVISION = 0
+ENGINE_REVISION = 1
 ENGINE_AGE = 3
 CLEANFILES = 
 EXTRA_DIST = $(am__append_1)
index bc27d49..629a2b5 100755 (executable)
@@ -16,7 +16,8 @@
   #define WIN32  // needed for others
 #endif
 
-// Release version status#undef RELEASE_VERSION
+// Release version status
+#undef RELEASE_VERSION
 #define RELEASE_SYDEBUG 2 // extended DBG included
 //#define OPTIONAL_SYDEBUG 1
 
@@ -58,9 +59,6 @@
 #define DEFAULT_EMAILS_ONLYLAST true
 #define DEFAULT_EMAILS_DAYSBEFORE 10
 
-// Outlook specifics
-// =================
-
 
 // Eval limit options
 // ==================
@@ -73,7 +71,7 @@
 
 // Identification for update check and demo period
 #define SYSER_VARIANT_CODE SYSER_VARIANT_PRO
-#define SYSER_PRODUCT_CODE SYSER_PRODCODE_CLIENT_OUTLOOK_PRO
+#define SYSER_PRODUCT_CODE SYSER_PRODCODE_CLIENT_LIB_WIN32
 #define SYSER_EXTRA_ID SYSER_EXTRA_ID_PROTO
 
 
index 30d56d7..35d0321 100644 (file)
@@ -217,7 +217,7 @@ clientEngine:
 
 sysync_client_engine.so: $(OBJS)
        $(LD) -shared -Xlinker -soname=sysync_client_engine $(LDFLAGS) $(OBJS) $(LIBS)\
-       -o sysync_SDK/bin/Linux/sysync_client_engine.so
+        -o sysync_SDK/bin/Linux/sysync_client_engine.so
 
 ifeq ($(TARGET), sysync_client_engine.so)
 $(WDOP)/%.c.o:   $(WD)/%.c
index c7bce44..f603e86 100644 (file)
@@ -71,8 +71,8 @@
 #endif
 
 #ifndef SYSYNC_BUILDNUMBER
-#define SYSYNC_BUILDNUMBER          0
-#define SYSYNC_BUILDNUMBER_TXT     "0"
+#define SYSYNC_BUILDNUMBER          5
+#define SYSYNC_BUILDNUMBER_TXT     "5"
 #endif
 
 
index 70990d0..8e2f36b 100755 (executable)
@@ -55,6 +55,7 @@ bool getPlatformString(TPlatformStringID aStringID, string &aString)
   char buffer[bufsiz];
   buffer[0]=0; // terminate for safety
   string str;
+  string aSub;
   struct passwd *userInfoP=NULL;
 
   switch (aStringID) {
@@ -105,11 +106,15 @@ bool getPlatformString(TPlatformStringID aStringID, string &aString)
       // My specific subdirectory for storing my app data/prefs
       userInfoP = getpwuid(getuid());
       aString = userInfoP->pw_dir; // user home dir
+      aSub = APPDATA_SUBDIR;
       #ifdef ANDROID
-      aString += "/data/com.sysync/" APPDATA_SUBDIR; // application specific subdir for android
+      aString += "/data/com.sysync"; // application specific subdir for android
+      if (!aSub.empty()) aString+= "/"; // slash only if subdir is really there
       #else
-      aString += "/.sysync/" APPDATA_SUBDIR; // application specific subdir
+      aString += "/.sysync/"; // application specific subdir
+      // don't adapt it here to avoid potential problems on other platforms
       #endif
+      aString += aSub;
       break;
     #endif
     /*
index 6c6a31f..9224a58 100755 (executable)
@@ -108,6 +108,9 @@ SML_API int smlLibStrcmp(const char *pTarget, const char *pSource){
 SML_API int smlLibStrncmp(const char *pTarget, const char *pSource, int count){
   return strncmp((char *)pTarget, (char *)pSource, count);
 }
+SML_API int smlLibStrnCasecmp(const char *pTarget, const char *pSource, int count){
+  return strncasecmp((char *)pTarget, (char *)pSource, count);
+}
 SML_API String_t smlLibStrchr(const char *pString, char character){
   return strchr((char *)pString, character);
 }
index f3ed1ed..9aba120 100755 (executable)
@@ -86,6 +86,7 @@
   SML_API_DEF String_t  smlLibStrcat(const char *pTarget, const char *pSource) LIB_FUNC;
   SML_API_DEF int   smlLibStrcmp(const char *pTarget, const char *pSource) LIB_FUNC;
   SML_API_DEF int   smlLibStrncmp(const char *pTarget, const char *pSource, int count) LIB_FUNC;
+  SML_API_DEF int   smlLibStrnCasecmp(const char *pTarget, const char *pSource, int count) LIB_FUNC;
   SML_API_DEF String_t  smlLibStrchr(const char *pString, char character) LIB_FUNC;
   SML_API_DEF int   smlLibStrlen(const char *pString) LIB_FUNC;
 #endif
index f35b300..427bf25 100755 (executable)
@@ -629,7 +629,8 @@ xmlHTMLEntity(xmlScannerPrivPtr_t pScanner, MemPtr_t *begin, int *len)
         { "gt", '>' },
         { "lt", '<' },
         { "apos", '\'' },
-        { "quot", '"' }
+        { "quot", '"' },
+        { "#43", '+'}
     };
     MemPtr_t entity = pScanner->pos + 1;
     int i;
index da3741d..52493f6 100755 (executable)
@@ -179,7 +179,7 @@ SmlPcdataExtension_t getExtByName(String_t ns) {
   for (;dtd->ext != SML_EXT_LAST; dtd++) {
     const char *dtdname=dtd->name;
     if (!dtdname) continue; /* skip empty names (should not appear but better be on the safe side) */
-    if (dtd->ext==SML_EXT_UNDEFINED && smlLibStrncmp("SYNCML:SYNCML",ns,13)==0) {
+    if (dtd->ext==SML_EXT_UNDEFINED && smlLibStrnCasecmp("SYNCML:SYNCML",ns,13)==0) {
       // SyncML namespace is ok without checking version!
       ext = SML_EXT_UNDEFINED;
       break;
index 457b105..a643f6a 100755 (executable)
@@ -1651,6 +1651,8 @@ sInt32 TBinfileClientConfig::newProfile(const char *aProfileName, bool aSetDefau
   // Note: create target also for not available datastores
   TLocalDSList::iterator pos;
   for (pos=fDatastores.begin();pos!=fDatastores.end();pos++) {
+    if ((*pos)->isAbstractDatastore()) continue; // don't try to create targets for abstract datastores (like superdatastores)
+    // non-abstract datastores at this point are always binfile-based by definition (this is a binfileimplclient, and this is one of its datastores)
     TBinfileDSConfig *cfgP = static_cast<TBinfileDSConfig *>(*pos);
     cfgP->initTarget(target,profile.profileID,aSetDefaults ? NULL : "",aSetDefaults && DEFAULT_DATASTORES_ENABLED); // remote datastore names default to local ones, empty if not default
     // copy from template
@@ -2148,6 +2150,7 @@ sInt32 TBinfileClientConfig::findOrCreateTargetIndexByDBInfo(
     // does not exist yet, create it now
     TLocalDSList::iterator pos;
     for (pos=fDatastores.begin();pos!=fDatastores.end();pos++) {
+      if ((*pos)->isAbstractDatastore()) continue; // only non-abstract datastores are guaranteed binfileds and have a target
       TBinfileDSConfig *cfgP = static_cast<TBinfileDSConfig *>(*pos);
       if (
         cfgP->fLocalDBTypeID==aLocalDBTypeID &&
@@ -2631,7 +2634,7 @@ localstatus TBinfileImplClient::SelectProfile(uInt32 aProfileSelector, bool aAut
   // detect special tunnel session's selection
   bool tunnel = aProfileSelector==TUNNEL_PROFILE_ID;
        // select profile if active
-       if (fConfigP->fBinfilesActive) {  
+       if (fConfigP->fBinfilesActive) {
     if (tunnel) {
       aProfileSelector=DEFAULT_PROFILE_ID;
     }
index 188ac60..a5f1bc6 100755 (executable)
@@ -250,6 +250,8 @@ public:
   virtual TFieldMapArrayItem *newFieldMapArrayItem(TCustomDSConfig *aCustomDSConfig, TConfigElement *aParentElement)
     { return new TFieldMapArrayItem(aCustomDSConfig,aParentElement); };
   #endif
+  // returns false for datastores that are not abstract, i.e. have a backend implementation (=all stdlogicds derivates)
+  virtual bool isAbstractDatastore(void) { return false; }; // customimplds is the foundation for a implemented backend - so it is no longer abstract
 protected:
   // Add (probably datastore-specific) limits such as MaxSize and NoTruncate to types
   virtual void addTypeLimits(TLocalEngineDS *aLocalDatastoreP, TSyncSession *aSessionP);
index ac521a8..00f229b 100755 (executable)
@@ -559,6 +559,16 @@ void TDebugLoggerBase::DebugPrintf(uInt32 aDbgMask, cAppCharP aFormat, ...)
 } // TDebugLoggerBase::DebugVPrintf
 
 
+// open new Block without attribute list
+void TDebugLoggerBase::DebugOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed)
+{
+  // we need a format and debug not completely off
+  if (getMask() && aBlockName) {
+    DebugOpenBlock(aBlockName,aBlockTitle,aCollapsed,NULL);
+  }
+} // TDebugLoggerBase::DebugOpenBlock
+
+
 // open new Block with attribute list, printf style
 void TDebugLoggerBase::DebugOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed, cAppCharP aBlockFmt, ...)
 {
@@ -598,17 +608,6 @@ void TDebugLoggerBase::DebugOpenBlockCollapsed(cAppCharP aBlockName, cAppCharP a
 } // TDebugLoggerBase::DebugOpenBlockCollapsed
 
 
-// open new Block without attribute list
-void TDebugLoggerBase::DebugOpenBlock(cAppCharP aBlockName, cAppCharP aBlockTitle, bool aCollapsed)
-{
-  // we need a format and debug not completely off
-  if (getMask() && aBlockName) {
-    static va_list va;
-    DebugVOpenBlock(aBlockName,aBlockTitle,aCollapsed,NULL,va);
-  }
-} // TDebugLoggerBase::DebugOpenBlock
-
-
 // output text to debug channel
 void TDebugLoggerBase::DebugPuts(uInt32 aDbgMask, cAppCharP aText, stringSize aTextSize, bool aPreFormatted)
 {
index 6ae94ea..4a1b03b 100644 (file)
@@ -1858,8 +1858,8 @@ static TSyError internal_ConnectEngine(
       // - flag static
       engine->fCIisStatic= true;
       // - prepare callback and pass to engine
-      (*aCIP)->callbackVersion = aCallbackVersion;
-      engine->fCI = *aCIP;
+      (*aCIP)->callbackVersion = aCallbackVersion; // fill in the outside callback version
+      engine->fCI = *aCIP;                         // engine uses the structure provided by the uiapp
       // - connect the engine
       err = engine->Connect("", aPrgVersion, aDebugFlags);
     }
index 6a63fd7..4b61a79 100644 (file)
@@ -435,7 +435,7 @@ public:
     TLocalEngineDS *dsP = static_cast<TLocalEngineDS *>(aFuncContextP->getCallerContext());
     aTermP->setAsString(dsP->getRemoteDatastore()->getFullName());
   }; // func_RemoteDBName
-  
+
 
   #ifdef SYSYNC_CLIENT
 
@@ -952,7 +952,7 @@ bool TLocalDSConfig::localStartElement(const char *aElementName, const char **aA
       return fail("Missing 'name' attribute in 'alias'");
     fAliasNames.push_back(name);
     expectEmpty();
-       }    
+       }
   #endif
   else if (strucmp(aElementName,"maxitemspermessage")==0)
     expectUInt32(fMaxItemsPerMessage);
@@ -1410,6 +1410,84 @@ bool TLocalEngineDS::dsSetClientSyncParams(
   bool aFilterInclusive
 )
 {
+  // - set remote params
+  fRemoteDBPath=aRemoteDBPath;
+  AssignString(fDBUser,aDBUser);
+  AssignString(fDBPassword,aDBPassword);
+       // check for running under control of a superdatastore
+  // - aRemoteDBPath might contain a special prefix: "<super>remote", with "super" specifying the
+  //   name of a local superdatastore to run the sync with
+  string opts;
+       if (!fRemoteDBPath.empty() && fRemoteDBPath.at(0)=='<') {
+       // we have an option prefix
+    size_t pfxe = fRemoteDBPath.find('>', 1);
+    if (pfxe!=string::npos) {
+       // extract options
+       opts.assign(fRemoteDBPath, 1, pfxe-1);
+      // store remote path cleaned from options
+      fRemoteDBPath.erase(0,pfxe+1);
+    }
+  }
+  if (!opts.empty()) {
+         #ifdef SUPERDATASTORES
+       // For now, the only option withing angle brackets is the name of the superdatastore, so opts==superdatastorename
+               // - look for superdatastore having the specified name
+    TSuperDSConfig *superdscfgP = static_cast<TSuperDSConfig *>(getSession()->getSessionConfig()->getLocalDS(opts.c_str()));
+    if (superdscfgP && superdscfgP->isAbstractDatastore()) {
+       // see if we have an instance of this already
+      fAsSubDatastoreOf = static_cast<TSuperDataStore *>(getSession()->findLocalDataStore(superdscfgP));
+      if (fAsSubDatastoreOf) {
+       // that superdatastore already exists, just override client sync params with those already set
+       aSyncMode = fAsSubDatastoreOf->fSyncMode;
+       aSlowSync = fAsSubDatastoreOf->fSlowSync;
+        aRecordFilterQuery = fAsSubDatastoreOf->fRemoteRecordFilterQuery.c_str();
+      }
+      else {
+       // instantiate new superdatastore
+             fAsSubDatastoreOf = static_cast<TSuperDataStore *>(superdscfgP->newLocalDataStore(getSession()));
+        if (fAsSubDatastoreOf) {
+          fSessionP->fLocalDataStores.push_back(fAsSubDatastoreOf);
+          // configure it with the same parameters as the subdatastore
+          if (!fAsSubDatastoreOf->dsSetClientSyncParams(
+            aSyncMode,
+            aSlowSync,
+            fRemoteDBPath.c_str(), // already cleaned from <xxx> prefix
+            aDBUser,
+            aDBPassword,
+            aLocalPathExtension,
+            aRecordFilterQuery,
+            aFilterInclusive
+          ))
+               return false; // failed
+        }
+      }
+      if (fAsSubDatastoreOf) {
+       // find link config for this superdatastore
+        TSubDSLinkConfig *lcfgP = NULL;
+        TSubDSConfigList::iterator pos;
+        for(pos=superdscfgP->fSubDatastores.begin();pos!=superdscfgP->fSubDatastores.end();pos++) {
+               if ((*pos)->fLinkedDSConfigP==fDSConfigP) {
+               // this is the link
+            lcfgP = *pos;
+            break;
+          }
+        }
+        if (lcfgP) {
+          // now link into superdatastore
+          fAsSubDatastoreOf->addSubDatastoreLink(lcfgP,this);        
+        }
+        else {
+          PDEBUGPRINTFX(DBG_ERROR,("Warning: '%s' is not a subdatastore of '%s'", getName(), opts.c_str()));
+          return false; // failed        
+        }        
+      }
+    }
+    else {
+       PDEBUGPRINTFX(DBG_ERROR,("Warning: No superdatastore name '%s' exists -> can't run '%s' under superdatastore control", opts.c_str(), getName()));
+      return false; // failed
+    }
+    #endif // SUPERDATASTORES
+  }
   // sync mode
   fSyncMode=aSyncMode;
   fSlowSync=aSlowSync;
@@ -1425,15 +1503,11 @@ bool TLocalEngineDS::dsSetClientSyncParams(
     fLocalDBPath+='/';
     fLocalDBPath+=aLocalPathExtension;
   }
-  // - set remote params
-  fRemoteDBPath=aRemoteDBPath;
-  AssignString(fDBUser,aDBUser);
-  AssignString(fDBPassword,aDBPassword);
   // - we have the params for syncing now
   return changeState(dssta_clientparamset)==LOCERR_OK;
 } // TLocalEngineDS::dsSetClientSyncParams
 
-#endif
+#endif // SYSYNC_CLIENT
 
 
 
@@ -1736,6 +1810,7 @@ const char *TLocalEngineDS::parseFilterCGI(cAppCharP aCGI, TSyncItemType *aItemT
       logop=0; // now consumed
       paraNest++;
       aFilter+='(';
+      p++;
     }
     else {
       // must be term: ident op val
@@ -1867,6 +1942,7 @@ const char *TLocalEngineDS::parseFilterCGI(cAppCharP aCGI, TSyncItemType *aItemT
           // this is not an error but only means end of filter expression
           goto endFilter;
         }
+        aFilter+=')';
         paraNest--;
         p++;
       } while (true);
@@ -2268,8 +2344,10 @@ TAlertCommand *TLocalEngineDS::engProcessSyncAlert(
       fSessionP->getReadOnly() || // session level read-only flag (probably set by login)
       fDSConfigP->fReadOnly; // or datastore config
     #ifdef SUPERDATASTORES
-    // check if not already alerted as subdatastore
-    if (fAsSubDatastoreOf) {
+    // if running as subdatastore of a superdatastore already, this call mus be from a superdatastore as well (aAsSubDatastoreOf!=NULL)
+    // Note: On a client, fAsSubDatastoreOf is set earlier in dsSetClientSyncParams()
+    //       On a server, fAsSubDatastoreOf will be set now to avoid alerting as sub- and normal datastore at the same time.
+    if (fAsSubDatastoreOf && !aAsSubDatastoreOf) {
       // bad, cannot be alerted directly AND as subdatastore
       aStatusCommand.setStatusCode(400);
       ADDDEBUGITEM(aStatusCommand,"trying to alert already alerted subdatastore");
@@ -2277,7 +2355,7 @@ TAlertCommand *TLocalEngineDS::engProcessSyncAlert(
       return NULL;
     }
     // set subdatastore mode
-    fAsSubDatastoreOf=aAsSubDatastoreOf;
+    fAsSubDatastoreOf = aAsSubDatastoreOf;
     #endif
     // reset type info
     fLocalSendToRemoteTypeP = NULL;
@@ -2607,22 +2685,6 @@ TAlertCommand *TLocalEngineDS::engProcessSyncAlert(
           fSyncMode
         );
         #endif // PROGRESS_EVENTS
-        //%%% To make client-side filtering work, determining send/receive types must be done
-        //    before loading the sync set.
-        //             Therefore these two init steps are now in the new engInitForClientSync() routine, which is now called
-        //    in syncclient.cpp immediately before starting to generate sync commands.
-        //    (Alternatively, engInitForSyncOps() could be placed here before switching to dssta_dataaccessstarted.
-        //    Tried that, works, but has the disadvantage that in case server sends devInf after answering alerts,
-        //    type resolution would fail or be forced to blind flight)
-        /*
-        // - prepare engine for sync (determining types)
-        // - let local datastore (derived DB-specific class) prepare for sync
-        sta = changeState(dssta_dataaccessstarted);
-        if (sta==LOCERR_OK && isStarted(false)) {
-          // already started now, change state
-          sta = changeState(dssta_syncsetready);
-        }
-        */
       } // client Case
       #endif // SYSYNC_CLIENT
     }
@@ -2838,7 +2900,7 @@ localstatus TLocalEngineDS::engInitForSyncOps(
   }
   #ifndef NO_REMOTE_RULES
   // check if rule match type will override what we found so far
-  if (fSessionP->fAppliedRemoteRuleP) {
+  if (!fSessionP->fActiveRemoteRules.empty()) {
     // have a look at our rulematch types
     TRuleMatchTypesContainer::iterator pos;
     TSyncItemType *ruleMatchTypeP = NULL;
@@ -2858,11 +2920,15 @@ localstatus TLocalEngineDS::engInitForSyncOps(
           n=strlen(p);
           e=p+n;
         }
-        // compare
-        if (strwildcmp(fSessionP->fAppliedRemoteRuleP->getName(), p, 0, n)==0) {
-          ruleMatchTypeP=(*pos).itemTypeP; // get the matching type
-          break;
+        // see if that matches with any of the active rules
+        TRemoteRulesList::iterator apos;
+        for(apos=fSessionP->fActiveRemoteRules.begin();apos!=fSessionP->fActiveRemoteRules.end();apos++) {
+          if (strwildcmp((*apos)->getName(), p, 0, n)==0) {
+            ruleMatchTypeP=(*pos).itemTypeP; // get the matching type
+            break;
+          }
         }
+        if (ruleMatchTypeP) break; // found a rule match type
         // test next match target
         p=e;
       }
@@ -2891,8 +2957,7 @@ localstatus TLocalEngineDS::engInitForSyncOps(
         RemoteSendToLocalTypeP=remCorrTypeP;
         // Show that we are using ruleMatch type
         PDEBUGPRINTFX(DBG_DATA+DBG_HOT,(
-          "Remote rule '%s' overrides default type usage - forcing type '%s' for send and receive",
-          fSessionP->fAppliedRemoteRuleP->getName(),
+          "An active remote rule overrides default type usage - forcing type '%s' for send and receive",
           ruleMatchTypeP->getTypeConfig()->getName()
         ));
         // done
@@ -3273,20 +3338,25 @@ localstatus TLocalEngineDS::engInitSyncAnchors(
 #ifdef SYSYNC_CLIENT
 
 // initialize Sync alert for datastore according to Parameters set with dsSetClientSyncParams()
-localstatus TLocalEngineDS::engPrepareClientSyncAlert(TSuperDataStore *aAsSubDatastoreOf)
+localstatus TLocalEngineDS::engPrepareClientSyncAlert(void)
 {
-  localstatus sta;
-
   #ifdef SUPERDATASTORES
-  // check if not already alerted as subdatastore
-  if (fAsSubDatastoreOf) {
-    // bad, cannot be alerted directly AND as subdatastore
-    DEBUGPRINTFX(DBG_ERROR,("trying to prepare alert for already alerted subdatastore"));
-    return LOCERR_WRONGUSAGE;
-  }
-  // set subdatastore mode
-  fAsSubDatastoreOf=aAsSubDatastoreOf;
+  // no operation here if running under control of a superdatastore.
+  // superdatastore's engPrepareClientSyncAlert() will call engPrepareClientRealDSSyncAlert of all subdatastores at the right time
+  if (fAsSubDatastoreOf)
+       return LOCERR_OK;
   #endif
+  // this is a real datastore
+  return engPrepareClientDSForAlert();
+} // TLocalEngineDS::engPrepareClientSyncAlert
+
+
+
+// initialize Sync alert for datastore according to Parameters set with dsSetClientSyncParams()
+localstatus TLocalEngineDS::engPrepareClientDSForAlert(void)
+{
+  localstatus sta;
+
   #ifdef SCRIPT_SUPPORT
   // AlertPrepareScript to add filters and CGI
   // - rebuild early (before all of the other DS scripts in makeAdminReady caused by engInitSyncAnchors below!)
@@ -3302,7 +3372,7 @@ localstatus TLocalEngineDS::engPrepareClientSyncAlert(TSuperDataStore *aAsSubDat
   // - save the identifying name of the DB
   fIdentifyingDBName = fLocalDBPath;
   // - get information about last session out of database
-  sta=engInitSyncAnchors(
+  sta = engInitSyncAnchors(
     relativeURI(fLocalDBPath.c_str()),
     fRemoteDBPath.c_str()
   );
@@ -3317,10 +3387,10 @@ localstatus TLocalEngineDS::engPrepareClientSyncAlert(TSuperDataStore *aAsSubDat
   if (fResumeAlertCode!=0 && fSessionP->getSyncMLVersion()>=syncml_vers_1_2) {
     // we have a suspended session, try to resume
     PDEBUGPRINTFX(DBG_PROTO,("Found suspended session with Alert Code = %hd",fResumeAlertCode));
-    fResuming=true;
+    fResuming = true;
   }
   return LOCERR_OK; // ok
-} // TLocalEngineDS::engPrepareClientSyncAlert
+} // TLocalEngineDS::engPrepareClientDSForAlert
 
 
 // generate Sync alert for datastore after initialisation with engPrepareClientSyncAlert()
@@ -3332,7 +3402,7 @@ localstatus TLocalEngineDS::engGenerateClientSyncAlert(
 {
   aAlertCommandP=NULL;
   #ifdef SUPERDATASTORES
-  if (fAsSubDatastoreOf) return LOCERR_OK; // NOP, ok
+  if (fAsSubDatastoreOf) return LOCERR_OK; // NOP, ok, only superdatastore creates an alert!
   #endif
 
   PDEBUGPRINTFX(DBG_PROTO,(
@@ -3440,8 +3510,23 @@ localstatus TLocalEngineDS::engGenerateClientSyncAlert(
 // - determine types to exchange
 // - make sync set ready
 localstatus TLocalEngineDS::engInitForClientSync(void)
+{  
+  #ifdef SUPERDATASTORES
+  // no init in case we are under control of a superdatastore -> the superdatastore will do that
+  if (fAsSubDatastoreOf)
+       return LOCERR_OK;
+  #endif
+  return engInitDSForClientSync();
+} // TLocalEngineDS::engInitForClientSync
+
+
+  
+// Init engine for client sync
+// - determine types to exchange
+// - make sync set ready
+localstatus TLocalEngineDS::engInitDSForClientSync(void)
 {
-  // - prepare engine for sync (determining types)
+       // make ready for syncops
   localstatus sta = engInitForSyncOps(getRemoteDBPath());
   if (sta==LOCERR_OK) {
     // - let local datastore (derived DB-specific class) prepare for sync
@@ -3452,7 +3537,7 @@ localstatus TLocalEngineDS::engInitForClientSync(void)
     }
   }
   return sta;
-} // TLocalEngineDS::engInitForClientSync
+} // TLocalEngineDS::engInitDSForClientSync
 
 
 #endif // Client
@@ -5875,23 +5960,24 @@ bool TLocalEngineDS::engProcessRemoteItemAsServer(
 /// @note superdatastore does it itself to have correct order of things happening
 void TLocalEngineDS::engRequestEnded(void)
 {
-  // variant for independent non-super/non-sub datastore
-  if (!fAsSubDatastoreOf) {
-    // If DS 1.2: Make sure everything is ready for a resume in case there's an abort (implicit Suspend)
-    // before the next request. Note that the we cannot wait for session timeout, as the resume attempt
-    // from the client probably arrives much earlier.
-    // Note: It is ESSENTIAL not to save the state until sync set is ready, because saving state will
-    //   cause DB access, and DB access is not permitted while sync set is possibly still loading
-    //   (possibly in a separate thread!). So dssta_syncmodestable (as in <=3.0.0.2) is NOT enough here!
-    if (testState(dssta_syncsetready)) {
-      // make sure all unsent items are marked for resume
-      engSaveSuspendState(false); // only if not already aborted
-    }
-    // let datastore prepare for end of request
-    dsRequestEnded();
-    // and let it prepare for end of this thread as well
-    dsThreadMayChangeNow();
+  #ifdef SUPERDATASTORES
+  if (fAsSubDatastoreOf)
+       return;
+  #endif
+  // If DS 1.2: Make sure everything is ready for a resume in case there's an abort (implicit Suspend)
+  // before the next request. Note that the we cannot wait for session timeout, as the resume attempt
+  // from the client probably arrives much earlier.
+  // Note: It is ESSENTIAL not to save the state until sync set is ready, because saving state will
+  //   cause DB access, and DB access is not permitted while sync set is possibly still loading
+  //   (possibly in a separate thread!). So dssta_syncmodestable (as in <=3.0.0.2) is NOT enough here!
+  if (testState(dssta_syncsetready)) {
+    // make sure all unsent items are marked for resume
+    engSaveSuspendState(false); // only if not already aborted
   }
+  // let datastore prepare for end of request
+  dsRequestEnded();
+  // and let it prepare for end of this thread as well
+  dsThreadMayChangeNow();
 } // TLocalEngineDS::engRequestEnded
 
 #endif // SYSYNC_SERVER
@@ -6105,7 +6191,7 @@ bool TLocalEngineDS::engProcessRemoteItemAsClient(
     }
     else {
       // if the DB has a error string to show, add it here
-      aStatusCommand.addItemString(lastDBErrorText().c_str());    
+      aStatusCommand.addItemString(lastDBErrorText().c_str());
     }
     return ok;
   }
@@ -6313,14 +6399,15 @@ bool TLocalEngineDS::engGenerateMapItems(TMapCommand *aMapCommandP, cAppCharP aL
   do {
     // check if already done
     if (pos==fPendingAddMaps.end()) break; // done
-    // add item
+    // get ID
     string locID = (*pos).first;
     dsFinalizeLocalID(locID); // make sure we have the permanent version in case datastore implementation did deliver temp IDs
-    // add local ID prefix, if any
-    if (aLocalIDPrefix && *aLocalIDPrefix)
-      locID.insert(0,aLocalIDPrefix);
+    // create prefixed version of ID
+    string prefixedLocID;
+    AssignString(prefixedLocID, aLocalIDPrefix); // init with prefix (if any)
+    prefixedLocID += locID; // append local ID
     // add it to map command
-    aMapCommandP->addMapItem(locID.c_str(),(*pos).second.c_str());
+    aMapCommandP->addMapItem(prefixedLocID.c_str(),(*pos).second.c_str());
     // check if we could send this command
     #ifdef USE_SML_EVALUATION
     if (
@@ -6337,10 +6424,10 @@ bool TLocalEngineDS::engGenerateMapItems(TMapCommand *aMapCommandP, cAppCharP aL
       // yes, it should work
       PDEBUGPRINTFX(DBG_PROTO,(
         "Mapitem generated: localID='%s', remoteID='%s'",
-        locID.c_str(),
+        prefixedLocID.c_str(),
         (*pos).second.c_str()
       ));
-      // move sent ones to unconfirmed list
+      // move sent ones to unconfirmed list (Note: use real locID, without prefix!)
       fUnconfirmedMaps[locID]=(*pos).second;
       // remove item from to-be-sent list
       TStringToStringMap::iterator temp_pos = pos++; // make copy and set iterator to next
index 799b2cb..f15c79d 100755 (executable)
@@ -207,6 +207,8 @@ public:
   virtual void clear();
   // - check for alias names
        uInt16 isDatastoreAlias(cAppCharP aDatastoreURI);
+  // - returns true for datastores that are abstract, i.e. don't have a backend implementation (like superdatastores, or non-derived localengineds)
+  virtual bool isAbstractDatastore(void) { return true; }; // pure localengineds is abstract. First derivate towards backend (stdlogicds) will override this with "false". 
 protected:
   // check config elements
   #ifndef HARDCODED_CONFIG
@@ -665,15 +667,19 @@ public:
   #ifdef SYSYNC_CLIENT
   /// initialize Sync alert for datastore according to Parameters set with dsSetClientSyncParams()
   /// @note initializes anchors and makes calls to isFirstTimeSync() valid
-  localstatus engPrepareClientSyncAlert(TSuperDataStore *aAsSubDatastoreOf);
+  SUPERDS_VIRTUAL localstatus engPrepareClientSyncAlert(void);
+  /// internal helper to be called by engPrepareClientSyncAlert() from this class and from superdatastore
+  localstatus engPrepareClientDSForAlert(void);
   /// generate Sync alert for datastore
   /// @note this could be repeatedly called due to auth failures at beginning of session
   /// @note this is a NOP for subDatastores (should not be called in this case, anyway)
   localstatus engGenerateClientSyncAlert(TAlertCommand *&aAlertCommandP);
-  // Init engine for client sync
+  // Init engine for client sync (superdatastore aware)
   // - determine types to exchange
   // - make sync set ready
-  localstatus engInitForClientSync(void);
+  SUPERDS_VIRTUAL localstatus engInitForClientSync(void);
+  // - non-superdatastore aware base functionality
+       localstatus engInitDSForClientSync(void);
   #endif
   /// Internal events during sync for derived classes
   /// @note local DB authorisation must be established already before calling these
index c01d0d0..6a42191 100644 (file)
@@ -1235,7 +1235,7 @@ TMimeDirProfileHandler::TMimeDirProfileHandler(
   fDoNotFoldContent = false; // standard requires folding
   fTreatRemoteTimeAsLocal = false; // only for broken implementations
   fTreatRemoteTimeAsUTC = false; // only for broken implementations
-  fAppliedRemoteRuleP = NULL; // no dependency
+  fActiveRemoteRules.clear(); // no dependency on certain remote rules
 } // TMimeDirProfileHandler::TMimeDirProfileHandler
 
 
@@ -3018,7 +3018,7 @@ void TMimeDirProfileHandler::generateLevels(
             // - just remember
             otherRulePropP=propP;
           }
-          else if (propP->ruleDependency==fAppliedRemoteRuleP) {
+          else if (isActiveRule(propP->ruleDependency)) {
             // specific for the applied rule
             expandPropP=propP; // default to expand current prop
             // now we have expanded a rule-specific property (blocks expanding of "other"-rule dependent prop)
@@ -4258,7 +4258,7 @@ bool TMimeDirProfileHandler::parseLevels(
                 // - just remember for now
                 otherRulePropP=propP;
               }
-              else if (propP->ruleDependency==fAppliedRemoteRuleP) {
+              else if (isActiveRule(propP->ruleDependency)) {
                 // specific for the applied rule
                 parsePropP=propP; // default to expand current prop
                 // now we have expanded a rule-specific property (blocks parsing of "other"-rule dependent prop)
@@ -4433,12 +4433,9 @@ void TMimeDirProfileHandler::getOptionsFromDatastore(void)
     fDoNotFoldContent = fRelatedDatastoreP->getSession()->fDoNotFoldContent;
     fTreatRemoteTimeAsLocal = fRelatedDatastoreP->getSession()->fTreatRemoteTimeAsLocal;
     fTreatRemoteTimeAsUTC = fRelatedDatastoreP->getSession()->fTreatRemoteTimeAsUTC;
-               fAppliedRemoteRuleP =
-                 #ifndef NO_REMOTE_RULES
-       fRelatedDatastoreP->getSession()->fAppliedRemoteRuleP;
-      #else
-      NULL;
-      #endif
+    #ifndef NO_REMOTE_RULES
+               fActiveRemoteRules = fRelatedDatastoreP->getSession()->fActiveRemoteRules; // copy the list
+    #endif
   }
 }
 
@@ -5107,18 +5104,35 @@ void TMimeDirProfileHandler::setProfileMode(sInt32 aMode)
 
 
 #ifndef NO_REMOTE_RULES
+
 void TMimeDirProfileHandler::setRemoteRule(const string &aRemoteRuleName)
 {
   TSessionConfig *scP = getSession()->getSessionConfig();
   TRemoteRulesList::iterator pos;
   for(pos=scP->fRemoteRulesList.begin();pos!=scP->fRemoteRulesList.end();pos++) {
     if((*pos)->fElementName == aRemoteRuleName) {
-      fAppliedRemoteRuleP = *pos;
+       // only this rule must be active
+      fActiveRemoteRules.clear();
+      fActiveRemoteRules.push_back(*pos);
       break;
     }
   }
 } // TMimeDirProfileHandler::setRemoteRule
-#endif
+
+
+// check if given rule (by name, or if aRuleName=NULL by rule pointer) is active
+bool TMimeDirProfileHandler::isActiveRule(TRemoteRuleConfig *aRuleP)
+{
+  TRemoteRulesList::iterator pos;
+  for(pos=fActiveRemoteRules.begin();pos!=fActiveRemoteRules.end();pos++) {
+       if ((*pos)==aRuleP)
+       return true;
+  }
+  // no match
+  return false;
+} // TMimeDirProfileHandler::isActiveRule
+
+#endif // NO_REMOTE_RULES
 
 
 // - check mode
index 999ab4b..c3be59a 100755 (executable)
@@ -500,8 +500,11 @@ private:
   // - time handling
   bool fTreatRemoteTimeAsLocal;
   bool fTreatRemoteTimeAsUTC;
-  // - dependency on certain remote rule
-  TRemoteRuleConfig *fAppliedRemoteRuleP;
+  #ifndef NO_REMOTE_RULES
+  // - dependency on certain remote rule(s)
+  TRemoteRulesList fActiveRemoteRules; // list of active remote rules that might influence behaviour
+       bool isActiveRule(TRemoteRuleConfig *aRuleP); // check if given rule is among the active ones
+  #endif
   // vars
   TMIMEProfileConfig *fProfileCfgP; // the MIME-DIR profile config element
   // property definitions
index 022f864..8d76bbc 100755 (executable)
@@ -1255,25 +1255,13 @@ void initRRuleExpansion(
 }
 
 
-// negative aAtLeastDays allowed only with aModulo==1 (because of undefined % operator for negatives - altough it is normally truncate towards zero = remainder)
-// - advances cursor by at least aAtLeastDays
-// - if advance is more than aNoModuloDays, advance will be one aModulo interval
-static void adjustCursor(TRRuleExpandStatus &es, lineardate_t aAtLeastDays, sInt16 aModulo=1, sInt16 aNoModuloDays=0)
+// advance cursor by given number of days
+static void adjustCursor(TRRuleExpandStatus &es, lineardate_t aDays)
 {
-       lineardate_t days = 0;
-  days = aAtLeastDays; // default to simply add days
-  if (aModulo>1) {
-    // we need to make sure remainder is 0 and advance extra
-    lineardate_t extradays = aAtLeastDays % aModulo;
-    // only if extradays exceeds given range (which usually represents the reach of the current mask, e.g. remaining days in the week)
-    if (extradays>aNoModuloDays) {
-      days += aModulo-extradays;
-    }
-  }
   // now days = number of days to advance
-  es.cursor += days*linearDateToTimeFactor;
+  es.cursor += aDays*linearDateToTimeFactor;
   // adjust weekday
-  es.cursorWDay += days % DaysPerWk;
+  es.cursorWDay += aDays % DaysPerWk;
   if (es.cursorWDay<0) es.cursorWDay += DaysPerWk;
   else if (es.cursorWDay>=DaysPerWk) es.cursorWDay -= DaysPerWk;    
   #ifdef SYNTHESIS_UNIT_TEST
@@ -1284,6 +1272,21 @@ static void adjustCursor(TRRuleExpandStatus &es, lineardate_t aAtLeastDays, sInt
   #endif
 }
 
+
+static lineardate_t makeMultiple(lineardate_t d, sInt16 aMultiple, sInt16 aMaxRemainder=0)
+{
+  if (aMultiple>1) {
+    // we need to make sure remainder is 0 and advance extra
+    lineardate_t r = d % aMultiple; // remainder
+    if (r>aMaxRemainder) {
+                       // round up to next multiple
+       d += aMultiple - r;
+    }
+  }
+       return d;       
+}
+
+
 // simple and fast cursor increment
 static void incCursor(TRRuleExpandStatus &es)
 {
@@ -1340,7 +1343,7 @@ lineartime_t getNextOccurrence(TRRuleExpandStatus &es)
        // Daily: can be calculated directly
     if (!es.started) {
        // calculate first occurrence
-       adjustCursor(es, es.expansionStartDayOffset, es.interval); // move cursor to first day
+       adjustCursor(es, makeMultiple(es.expansionStartDayOffset, es.interval)); // move cursor to first day
     }
     else {
       // calculate next occurrence
@@ -1356,11 +1359,14 @@ lineartime_t getNextOccurrence(TRRuleExpandStatus &es)
       if (es.firstmask==0)
         es.firstmask = 1<<es.cursorWDay; // set start day in mask
       // advance cursor to first day of expansion period
-      sInt16 woffs = es.cursorWDay-es.weekstart; // how many days back to next week start
-      if (woffs<0) woffs+=DaysPerWk; // we want to go BACK to next week start
-      adjustCursor(es, -woffs, 1); // back to previous start of week
-       adjustCursor(es, es.expansionStartDayOffset, es.interval*DaysPerWk, DaysPerWk-woffs-1); // advance by intervals when advance exeeds rest of week (covered by mask)
-      adjustCursor(es, woffs, 1); // forward again to start weekday
+      // - go back to start of very first week covered by the recurrence
+      sInt16 woffs = es.cursorWDay-es.weekstart; // how many days back to next week start from dtstart
+      if (woffs<0) woffs+=DaysPerWk; // we want to go BACK to previous week start
+      adjustCursor(es, -woffs); // back to previous start of week (=start of first week covered by recurrence)
+      lineardate_t d = es.expansionStartDayOffset+woffs; // days from beginning of first week to beginning day of expansion
+      // if we exceed reach of mask in current interval, round up to beginning of next interval
+      d = makeMultiple(d,es.interval*DaysPerWk,DaysPerWk-1); // expand into next interval if needed 
+       adjustCursor(es, d); // now advance cursor to 
            if (expansionEnd(es)) goto done; // could by beyond current expanding scope due to interval jump 
                }
     // calculate first/next occurrence (if not first occurrence, do not check initially but advance first)
index ee8770b..bacc6e7 100755 (executable)
@@ -871,16 +871,16 @@ public:
 
 
   // string REMOTERULENAME()
-  // returns name of applied remote rule, empty if none
+  // returns name of the LAST matched remote rule (or subrule), empty if none
+  // Note: this is legacy from 3.4.0.4 onwards, as we now have a list of multiple active rules 
   static void func_Remoterulename(TItemField *&aTermP, TScriptContext *aFuncContextP)
   {
     #ifndef NO_REMOTE_RULES
-    string r;
     if (aFuncContextP->getSession()) {
       // there is a session
-      if (aFuncContextP->getSession()->fAppliedRemoteRuleP) {
-        // there is a rule applied
-        aTermP->setAsString(aFuncContextP->getSession()->fAppliedRemoteRuleP->getName());
+      if (!aFuncContextP->getSession()->fActiveRemoteRules.empty()) {
+        // there is at least one rule applied, get the last one in the list
+        aTermP->setAsString(aFuncContextP->getSession()->fActiveRemoteRules.back()->getName());
         return;
       }
     }
@@ -890,6 +890,26 @@ public:
   }; // func_Remoterulename
 
 
+  // boolean ISACTIVERULE(string rulename)
+  // checks if given rule is currently activated 
+  static void func_isActiveRule(TItemField *&aTermP, TScriptContext *aFuncContextP)
+  {
+    #ifndef NO_REMOTE_RULES
+    string r;
+    aFuncContextP->getLocalVar(0)->getAsString(r);
+    if (aFuncContextP->getSession()) {
+      // there is a session, return status
+      aTermP->setAsBoolean(aFuncContextP->getSession()->isActiveRule(r.c_str()));
+      return;
+    }
+    #endif
+    // remote rules not supported or no session active
+    aTermP->assignEmpty();
+  }; // func_Remoterulename
+
+
+
+
   // TREATASLOCALTIME(integer flag)
   static void func_SetTreatAsLocaltime(TItemField *&aTermP, TScriptContext *aFuncContextP)
   {
@@ -2022,11 +2042,9 @@ public:
       if (profileHandlerP) {
        // now we can convert
         // - set the mode code (none = 0 = default)
-        // TODO? Shouldn't this check whether arg #2 was passed at all? If not,
-        // the code will segfault when trying to call getAsInteger()
         profileHandlerP->setProfileMode(aFuncContextP->getLocalVar(2)->getAsInteger());
         profileHandlerP->setRelatedDatastore(NULL); // no datastore in particular is related
-#ifndef NO_REMOTE_RULES
+                               #ifndef NO_REMOTE_RULES
         // - try to find remote rule
         TItemField *field = aFuncContextP->getLocalVar(3);
         if (field) {
@@ -2034,7 +2052,7 @@ public:
           if (!s.empty())
             profileHandlerP->setRemoteRule(s);
         }
-#endif
+                               #endif
         // - convert
                aFuncContextP->getLocalVar(0)->getAsString(s);
         ok = profileHandlerP->parseText(s.c_str(), s.size(), *itemP);
@@ -2163,6 +2181,7 @@ const TBuiltInFuncDef BuiltInFuncDefs[] = {
   { "SWAP", TBuiltinStdFuncs::func_Swap, fty_none, 2, param_swap },
   { "TYPENAME", TBuiltinStdFuncs::func_TypeName, fty_string, 1, param_oneVariant },
   { "REMOTERULENAME", TBuiltinStdFuncs::func_Remoterulename, fty_string, 0, NULL },
+  { "ISACTIVERULE", TBuiltinStdFuncs::func_isActiveRule, fty_integer, 1, param_oneString },
   { "LOCALURI", TBuiltinStdFuncs::func_LocalURI, fty_string, 0, NULL },
   { "NOW", TBuiltinStdFuncs::func_Now, fty_timestamp, 0, NULL },
   { "SYSTEMNOW", TBuiltinStdFuncs::func_SystemNow, fty_timestamp, 0, NULL },
index 2f1a389..bff1b07 100644 (file)
@@ -528,6 +528,10 @@ localstatus TStdLogicDS::startDataAccessForServer(void)
       &DBFuncTable, // context's function table
       this // datastore pointer needed for context
     );
+    // - datastoreinitscript might abort the sync with this datastore, check for that and exit if so
+    if (isAborted()) {
+       return getAbortStatusCode();
+    }
     #endif
     // - init post fetch filtering, sets fFilteringNeededForAll and fFilteringNeeded correctly
     initPostFetchFiltering();
@@ -914,6 +918,10 @@ localstatus TStdLogicDS::startDataAccessForClient(void)
     &DBFuncTable, // context's function table
     this // datastore pointer needed for context
   );
+  // - datastoreinitscript might abort the sync with this datastore, check for that and exit if so
+  if (isAborted()) {
+    return getAbortStatusCode();
+  }
   #endif
   // - init post fetch filtering, sets fFilteringNeededForAll and fFilteringNeeded correctly
   initPostFetchFiltering();
index 5d142ca..75828a8 100755 (executable)
@@ -113,7 +113,6 @@ public:
   virtual bool isStarted(bool aWait);
   /// @}
 
-
 protected:
   /// @name dsXXXX (usually abstract) virtuals defining the interface to derived datastore classes (implementation, api)
   ///   These are usually designed such that they should always call inherited::dsXXX to let the entire chain
index 7be72b0..58ee12a 100755 (executable)
@@ -185,33 +185,43 @@ TSuperDataStore::TSuperDataStore(TSuperDSConfig *aDSConfigP, TSyncSession *aSess
     SYSYNC_THROW(TSyncException(DEBUGTEXT("TSuperDataStore::TSuperDataStore called with NULL config","lds1")));
   // reset first
   InternalResetDataStore();
-  // create links to subdatastores
-  TSubDSConfigList::iterator pos;
-  TSubDatastoreLink link;
-  for(pos=aDSConfigP->fSubDatastores.begin();pos!=aDSConfigP->fSubDatastores.end();pos++) {
-    // start not yet pending
-    link.fStartPending=false;
-    // set link to subdatastore link config
-    link.fDSLinkConfigP=*pos;
-    // find actual datastore by "handle" (= config pointer)
-    link.fDatastoreLinkP=aSessionP->findLocalDataStore(link.fDSLinkConfigP->fLinkedDSConfigP);
-    // if actual datastore does not yet exist, create it now. This can be
-    // the case for clients, where datastores are only instantiated when
-    // directly addressed by a SyncRequest (which will not happen for
-    // subdatastores normally)
-    if (!link.fDatastoreLinkP) {
-      // add
-      link.fDatastoreLinkP=aSessionP->addLocalDataStore(link.fDSLinkConfigP->fLinkedDSConfigP);
+  // for server, all configured subdatastores are automatically linked in
+  // (for client, only those that are active will be linked in at dsSetClientParams() time)
+  if (IS_SERVER) {
+    // create links to subdatastores
+    TSubDSConfigList::iterator pos;
+    for(pos=aDSConfigP->fSubDatastores.begin();pos!=aDSConfigP->fSubDatastores.end();pos++) {
+               // add link
+      addSubDatastoreLink(*pos,NULL); // search datastore
     }
-    // save link
-    fSubDSLinks.push_back(link);
   }
-  // Important: We need to get the iterator now again, as the implicit
-  //   iterator init via InternalResetDataStore() is invalid because the list was empty then.
-  fCurrentGenDSPos=fSubDSLinks.begin();
 } // TSuperDataStore::TSuperDataStore
 
 
+void TSuperDataStore::addSubDatastoreLink(TSubDSLinkConfig *aDSLinkConfigP, TLocalEngineDS *aDatastoreP)
+{
+  TSubDatastoreLink link;
+  // start not yet pending
+  link.fStartPending = false;
+  // set link to subdatastore's config
+  link.fDSLinkConfigP = aDSLinkConfigP;
+  if (aDatastoreP)
+    link.fDatastoreLinkP = aDatastoreP; // we already know the datastore (for client, it might not yet be in session list of datastores)
+  else
+       link.fDatastoreLinkP = fSessionP->findLocalDataStore(link.fDSLinkConfigP->fLinkedDSConfigP); // find actual datastore by "handle" (= config pointer)
+  // make sure datastore is instantiated
+  if (!link.fDatastoreLinkP) {
+    // instantiate now (should not happen on server, as all datastores are instantiated on a server anyway)
+    link.fDatastoreLinkP=fSessionP->addLocalDataStore(link.fDSLinkConfigP->fLinkedDSConfigP);
+  }
+  // save link
+  fSubDSLinks.push_back(link); 
+  // Important: We need to get the iterator now again, in case the list was empty before
+  // (because the iterator set in InternalResetDataStore() is invalid because it was created for an empty list).
+  fCurrentGenDSPos=fSubDSLinks.begin();
+}
+
+
 void TSuperDataStore::InternalResetDataStore(void)
 {
   // init
@@ -292,14 +302,15 @@ TAlertCommand *TSuperDataStore::engProcessSyncAlert(
         substatus               // status that might be modified
       );
       if (subalertcmdP) {
-        // get rid of this, we don't need it
+        // get rid of this, we don't need it (server case only, client case does not generate an alert command here anyway)
         delete subalertcmdP;
       }
-      else {
+      // check if processing alert had a problem
+      if (substatus.getStatusCode()!=0) {
         // basic problem with one of the subdatastores
         // - propagate error code
         aStatusCommand.setStatusCode(substatus.getStatusCode());
-        // - cancel alert
+        // - no alert to send
         return NULL;
       }
       // this one is pending for start
@@ -777,17 +788,14 @@ void TSuperDataStore::engFinishDataStoreSync(localstatus aErrorStatus)
 // ----------------------------------
 
 
-// Abstracts of TLocalEngineDS
-// ----------------------------
-
 
 // called at sync alert (before generating for client, after receiving for server)
 // - obtains combined anchor from subdatastores
 // - combines them into a common anchor (if possible)
 // - updates fFirstTimeSync as well
 localstatus TSuperDataStore::engInitSyncAnchors(
-  cAppCharP aDatastoreURI,      // local datastore URI
-  cAppCharP aRemoteDBID         // ID of remote datastore (to find session information in local DB)
+  cAppCharP aDatastoreURI,      // (Note: unused in superdatastore) local datastore URI
+  cAppCharP aRemoteDBID         // (Note: unused in superdatastore) ID of remote datastore (to find session information in local DB)
 )
 {
   bool allanchorsequal=true;
@@ -798,20 +806,19 @@ localstatus TSuperDataStore::engInitSyncAnchors(
   TSubDSLinkList::iterator pos;
   for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
     if (pos==fSubDSLinks.begin()) {
-      /* not needed, because engInitSyncAnchors() will be called only after all subdatastore's
-         engProcessSyncAlert() was called, which in turn contains a call to engInitSyncAnchors()
-         This means we can safely assume we have the fLastRemoteAnchor/fNextLocalAnchor info
-         ready here.
-         In fact, calling this here AGAIN had the effect that the state of the first
-         subdatastore would be set BACK to dssta_adminready (instead of the required dssta_syncmodestable,
-         and in turn when the first sync command arrived, it would be rejected with "SYNC received too early".
-      // init anchors of subdatastore
-      sta = pos->fDatastoreLinkP->engInitSyncAnchors(aDatastoreURI, aRemoteDBID);
-      if (sta!=LOCERR_OK)
-        break; // exit, we cannot init
-      */
-      // assign references of first datastore
-      // - this must be same from all subdatastores
+       // Server case note:
+       //   Subdatastore's engInitSyncAnchors() MUST NOT be called here, because this routine will
+      //   always be called after all subdatastore's engProcessSyncAlert() was called
+      //   which in turn contains a call to engInitSyncAnchors().
+      // Client case note:
+       //   Subdatastore's engInitSyncAnchors() MUST NOT be called here, because this routine will
+      //   be called from TSuperDataStore::engPrepareClientSyncAlert() after iterating through
+      //   subdatastores and calling their engPrepareClientDSForAlert(), which in turn
+      //   contains a call to engInitSyncAnchors(). 
+      // This means we can safely assume we have the fLastRemoteAnchor/fNextLocalAnchor info
+      // ready here.
+      // - Assign references of first datastore
+                       //   Note: remote anchor must be same from all subdatastores
       fLastRemoteAnchor=pos->fDatastoreLinkP->fLastRemoteAnchor;
       // - these are used from the first datastore, and might differ (a few seconds,
       //   that is) for other datastores
@@ -1160,6 +1167,83 @@ bool TSuperDataStore::engGenerateSyncCommands(
 
 #ifdef SYSYNC_CLIENT
 
+// Client only: initialize Sync alert for datastore according to Parameters set with dsSetClientSyncParams()
+localstatus TSuperDataStore::engPrepareClientSyncAlert(void)
+{
+       localstatus sta;
+  
+  // not resuming by default
+       fResuming = false;
+  fResumeAlertCode = 0;
+       // prepare all subdatastores that were parametrized to participate in a sync by dsSetClientSyncParams()
+  TSubDSLinkList::iterator pos;
+  for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+       TLocalEngineDS *dsP = pos->fDatastoreLinkP;
+    if (dsP->testState(dssta_clientparamset)) {
+       // configured for sync, prepare for alert
+      sta = dsP->engPrepareClientDSForAlert();
+      if (sta!=LOCERR_OK)
+       return sta; // error
+      // collect slow sync status
+      fSlowSync = fSlowSync || dsP->fSlowSync;
+       // collect resume alert code
+      if (dsP->fResuming) {
+       // subdatastore would like to resume
+        if (fResumeAlertCode!=0 && fResumeAlertCode!=dsP->fResumeAlertCode) {
+               // different idea about what to resume -> can't resume
+          PDEBUGPRINTFX(DBG_ERROR,("subdatastores differ in resume alert code -> cancel resume of superdatastore"));
+          fResuming = false;
+        }
+        else {
+               // resume is possible (but might be cancelled if another subdatastore disagrees 
+               fResumeAlertCode = dsP->fResumeAlertCode;
+               fResuming = true;
+        }
+      }
+    }
+  }
+  // now init my own anchors and firstsync state, which are a combination of my subdatastore's
+  sta = engInitSyncAnchors(NULL,NULL);
+  if (sta!=LOCERR_OK)
+    return sta; // error
+  // determine final resume state
+  if (fResuming) {
+    PDEBUGPRINTFX(DBG_PROTO,("Found suspended session with Alert Code = %hd for all subdatastores",fResumeAlertCode));
+       }
+  else {
+       // superdatastore can't resume, cancel all subdatastore's resumes that might be set
+         for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+       pos->fDatastoreLinkP->fResuming = false;
+    }
+  }
+  // all successful
+  return LOCERR_OK;
+} // TSuperDataStore::engPrepareClientSyncAlert
+  
+  
+
+// Init engine for client sync
+// - determine types to exchange
+// - make sync set ready
+localstatus TSuperDataStore::engInitForClientSync(void)
+{
+       localstatus sta = LOCERR_OK;
+  // first let all subdatastores init for sync
+  TSubDSLinkList::iterator pos;
+  for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) {
+    sta = pos->fDatastoreLinkP->engInitDSForClientSync();
+    if (sta!=LOCERR_OK)
+       return sta;
+  }
+  // now change my own state
+  return inherited::engInitDSForClientSync();
+} // TSuperDataStore::engInitForClientSync
+
+
+
+
+
+
 // Client only: returns number of unsent map items
 sInt32 TSuperDataStore::numUnsentMaps(void)
 {
@@ -1200,7 +1284,7 @@ bool TSuperDataStore::engGenerateMapItems(TMapCommand *aMapCommandP, cAppCharP a
     if (pos==fSubDSLinks.end()) break; // done
     // create current prefix
     AssignString(prefix,aLocalIDPrefix);
-    prefix.append(fCurrentGenDSPos->fDSLinkConfigP->fGUIDPrefix);
+    prefix.append(pos->fDSLinkConfigP->fGUIDPrefix);
     // generate Map items
     ok=pos->fDatastoreLinkP->engGenerateMapItems(aMapCommandP,prefix.c_str());
     // exit if not yet finished with generating map items for this datastore
index d18b42c..2e1295b 100755 (executable)
@@ -110,6 +110,8 @@ public:
   TSuperDataStore(TSuperDSConfig *aDSConfigP, TSyncSession *aSessionP, const char *aName, uInt32 aCommonSyncCapMask=0);
   virtual ~TSuperDataStore();
   virtual void dsResetDataStore(void) { InternalResetDataStore(); inherited::dsResetDataStore(); };
+  // add links to subdatastores
+       void addSubDatastoreLink(TSubDSLinkConfig *aDSLinkConfigP, TLocalEngineDS *aDatastoreP);
   // abort
   virtual void engAbortDataStoreSync(TSyError aReason, bool aLocalProblem, bool aResumable=true);
   virtual bool isAborted(void); // test abort status
@@ -217,6 +219,11 @@ protected:
   // - returns true if DB implementation supports resume (saving of resume marks, alert code, pending maps, tempGUIDs)
   virtual bool dsResumeSupportedInDB(void);
   #ifdef SYSYNC_CLIENT
+  // Client only: initialize Sync alert for datastore according to Parameters set with dsSetClientSyncParams()
+  /// @note initializes anchors and makes calls to isFirstTimeSync() valid
+  virtual localstatus engPrepareClientSyncAlert(void);
+  // Client only: init engine for client sync (superdatastore aware)
+  virtual localstatus engInitForClientSync(void);
   // Client only: called to generate Map items
   // - Returns true if now finished for this datastore
   // - also sets fState to dss_done when finished
index eaea059..bc0f4e0 100755 (executable)
@@ -1005,7 +1005,7 @@ localstatus TSyncAgent::NextMessage(bool &aDone)
     else
     #endif
     {
-      if (!getLocalDeviceID(fLocalURI) || devidWithUserHash()) {
+      if (!getSyncAppBase()->getMyDeviceID(fLocalURI) || devidWithUserHash()) {
         // Device ID is not really unique, make a hash including user name to make it pseudo-unique
         // create MD5 hash from non-unique ID and user name
         // Note: when compiled with GUARANTEED_UNIQUE_DEVICID, devidWithUserHash() is always false.
@@ -1088,9 +1088,9 @@ localstatus TSyncAgent::NextMessage(bool &aDone)
     bool anyslowsyncs=false;
     TLocalEngineDS *localDS;
     for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) {
-      // prepare alert
+      // prepare alert (Note: datastore may be run by a superdatastore)
       localDS = *pos;
-      status=localDS->engPrepareClientSyncAlert(NULL); // not as superdatastore
+      status=localDS->engPrepareClientSyncAlert();
       if (status!=LOCERR_OK) {
         // local database error
         return localError(status); // not found
@@ -1138,7 +1138,7 @@ localstatus TSyncAgent::NextMessage(bool &aDone)
       localDS = *pos;
       if (!localDS->isSubDatastore()) {
         TAlertCommand *alertcmdP;
-        status=localDS->engGenerateClientSyncAlert(alertcmdP);
+        status = localDS->engGenerateClientSyncAlert(alertcmdP);
         if (status!=0) {
           // local database error
           return status; // not found
@@ -1164,12 +1164,14 @@ localstatus TSyncAgent::NextMessage(bool &aDone)
                // prepare engine for sync (%%% new routine in 3.2.0.3, summarizing engInitForSyncOps() and
             // switching to dssta_dataaccessstarted, i.e. loading sync set), but do in only once
             if (!((*pos)->testState(dssta_syncsetready))) {
-               // not yet started
-                       status = (*pos)->engInitForClientSync();
-                   if (status!=LOCERR_OK) {
+              // not yet started
+              status = (*pos)->engInitForClientSync();
+              if (status!=LOCERR_OK ) {
                 // failed
-                AbortSession(status,true);
-                return getAbortReasonStatus();
+                if (status!=LOCERR_DATASTORE_ABORT) {
+                  AbortSession(status,true);
+                  return getAbortReasonStatus();
+                } 
               }
             }
             // start or continue (which is largely nop, as continuing works via unfinished sync command)
@@ -2334,13 +2336,19 @@ void TSyncAgent::getBufferedAnswer(MemPtr_t &aAnswer, MemSize_t &aAnswerSize)
 // returns remaining time for request processing [seconds]
 sInt32 TSyncAgent::RemainingRequestTime(void)
 {
-  // if no request timeout specified, use session timeout
-  sInt32 t = fRequestMaxTime ? fRequestMaxTime : getSessionConfig()->fSessionTimeout;
-  // calculate number of remaining seconds
-  return
-    t==0 ?
-      0x7FFFFFFF : // "infinite"
-      t - (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor);
+       if (IS_CLIENT) {
+       // clients don't process requests, so there's no limit
+       return 0x7FFFFFFF; // "infinite"
+  }
+  else {
+    // if no request timeout specified, use session timeout
+    sInt32 t = fRequestMaxTime ? fRequestMaxTime : getSessionConfig()->fSessionTimeout;
+    // calculate number of remaining seconds
+    return
+      t==0 ?
+        0x7FFFFFFF : // "infinite"
+        t - (sInt32)((getSystemNowAs(TCTX_UTC)-getLastRequestStarted()) / (lineartime_t)secondToLinearTimeFactor);
+  }
 } // TSyncAgent::RemainingRequestTime
 
 
index 7e9987d..276fa3a 100755 (executable)
@@ -2569,6 +2569,10 @@ string TSyncAppBase::getManufacturer(void)
        #ifdef ENGINEINTERFACE_SUPPORT
   if (fConfigP && !(fConfigP->fMan.empty()))
        return fConfigP->fMan;
+  string s;
+  if (getConfigVar("custommanufacturer", s)) {
+       return s;
+  }
   #endif
   // if no string configured, return default
        return CUST_SYNC_MAN;
@@ -2576,10 +2580,15 @@ string TSyncAppBase::getManufacturer(void)
 
 
 // model (application name) of overall solution
-string TSyncAppBase::getModel(void) {
+string TSyncAppBase::getModel(void)
+{
        #ifdef ENGINEINTERFACE_SUPPORT
   if (fConfigP && !(fConfigP->fMod.empty()))
        return fConfigP->fMod;
+  string s;
+  if (getConfigVar("custommodel", s)) {
+       return s;
+  }
   #endif
   // if no string configured, return default
        return CUST_SYNC_MODEL;
@@ -2587,13 +2596,17 @@ string TSyncAppBase::getModel(void) {
 
 
 // hardware version
-string TSyncAppBase::getHardwareVersion(void) {
+string TSyncAppBase::getHardwareVersion(void)
+{
+  string s;
   #ifdef ENGINEINTERFACE_SUPPORT
   if (fConfigP && !(fConfigP->fHwV.empty())) {
     return fConfigP->fHwV;
   }
+  if (getConfigVar("customhardwareversion", s)) {
+       return s;
+  }
   #endif
-  string s;
   // if no string configured, return default
   getPlatformString(pfs_device_name, s);
   return s;
@@ -2601,13 +2614,17 @@ string TSyncAppBase::getHardwareVersion(void) {
 
 
 // firmware version (depends a lot on the context - OS version?)
-string TSyncAppBase::getFirmwareVersion(void) {
+string TSyncAppBase::getFirmwareVersion(void)
+{
+  string s;
   #ifdef ENGINEINTERFACE_SUPPORT
   if (fConfigP && !(fConfigP->fFwV.empty())) {
     return fConfigP->fFwV;
   }
+  if (getConfigVar("customfirmwareversion", s)) {
+       return s;
+  }
   #endif
-  string s;
   // if no string configured, return default
   getPlatformString(pfs_platformvers, s);
   return s;
@@ -2615,11 +2632,16 @@ string TSyncAppBase::getFirmwareVersion(void) {
 
 
 // hardware type (PDA, PC, ...)
-string TSyncAppBase::getDevTyp() {
+string TSyncAppBase::getDevTyp()
+{
   #ifdef ENGINEINTERFACE_SUPPORT
+  string s;
   if (fConfigP && !(fConfigP->fDevTyp.empty())) {
     return fConfigP->fDevTyp;
   }
+  if (getConfigVar("customdevicetype", s)) {
+       return s;
+  }
   #endif
   // if no string configured, return default
   if (isServer())
@@ -2629,6 +2651,21 @@ string TSyncAppBase::getDevTyp() {
 } // TSyncAppBase::getDevTyp
 
 
+// device ID (can be customized using "customdeviceid" config variable)
+// Returns true if deviceID is guaranteed unique
+bool TSyncAppBase::getMyDeviceID(string &devid)
+{
+  #ifdef ENGINEINTERFACE_SUPPORT
+  if (getConfigVar("customdeviceid", devid)) {
+       return true; // custom device ID is assumed to be guaranteed unique
+  }
+  #endif
+       // use device ID as determined by platform adapters  
+  return getLocalDeviceID(devid);
+} // TSyncAppBase::getMyDeviceID
+
+
+
 #ifdef APP_CAN_EXPIRE
 
 void TSyncAppBase::updateAppExpiry(void)
@@ -2719,12 +2756,12 @@ localstatus TSyncAppBase::appEnableStatus(void)
   #else
     #ifndef APP_CAN_EXPIRE
     localstatus regsta = LOCERR_OK; // not registerable, not exprining - just run forever
-    #ifdef RELEASE_VERSION
+    #ifndef NEVER_EXPIRES_IS_OK
     #error "WARNING: Completely unlimited operation w/o license or expiry - is this intended??"
-    #endif
+    #endif // not NEVER_EXPIRES_IS_OK
     #else
     localstatus regsta = LOCERR_BADREG; // not registerable, assume no license, must be eval which expires
-    #endif
+    #endif // APP_CAN_EXPIRE
   #endif
   localstatus sta = regsta;
   // check expiry (only if registration has not already defined one)
index 1b4eee8..caa1b6c 100755 (executable)
@@ -600,6 +600,8 @@ public:
   string getFirmwareVersion();
   // - device type, only used for clients
   string getDevTyp();
+  // - device ID, can be customized via "customdeviceid" config var
+  bool getMyDeviceID(string &devid);
   // - hardwired information (cannot change, always identifying Synthesis engine and its version
   cAppCharP getOEM(void) { return SYSYNC_OEM; } // hardwired, not configurable in target options
   cAppCharP getSoftwareVersion(void) { return SYSYNC_FULL_VERSION_STRING; } // hardwired to real version number
index aa1c94e..1892b24 100755 (executable)
@@ -331,6 +331,10 @@ bool TSmlCommand::handleStatus(TStatusCommand *aStatusCmdP)
     fSessionP->AbortSession(412,false,statuscode); // other party's fault: incomplete command
     return true; // done with command
   }
+  else if (statuscode==418) {
+    POBJDEBUGPRINTFX(fSessionP,DBG_PROTO,("Status: 418: already existed on peer --> accept as ok"));
+    return true; // done with command
+  }
   else if (statuscode<500) {
     // originator exception (we sent some bad stuff)
     POBJDEBUGPRINTFX(fSessionP,DBG_ERROR,("Status: %hd: originator exception",statuscode));
@@ -3704,7 +3708,11 @@ bool TStatusCommand::analyze(TPackageStates aPackageState)
     }
     #ifdef SYDEBUG
     // warn if error (don't treat slow sync status or conflict indication as errors)
-    if (fStatusCode>=300 && fStatusCode!=508 && fStatusCode!=419) {
+    // - 418 = item already exits: sent by Funambol server when both client and
+    //   and server have a new item which is considered identical by the server
+    //   (must be really identical, minor difference will lead to a merged item
+    //   which is sent back to the client without the 418). See Moblin Bugzilla #4599.
+    if (fStatusCode>=300 && fStatusCode!=508 && fStatusCode!=419 && fStatusCode!=418) {
       PDEBUGPRINTFX(DBG_ERROR,(
         "WARNING: RECEIVED NON-OK STATUS %hd for &html;<a name=\"SO_%ld_%ld\" href=\"#IO_%ld_%ld\">&html;command '%s'&html;</a>&html; (outgoing MsgID=%ld, CmdID=%ld)",
         fStatusCode,
index 4776cf2..c7e5d8b 100644 (file)
@@ -415,6 +415,8 @@ void TRemoteRuleConfig::clear(void)
   #ifndef MINIMAL_CODE
   fRemoteDescName.erase();
   #endif
+  fSubRulesList.clear(); // no included subrules
+       fSubRule = false; // normal rule by default
   // - rules are final by default
   fFinalRule = true;
   // clear inherited
@@ -428,22 +430,22 @@ void TRemoteRuleConfig::clear(void)
 bool TRemoteRuleConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
 {
   // checking the elements
-  // - identification of remote
-  if (strucmp(aElementName,"manufacturer")==0)
+  // - identification of remote (irrelevant for subrules)
+  if (!fSubRule && strucmp(aElementName,"manufacturer")==0)
     expectString(fManufacturer);
-  else if (strucmp(aElementName,"model")==0)
+  else if (!fSubRule && strucmp(aElementName,"model")==0)
     expectString(fModel);
-  else if (strucmp(aElementName,"oem")==0)
+  else if (!fSubRule && strucmp(aElementName,"oem")==0)
     expectString(fOem);
-  else if (strucmp(aElementName,"firmware")==0)
+  else if (!fSubRule && strucmp(aElementName,"firmware")==0)
     expectString(fFirmwareVers);
-  else if (strucmp(aElementName,"software")==0)
+  else if (!fSubRule && strucmp(aElementName,"software")==0)
     expectString(fSoftwareVers);
-  else if (strucmp(aElementName,"hardware")==0)
+  else if (!fSubRule && strucmp(aElementName,"hardware")==0)
     expectString(fHardwareVers);
-  else if (strucmp(aElementName,"deviceid")==0)
+  else if (!fSubRule && strucmp(aElementName,"deviceid")==0)
     expectString(fDevId);
-  else if (strucmp(aElementName,"devicetype")==0)
+  else if (!fSubRule && strucmp(aElementName,"devicetype")==0)
     expectString(fDevTyp);
   // - options
   else if (strucmp(aElementName,"legacymode")==0)
@@ -498,15 +500,26 @@ bool TRemoteRuleConfig::localStartElement(const char *aElementName, const char *
     expectTristate(fForceUTC);
   else if (strucmp(aElementName,"forcelocaltime")==0)
     expectTristate(fForceLocaltime);
-  /*
-  // Some extra tweaking params for unstable devices and connections
-  else if (strucmp(aElementName,"maxobjspersession")==0)
-    expectUInt32(fMaxObjsPerSession); // max number of object add/deletes sent per session
-  else if (strucmp(aElementName,"maxkbspersession")==0)
-    expectUInt32(fMaxKBsPerSession); // max number of kilobytes content data sent per session
-  else if (strucmp(aElementName,"maxmessagesize")==0)
-    expectUInt32(fMaxMessageSize); // do not send larger messages than these (except if otherwise item cannot be sent)
-  */
+  // inclusion of subrules
+  else if (strucmp(aElementName,"include")==0) {
+               // <include rule=""/>  
+    expectEmpty();
+    const char* nam = getAttr(aAttributes,"rule");
+    if (!nam)
+       return fail("<include> must specify \"rule\"");
+    else {
+       // find rule
+      TRemoteRulesList::iterator pos;
+      TSessionConfig *scfgP = static_cast<TSessionConfig *>(getParentElement());
+      for(pos=scfgP->fRemoteRulesList.begin();pos!=scfgP->fRemoteRulesList.end();pos++) {
+        if (strucmp(nam,(*pos)->getName())==0) {
+                           fSubRulesList.push_back(*pos);
+          return true; // done
+        }
+      }
+       return fail("rule '%s' for <include> not found (must be defined before included)",nam);
+    }
+  }
   // rule script. Note that this is special, as it is NOT resolved in the config, but
   // copied to the session first, as it might differ between sessions.
   #ifdef SCRIPT_SUPPORT
@@ -687,12 +700,14 @@ bool TSessionConfig::localStartElement(const char *aElementName, const char **aA
 {
   // checking the elements
   #ifndef NO_REMOTE_RULES
-  if (strucmp(aElementName,"remoterule")==0) {
+  bool isSubRule = strucmp(aElementName,"subrule")==0;
+  if (strucmp(aElementName,"remoterule")==0 || isSubRule) {
     // check for optional name attribute
     const char* nam = getAttr(aAttributes,"name");
     if (!nam) nam="unnamed";
     // create rule
     TRemoteRuleConfig *ruleP = new TRemoteRuleConfig(nam,this);
+    ruleP->fSubRule = isSubRule;
     fRemoteRulesList.push_back(ruleP);
     expectChildParsing(*ruleP);
   }
@@ -1011,11 +1026,11 @@ TSyncSession::TSyncSession(
       getSyncAppBase()->getModel().c_str(), getSyncAppBase()->getManufacturer().c_str()
     ));
     // show platform we're on
-    string uri;
-    getPlatformString(pfs_device_uri,uri);
+    string devid;
+    getSyncAppBase()->getMyDeviceID(devid);
     PDEBUGPRINTFX(DBG_HOT,(
       "---- Running on " SYSYNC_PLATFORM_NAME ", URI/deviceID='%s'",
-      uri.c_str()
+      devid.c_str()
     ));
     // show process and thread ID of the main session thread
     #ifdef MULTI_THREAD_SUPPORT
@@ -1027,6 +1042,7 @@ TSyncSession::TSyncSession(
     #endif
     // show platform details
     string dname,vers;
+    // - as determined by engine itself
     getPlatformString(pfs_device_name,dname);
     getPlatformString(pfs_platformvers,vers);
     PDEBUGPRINTFX(DBG_HOT,(
@@ -1034,6 +1050,12 @@ TSyncSession::TSyncSession(
       dname.c_str(),
       vers.c_str()
     ));
+    // - as configured
+    PDEBUGPRINTFX(DBG_HOT,(
+      "---- Configured Hardware Version = '%s', Firmware Version = '%s'",
+      getSyncAppBase()->getHardwareVersion().c_str(),
+      getSyncAppBase()->getFirmwareVersion().c_str()
+    ));
     // show time zone infos
     lineartime_t tim;
     string z,ts;
@@ -1279,8 +1301,8 @@ void TSyncSession::InternalResetSessionEx(bool terminationCall)
   // - immediately abort SYNC command in progress
   fLocalSyncDatastoreP = NULL;
   #ifndef NO_REMOTE_RULES
-  // - no remote rule applied
-  fAppliedRemoteRuleP = NULL;
+  // - no remote rules applied
+  fActiveRemoteRules.clear();
   #endif
   // - set defaults for >=SyncML 1.1 features
   fRemoteWantsNOC = false; // no, unless requested
@@ -1505,8 +1527,6 @@ void TSyncSession::InternalResetSessionEx(bool terminationCall)
   // tristates!!
   fEnumDefaultPropParams=getSessionConfig()->fEnumDefaultPropParams;
   #ifdef SCRIPT_SUPPORT
-  // - rule script is empty at start of session
-  fRuleScript.erase();
   // call session init script
   if (!terminationCall && !fTerminated) {
     TScriptContext::execute(
@@ -3657,6 +3677,7 @@ bool TSyncSession::processSyncOpItem(
   // check for aborted datastore
   if (fLocalSyncDatastoreP->CheckAborted(aStatusCommand)) return false;
   // check if we can process it now
+  // Note: request time limit is active in server only.
   if (!fLocalSyncDatastoreP->engIsStarted(false) || RemainingRequestTime()<0) {
     aQueueForLater=true; // re-execute later...
     return true; // ...but otherwise ok
@@ -4315,94 +4336,103 @@ localstatus TSyncSession::checkRemoteSpecifics(SmlDevInfDevInfPtr_t aDevInfP)
   PDEBUGBLOCKDESC("RemoteRules","Checking for remote rules");
   // get config for session
   TSessionConfig *scP = getSessionConfig();
-  // look if we have a matching rule for this device
+  // look if we have matching rule(s) for this device
   TRemoteRulesList::iterator pos;
   for(pos=scP->fRemoteRulesList.begin();pos!=scP->fRemoteRulesList.end();pos++) {
+    TRemoteRuleConfig *ruleP = *pos;
     // compare with devinf (or test for default-rule if aDevInfP is NULL
     if (
-      ((*pos)->fManufacturer.empty() || (aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->man),(*pos)->fManufacturer.c_str())==0)) &&
-      ((*pos)->fModel.empty() || (aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->mod),(*pos)->fModel.c_str())==0)) &&
-      ((*pos)->fOem.empty() || (aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->oem),(*pos)->fOem.c_str())==0)) &&
-      ((*pos)->fFirmwareVers.empty() || (aDevInfP && (*pos)->fFirmwareVers==smlPCDataToCharP(aDevInfP->fwv))) &&
-      ((*pos)->fSoftwareVers.empty() || (aDevInfP && (*pos)->fSoftwareVers==smlPCDataToCharP(aDevInfP->swv))) &&
-      ((*pos)->fHardwareVers.empty() || (aDevInfP && (*pos)->fHardwareVers==smlPCDataToCharP(aDevInfP->hwv))) &&
-      ((*pos)->fDevId.empty() || (aDevInfP && (*pos)->fDevId==smlPCDataToCharP(aDevInfP->devid))) &&
-      ((*pos)->fDevTyp.empty() || (aDevInfP && (*pos)->fDevTyp==smlPCDataToCharP(aDevInfP->devtyp)))
+       !ruleP->fSubRule && // subrules never apply directly
+      (ruleP->fManufacturer.empty() || (aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->man),ruleP->fManufacturer.c_str())==0)) &&
+      (ruleP->fModel.empty() || (aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->mod),ruleP->fModel.c_str())==0)) &&
+      (ruleP->fOem.empty() || (aDevInfP && strwildcmp(smlPCDataToCharP(aDevInfP->oem),ruleP->fOem.c_str())==0)) &&
+      (ruleP->fFirmwareVers.empty() || (aDevInfP && ruleP->fFirmwareVers==smlPCDataToCharP(aDevInfP->fwv))) &&
+      (ruleP->fSoftwareVers.empty() || (aDevInfP && ruleP->fSoftwareVers==smlPCDataToCharP(aDevInfP->swv))) &&
+      (ruleP->fHardwareVers.empty() || (aDevInfP && ruleP->fHardwareVers==smlPCDataToCharP(aDevInfP->hwv))) &&
+      (ruleP->fDevId.empty() || (aDevInfP && ruleP->fDevId==smlPCDataToCharP(aDevInfP->devid))) &&
+      (ruleP->fDevTyp.empty() || (aDevInfP && ruleP->fDevTyp==smlPCDataToCharP(aDevInfP->devtyp)))
     ) {
-      // found, apply rules
-      TRemoteRuleConfig *ruleP = *pos;
-      PDEBUGPRINTFX(DBG_HOT,("Found special rule '%s' for remote party, applying",ruleP->getName()));
-      // set options
-      // - only device specific
-      fAppliedRemoteRuleP = ruleP; // save pointer to applied rule
-      // - apply options that have a value
-      if (ruleP->fLegacyMode>=0) fLegacyMode = ruleP->fLegacyMode;
-      if (ruleP->fLenientMode>=0) fLenientMode = ruleP->fLenientMode;
-      if (ruleP->fLimitedFieldLengths>=0) fLimitedRemoteFieldLengths = ruleP->fLimitedFieldLengths;
-      if (ruleP->fDontSendEmptyProperties>=0) fDontSendEmptyProperties = ruleP->fDontSendEmptyProperties;
-      if (ruleP->fDoQuote8BitContent>=0) fDoQuote8BitContent = ruleP->fDoQuote8BitContent;
-      if (ruleP->fDoNotFoldContent>=0) fDoNotFoldContent = ruleP->fDoNotFoldContent;
-      if (ruleP->fNoReplaceInSlowsync>=0) fNoReplaceInSlowsync = ruleP->fNoReplaceInSlowsync;
-      if (ruleP->fTreatRemoteTimeAsLocal>=0) fTreatRemoteTimeAsLocal = ruleP->fTreatRemoteTimeAsLocal;
-      if (ruleP->fTreatRemoteTimeAsUTC>=0) fTreatRemoteTimeAsUTC = ruleP->fTreatRemoteTimeAsUTC;
-      if (ruleP->fVCal10EnddatesSameDay>=0) fVCal10EnddatesSameDay = ruleP->fVCal10EnddatesSameDay;
-      if (ruleP->fIgnoreDevInfMaxSize>=0) fIgnoreDevInfMaxSize = ruleP->fIgnoreDevInfMaxSize;
-      if (ruleP->fIgnoreCTCap>=0) fIgnoreCTCap = ruleP->fIgnoreCTCap;
-      if (ruleP->fDSPathInDevInf>=0) fDSPathInDevInf = ruleP->fDSPathInDevInf;
-      if (ruleP->fDSCgiInDevInf>=0) fDSCgiInDevInf = ruleP->fDSCgiInDevInf;
-      if (ruleP->fUpdateClientDuringSlowsync>=0) fUpdateClientDuringSlowsync = ruleP->fUpdateClientDuringSlowsync;
-      if (ruleP->fUpdateServerDuringSlowsync>=0) fUpdateServerDuringSlowsync = ruleP->fUpdateServerDuringSlowsync;
-      if (ruleP->fAllowMessageRetries>=0) fAllowMessageRetries = ruleP->fAllowMessageRetries;
-      if (ruleP->fStrictExecOrdering>=0) fStrictExecOrdering = ruleP->fStrictExecOrdering;
-      if (ruleP->fTreatCopyAsAdd>=0) fTreatCopyAsAdd = ruleP->fTreatCopyAsAdd;
-      if (ruleP->fCompleteFromClientOnly>=0) fCompleteFromClientOnly = ruleP->fCompleteFromClientOnly;
-      if (ruleP->fRequestMaxTime>=0) fRequestMaxTime = ruleP->fRequestMaxTime;
-      if (ruleP->fDefaultOutCharset!=chs_unknown) fDefaultOutCharset = ruleP->fDefaultOutCharset;
-      if (ruleP->fDefaultInCharset!=chs_unknown) fDefaultInCharset = ruleP->fDefaultInCharset;
-      // - possibly override decisions that are otherwise made by session
-      //   Note: this is not a single option because we had this before rule options were tristates.
-      if (ruleP->fForceUTC>0) fRemoteCanHandleUTC=true;
-      if (ruleP->fForceLocaltime>0) fRemoteCanHandleUTC=false;
-      // - install rule script
-      #ifdef SCRIPT_SUPPORT
-      if (!ruleP->fRuleScriptTemplate.empty()) fRuleScript = (*pos)->fRuleScriptTemplate;
-      #endif
-      // - descriptive name for the device (for log)
-      #ifndef MINIMAL_CODE
-      if (!ruleP->fRemoteDescName.empty()) fRemoteDescName = (*pos)->fRemoteDescName;
-      #endif
-      // - test for rejection
-      if (ruleP->fRejectStatusCode!=DONT_REJECT) {
-        // reject operation with this device
-        sta = (*pos)->fRejectStatusCode;
-        PDEBUGPRINTFX(DBG_ERROR,("remote party rejected by configured 'remoterule', status=%hd",sta));
-        AbortSession(sta,true);
-        return sta;
-      }
-      // done only if this rule is final
+      // found matching rule
+      PDEBUGPRINTFX(DBG_HOT,("Found <remoterule> '%s' matching for this peer",ruleP->getName()));
+      // remember it
+      fActiveRemoteRules.push_back(ruleP);
+      // add included subrules
+                 TRemoteRulesList::iterator spos;
+               for(spos=ruleP->fSubRulesList.begin();spos!=ruleP->fSubRulesList.end();spos++) {
+             fActiveRemoteRules.push_back(*spos);
+             PDEBUGPRINTFX(DBG_HOT,("- rule also activates sub-rule '%s'",(*spos)->getName()));
+                       }
+      // if this rule is final, don't check for further matches
       if (ruleP->fFinalRule) break;
+    }  
+  }
+  // process activated rules and subrules
+  for(pos=fActiveRemoteRules.begin();pos!=fActiveRemoteRules.end();pos++) {      
+    // activate this rule
+    TRemoteRuleConfig *ruleP = *pos;
+    // - apply options that have a value
+    if (ruleP->fLegacyMode>=0) fLegacyMode = ruleP->fLegacyMode;
+    if (ruleP->fLenientMode>=0) fLenientMode = ruleP->fLenientMode;
+    if (ruleP->fLimitedFieldLengths>=0) fLimitedRemoteFieldLengths = ruleP->fLimitedFieldLengths;
+    if (ruleP->fDontSendEmptyProperties>=0) fDontSendEmptyProperties = ruleP->fDontSendEmptyProperties;
+    if (ruleP->fDoQuote8BitContent>=0) fDoQuote8BitContent = ruleP->fDoQuote8BitContent;
+    if (ruleP->fDoNotFoldContent>=0) fDoNotFoldContent = ruleP->fDoNotFoldContent;
+    if (ruleP->fNoReplaceInSlowsync>=0) fNoReplaceInSlowsync = ruleP->fNoReplaceInSlowsync;
+    if (ruleP->fTreatRemoteTimeAsLocal>=0) fTreatRemoteTimeAsLocal = ruleP->fTreatRemoteTimeAsLocal;
+    if (ruleP->fTreatRemoteTimeAsUTC>=0) fTreatRemoteTimeAsUTC = ruleP->fTreatRemoteTimeAsUTC;
+    if (ruleP->fVCal10EnddatesSameDay>=0) fVCal10EnddatesSameDay = ruleP->fVCal10EnddatesSameDay;
+    if (ruleP->fIgnoreDevInfMaxSize>=0) fIgnoreDevInfMaxSize = ruleP->fIgnoreDevInfMaxSize;
+    if (ruleP->fIgnoreCTCap>=0) fIgnoreCTCap = ruleP->fIgnoreCTCap;
+    if (ruleP->fDSPathInDevInf>=0) fDSPathInDevInf = ruleP->fDSPathInDevInf;
+    if (ruleP->fDSCgiInDevInf>=0) fDSCgiInDevInf = ruleP->fDSCgiInDevInf;
+    if (ruleP->fUpdateClientDuringSlowsync>=0) fUpdateClientDuringSlowsync = ruleP->fUpdateClientDuringSlowsync;
+    if (ruleP->fUpdateServerDuringSlowsync>=0) fUpdateServerDuringSlowsync = ruleP->fUpdateServerDuringSlowsync;
+    if (ruleP->fAllowMessageRetries>=0) fAllowMessageRetries = ruleP->fAllowMessageRetries;
+    if (ruleP->fStrictExecOrdering>=0) fStrictExecOrdering = ruleP->fStrictExecOrdering;
+    if (ruleP->fTreatCopyAsAdd>=0) fTreatCopyAsAdd = ruleP->fTreatCopyAsAdd;
+    if (ruleP->fCompleteFromClientOnly>=0) fCompleteFromClientOnly = ruleP->fCompleteFromClientOnly;
+    if (ruleP->fRequestMaxTime>=0) fRequestMaxTime = ruleP->fRequestMaxTime;
+    if (ruleP->fDefaultOutCharset!=chs_unknown) fDefaultOutCharset = ruleP->fDefaultOutCharset;
+    if (ruleP->fDefaultInCharset!=chs_unknown) fDefaultInCharset = ruleP->fDefaultInCharset;
+    // - possibly override decisions that are otherwise made by session
+    //   Note: this is not a single option because we had this before rule options were tristates.
+    if (ruleP->fForceUTC>0) fRemoteCanHandleUTC=true;
+    if (ruleP->fForceLocaltime>0) fRemoteCanHandleUTC=false;
+    // - descriptive name for the device (for log)
+    #ifndef MINIMAL_CODE
+    if (!ruleP->fRemoteDescName.empty()) fRemoteDescName = ruleP->fRemoteDescName;
+    #endif
+    // - test for rejection
+    if (ruleP->fRejectStatusCode!=DONT_REJECT) {
+      // reject operation with this device
+      sta = ruleP->fRejectStatusCode;
+      PDEBUGPRINTFX(DBG_ERROR,("remote party rejected by <remoterule> '%s', status=%hd",ruleP->getName(),sta));
+      AbortSession(sta,true);
+      return sta;
     }
-  } // for
-  // - resolve and execute rule script
-  #ifdef SCRIPT_SUPPORT
-  if (!fRuleScript.empty()) {
-    // resolve variable references
-    TScriptContext::linkIntoContext(fRuleScript,fSessionScriptContextP,this);
-    // execute now
-    PDEBUGPRINTFX(DBG_HOT,("Executing rulescript."));
-    TScriptContext::execute(
-      fSessionScriptContextP,
-      fRuleScript,
-      NULL, // context's function table
-      NULL // datastore pointer needed for context
-    );
-  }
-  #endif // SCRIPT_SUPPORT
+    // - execute rule script
+    #ifdef SCRIPT_SUPPORT
+    if (!ruleP->fRuleScriptTemplate.empty()) {
+       // copy from template
+       string ruleScript = ruleP->fRuleScriptTemplate;
+      // resolve variable references
+      TScriptContext::linkIntoContext(ruleScript,fSessionScriptContextP,this);
+      // execute now
+      PDEBUGPRINTFX(DBG_HOT,("Executing rulescript for rule '%s'",ruleP->getName()));
+      TScriptContext::execute(
+        fSessionScriptContextP,
+        ruleScript,
+        NULL, // context's function table
+        NULL // datastore pointer needed for context
+      );
+    }
+    #endif
+  } // for all activated rules
   PDEBUGENDBLOCK("RemoteRules");
   #endif // NO_REMOTE_RULES
   // Final adjustments
   #ifndef NO_REMOTE_RULES
-  if (!fAppliedRemoteRuleP)
+  if (fActiveRemoteRules.empty())
   #endif
   {
        // no remote rule (none found or mechanism excluded by NO_REMOTE_RULES)
@@ -4429,7 +4459,7 @@ localstatus TSyncSession::checkRemoteSpecifics(SmlDevInfDevInfPtr_t aDevInfP)
     }
   }
   // show summary
-  PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("Summary of all behaviour options (possibly set by remote rule)"));
+  PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("Summary of all behaviour options (possibly modified by remote rule(s))"));
   #ifndef MINIMAL_CODE
   PDEBUGPRINTFX(DBG_HOT+DBG_REMOTEINFO,("- Remote Description        : %s",fRemoteDescName.c_str()));
   #endif
@@ -4462,6 +4492,25 @@ localstatus TSyncSession::checkRemoteSpecifics(SmlDevInfDevInfPtr_t aDevInfP)
 } // TSyncSession::checkRemoteSpecifics
 
 
+#ifndef NO_REMOTE_RULES
+
+// check if given rule (by name, or if aRuleName=NULL by rule pointer) is active
+bool TSyncSession::isActiveRule(cAppCharP aRuleName, TRemoteRuleConfig *aRuleP)
+{
+  TRemoteRulesList::iterator pos;
+  for(pos=fActiveRemoteRules.begin();pos!=fActiveRemoteRules.end();pos++) {
+       if (
+       (aRuleName==NULL && (*pos)==aRuleP) || // match by pointer...
+      (strucmp(aRuleName,(*pos)->getName())==0) // ...or name
+    )
+       return true;
+  }
+  // no match
+  return false;
+} // TSyncSession::isActiveRule
+
+#endif // NO_REMOTE_RULES
+
 
 // access to config
 TSessionConfig *TSyncSession::getSessionConfig(void)
index 6d25165..153f987 100755 (executable)
@@ -101,6 +101,10 @@ typedef struct {
 
 #ifndef NO_REMOTE_RULES
 
+class TRemoteRuleConfig; // forward
+
+typedef std::list<TRemoteRuleConfig *> TRemoteRulesList;
+
 // remote party special rule
 class TRemoteRuleConfig: public TConfigElement
 {
@@ -144,15 +148,19 @@ public:
   TCharSets fDefaultInCharset; // default charset for input interpretation
   TSyError fRejectStatusCode; // if >=0, attempt to connect will always be rejected with given status code
   sInt8 fForceUTC; // force sending time in UTC (overrides SyncML 1.1 <utc/> devInf flag)
-  sInt8 fForceLocaltime; // force sending time in localtime (overrides SyncML 1.1 <utc/> devInf flag)
+  sInt8 fForceLocaltime; // force sending time in localtime (overrides SyncML 1.1 <utc/> devInf flag)    
   #ifndef MINIMAL_CODE
   string fRemoteDescName; // descriptive name of remote
   #endif
   #ifdef SCRIPT_SUPPORT
   string fRuleScriptTemplate; // template for rule script
   #endif
+  // list of subrules to activate
+  TRemoteRulesList fSubRulesList;
   // flag if this is a final rule (if matches, no more rules will be checked)
   bool fFinalRule;
+  // flag if this is a subrule (cannot match by itself)
+  bool fSubRule;
 protected:
   // check config elements
   #ifndef HARDCODED_CONFIG
@@ -162,7 +170,6 @@ protected:
 }; // TRemoteRuleConfig
 
 
-typedef std::list<TRemoteRuleConfig *> TRemoteRulesList;
 
 #endif // NO_REMOTE_RULES
 
@@ -637,7 +644,8 @@ public:
   TCharSets fDefaultOutCharset; // default charset for output generation
   TCharSets fDefaultInCharset; // default charset for input interpretation
   #ifndef NO_REMOTE_RULES
-  TRemoteRuleConfig *fAppliedRemoteRuleP; // applied remote rule
+  bool isActiveRule(cAppCharP aRuleName, TRemoteRuleConfig *aRuleP=NULL); // check if given rule (by name, or if aRuleName=NULL by rule pointer) is active
+  TRemoteRulesList fActiveRemoteRules; // list of remote rules currently active in this session
   #endif
   // legacy mode
   bool fLegacyMode; // if set, remote will see the types marked preferred="legacy" in devInf as preferred types, not the regular preferred ones
@@ -862,10 +870,6 @@ protected:
   #ifdef SCRIPT_SUPPORT
   // Session level script context
   TScriptContext *fSessionScriptContextP;
-  // Active RemoteRule's scipt. Note that this is copied from the config
-  // as it might differ from session to session (and can't be resolved in
-  // the config globally)
-  string fRuleScript; // rule script, copied from active RemoteRule
   #endif
   // Session options
   bool fReadOnly;
index b31f6c0..7ada448 100644 (file)
@@ -39,7 +39,7 @@ namespace sysync {
 
 #define SYSER_EXTRA_ID_GOOSYNC 10 // Toffa.com Goosync special version
 #define SYSER_EXTRA_ID_FONELINK 11 // novamedia FoneLink special version
-
+#define SYSER_EXTRA_ID_SOOCIAL 12 // soocial.com special version
 
 
 // product codes
index 80cedf3..298bfe8 100755 (executable)
@@ -48,6 +48,11 @@ typedef enum {
   tctx_tz_Atlantic,                 //  -4    Atlantik (Kanada)
   tctx_tz_Atlantic_2006,            //  -4    Atlantik (Kanada)
   tctx_tz_Atlantic_2007,            //  -4    Atlantik (Kanada)
+  tctx_tz_ART_ARST,                 //  -3    Buenos Aires
+  tctx_tz_ART_ARST_2006,            //  -3    Buenos Aires
+  tctx_tz_ART_ARST_2007,            //  -3    Buenos Aires
+  tctx_tz_ART_ARST_2008,            //  -3    Buenos Aires
+  tctx_tz_ART_ARST_2009,            //  -3    Buenos Aires
   tctx_tz_AUS_Central,              //   9.5  Darwin
   tctx_tz_AUS_Eastern,              //  10    Canberra, Melbourne, Sydney
   tctx_tz_Azerbaijan,               //   4    Baku
@@ -178,7 +183,7 @@ typedef enum {
   tctx_tz_Pacific_Mexico,           //  -8    Tijuana, Niederkalifornien (Mexiko)
   tctx_tz_Romance,                  //   1    Bruessel, Kopenhagen, Madrid, Paris
   tctx_tz_Russian,                  //   3    Moskau, St. Petersburg, Wolgograd
-  tctx_tz_SA_Eastern,               //  -3    Buenos Aires, Georgetown
+  tctx_tz_SA_Eastern,               //  -3    Buenos Aires, Georgetown (OLD)
   tctx_tz_SA_Pacific,               //  -5    Bogota, Lima, Quito, Rio Branco
   tctx_tz_SA_Western,               //  -4    Caracas, La Paz
   tctx_tz_Samoa,                    // -11    Midway-Inseln, Samoa
@@ -338,6 +343,11 @@ const tbl_tz_entry tbl_tz[tctx_numtimezones] =
   { "Atlantic",          "America/Halifax",         -240, 60, " ",    "", {  3, 0,2, 2,0 },  { 11, 0,1, 2,0 } },  //  -4
   { "Atlantic",          "America/Halifax",         -240, 60, " ","2006", {  4, 0,1, 2,0 },  { 10, 0,5, 2,0 } },  //  -4
   { "Atlantic",          "America/Halifax",         -240, 60, " ","2007", {  3, 0,2, 2,0 },  { 11, 0,1, 2,0 } },  //  -4
+  { "ART/ARST",          "America/Buenos_Aires",    -180, 60, " ",    "", {  0, 0,0, 0,0 },  {  0, 0,0, 0,0 } },  //  -3
+  { "ART/ARST",          "America/Buenos_Aires",    -180, 60, " ","2006", {  0, 0,0, 0,0 },  {  0, 0,0, 0,0 } },  //  -3
+  { "ART/ARST",          "America/Buenos_Aires",    -180, 60, " ","2007", { 12,-1,29,23,59}, {  1,-1,1, 0,0 } },  //  -3
+  { "ART/ARST",          "America/Buenos_Aires",    -180, 60, " ","2008", { 10, 6,3,23,59},  {  3, 6,3,23,59} },  //  -3
+  { "ART/ARST",          "America/Buenos_Aires",    -180, 60, " ","2009", { 10, 6,3,23,59},  {  3, 6,2,23,59} },  //  -3
   { "AUS_Central",       "Australia/Darwin",         570,  0, " ",    "", {  0, 0,0, 0,0 },  {  0, 0,0, 0,0 } },  //   9.5
   { "AUS_Eastern",       "Australia/Sydney",         600, 60, " ",    "", { 10, 0,5, 2,0 },  {  3, 0,5, 3,0 } },  //  10
   { "Azerbaijan",        "Asia/Baku",                240, 60, " ",    "", {  3, 0,5, 4,0 },  { 10, 0,5, 5,0 } },  //   4
index 81696c6..3e1fb97 100755 (executable)
@@ -369,6 +369,7 @@ void SubSystem  ( string &s,   string subName ) {
 void Manufacturer( string &s, string    name ) { AddCapa( s, CA_Manufacturer, name ); }
 void Description ( string &s, string    desc ) { AddCapa( s, CA_Description,  desc ); }
 void GuidStr     ( string &s, string guidStr ) { AddCapa( s, CA_GUID,      guidStr ); }
+void BuiltIn     ( string &s, string  plugin ) { AddCapa( s, CA_Plugin,     plugin ); }
 
 
 // Add global context <gContext> information to <s>
@@ -935,7 +936,6 @@ bool GlobContextFound( string dbName, GlobContext* &g )
     g= g->next;
   } // while
 
-//printf( "ContextFound: %08X '%s'\n", g, dbName.c_str() );
   return g!=NULL && g->ref!=NULL;
 } // GlobContextFound
 
index 331d1e5..22f104c 100755 (executable)
@@ -119,6 +119,9 @@ void Description ( string &s, string desc );
 /*! Add a <guid> string to <s> */
 void GuidStr     ( string &s, string guidStr );
 
+/*! Add a <plugin> string to <s> */
+void BuiltIn     ( string &s, string  plugin );
+
 /*! Add global context <gContext> information to <s> */
 void GContext    ( string &s, GlobContext* gContext );
 
index d1bfeff..55ff762 100755 (executable)
@@ -30,9 +30,9 @@
 #include "android/log.h"
 #endif
 
-#define MyDB "SDK"                /* local debug name */
-#define SDKversionMask 0xffff00ff /* Old mask for version comparison: Omit OS identifier */
-#define maxmsglen      1024       /* Maximum string length for callback string */
+#define MyDB "SDK"                    /* local debug name */
+#define Old_SDKversionMask 0xffff00ff /* Old mask for version comparison: Omit OS identifier */
+#define maxmsglen 1024                /* Maximum string length for callback string */
 
 
 
@@ -48,9 +48,9 @@ CVersion Plugin_Version( short buildNumber )
   #define P 256
   long    v;
 
-  #define SDK_VERSION_MAJOR 1 /* Release: V1.6.1, change this if you need troubles */
-  #define SDK_VERSION_MINOR 6
-  #define SDK_SUBVERSION    1
+  #define SDK_VERSION_MAJOR 1 /* Release: V1.7.0, change this if you need troubles */
+  #define SDK_VERSION_MINOR 7
+  #define SDK_SUBVERSION    0
 
   /* allowed range for the local build number */
   if (buildNumber<  0) buildNumber=   0;
@@ -75,7 +75,7 @@ CVersion Plugin_Version( short buildNumber )
 bool Feature_Supported  ( CVersion versionFeature, CVersion currentVersion )
 {
   CVersion v= currentVersion;
-  if      (v<VP_NewBuildNumber) v= v & SDKversionMask; /* avoid OS identifier comparison */
+  if      (v<VP_NewBuildNumber) v= v & Old_SDKversionMask; /* avoid OS identifier comparison */
   return   v>=versionFeature;
 } /* FeatureSupported */
 
@@ -85,7 +85,7 @@ bool Feature_Supported  ( CVersion versionFeature, CVersion currentVersion )
 bool Feature_SupportedEq( CVersion versionFeature, CVersion currentVersion )
 {
   CVersion v= currentVersion;
-  if      (v<VP_NewBuildNumber) v= v & SDKversionMask; /* avoid OS identifier comparison */
+  if      (v<VP_NewBuildNumber) v= v & Old_SDKversionMask; /* avoid OS identifier comparison */
   return   v==versionFeature;
 } /* FeatureSupportedEq */
 
index 5ae82bf..327294e 100755 (executable)
@@ -57,17 +57,23 @@ TSyError TEngineModuleBase::Connect( string   aEngineName,
                                      CVersion aPrgVersion,
                                      uInt16   aDebugFlags)
 {
-  TSyError err= LOCERR_OK;
+  TSyError     err= LOCERR_OK;
+
   fEngineName = aEngineName;
   fPrgVersion = aPrgVersion;
   fDebugFlags = aDebugFlags;
 
   #if defined SYSYNC_ENGINE || defined SYSYNC_ENGINE_TEST
+    uInt16 cbVersion= DB_Callback_Version; // use current by default
     if (fCI==NULL) {
       fCI = &fCIBuffer;
       fCIisStatic = true;
     }
-    InitCallback_Exotic( fCI,      DB_Callback_Version );
+    else {
+      cbVersion= fCI->callbackVersion; // the cbVersion from outside (ConnectEngineS)
+    } // if
+
+    InitCallback_Exotic( fCI, cbVersion ); // be aware that it could be an older version
                          fCI->thisBase= this; // get <this> later for callback calls
     CB_Connect         ( fCI );
   #endif
index 5072d6d..09a6f66 100644 (file)
@@ -157,6 +157,12 @@ enum TSyErrorEnum {
   LOCERR_BADURL = 20046,
   /** server not found */
   LOCERR_SRVNOTFOUND = 20047,
+  /**
+   * ABORTDATASTORE() parameter to flag the current datastore as bad
+   * without aborting the whole session. Exact reason for abort depends
+   * on caller of that macro.
+   */
+  LOCERR_DATASTORE_ABORT = 20048,
 
   /** cURL error code */
   LOCERR_CURL = 21000,
index 0e40428..9e646ca 100755 (executable)
@@ -93,11 +93,9 @@ enum Version {
 /*VP_Init                  0x01000000   * V1.0.N.0 : Initial version */
 /*VP_1st                   0x01000002   * V1.0.N.2 : 1st delivered version */
 /*VP_Session_Login         0x01000003   * V1.0.N.3 : VAR_String for Session_Login: this version only */
+/*VP_EngineVersionParam    0x01000004   * V1.0.N.4 : <engineVersion> param for "Module_PluginParams" */
+/*VP_CB_Version2         = 0x01000005   * V1.0.N.5 : Callback version >= 2 supported */
 
-  /** V1.0.N.4 : <engineVersion> param for "Module_PluginParams" */
-  VP_EngineVersionParam  = 0x01000004,
-  /** V1.0.N.5 : Callback version >= 2 supported                 */
-  VP_CB_Version2         = 0x01000005,
   /** V1.0.N.6 : With new function "DeleteBlob"                  */
   VP_DeleteBlob          = 0x01000006,
   /** V1.0.N.7 : With new "scripting" function "AdaptItemData"   */
@@ -155,8 +153,11 @@ enum Version {
   VP_Tunnel              = 0x01060000,
   /** V1.6.1.X : Correct SetValue support                        */
   VP_SetValue            = 0x01060100,
-  /** V1.6.1.X : Current version, use 'Plugin_Version()'         */
-  VP_CurrentVersion      = 0x01060100,
+  /** V1.6.2.X : 64 bit Java BLOB signature                      */
+  VP_BLOB_JSignature     = 0x01060200,
+/*VP_091221                0x01060200   * V1.6.2.X : Released 21-Dec-09 */
+  /** V1.7.0.X : Current version, use 'Plugin_Version()'         */
+  VP_CurrentVersion      = 0x01070000,
 
   /** -------- : Bad/undefined version                           */
   VP_BadVersion          = 0xffffffff,
@@ -250,6 +251,7 @@ enum Version {
 #define CA_JNI               "JNI"          /* Indicates, if plugin is based on JNI */
 #define CA_CSHARP            "C#"           /* Indicates, if plugin is based on C#  */
 #define CA_GUID              "GUID"         /* GUID */
+#define CA_Plugin            "PLUGIN"       /* Built-In plugin name */
 #define CA_GlobContext       "GlobContext"  /* The global context, if available */
 #define CA_ADMIN_Info        "ADMIN_Info"   /* Get ADMIN info as <name><SP>"ADMIN" with 'CreateContext' */
                                             /*   (supported for V1.3.7 and higher) */
@@ -283,13 +285,14 @@ enum Version {
 
 #define Plugin_UI            "plugin_ui"
 
+
 /* Compatibility to older versions */
-#define Plugin_Param_OLD     "plugin_param_OLD"
 #define Plugin_SE_Auth_OLD   "plugin_sessionauth_OLD"
 #define Plugin_DS_Data_OLD1  "plugin_datastore_OLD1"
 #define Plugin_DS_Data_OLD2  "plugin_datastore_OLD2"
 #define Plugin_DS_Admin_OLD  "plugin_datastoreadmin_OLD"
-#define Plugin_DS_Blob_OLD   "plugin_datablob_OLD"
+#define Plugin_DS_Blob_OLD1  "plugin_datablob_OLD1"
+#define Plugin_DS_Blob_OLD2  "plugin_datablob_OLD2"
 
 
 
@@ -374,6 +377,8 @@ enum DebugFlags {
   DBG_PLUGIN_EXOT  = 0x0004,
   /** direct printf calls for test */ 
   DBG_PLUGIN_DIRECT= 0x0008,
+  /** GetValue/SetValue debugging */
+  DBG_GET_SET_VALUE= 0x0010,
   /** Default mask: all bits set */
   DBG_PLUGIN_ALL   = 0xffff,
 };
diff --git a/src/synthesis/src/sysync_SDK/configs/README b/src/synthesis/src/sysync_SDK/configs/README
new file mode 100644 (file)
index 0000000..ea01f5c
--- /dev/null
@@ -0,0 +1,22 @@
+The sample configs contain common elements (datatypes, scripts, remote
+rules, debug settings) which are maintained as separate files in the
+corresponding directories. When modifying those common elements, run
+"update-samples.pl" in this directory to update the sample configs.
+
+The complete samples are under version control for several reasons:
+1. avoid dependency on Perl unless common elements need to be updated
+2. effect of changes on complete config show up in patches
+3. the file layout and unshared parts (<client> and <server>) are
+   determined by the sample configs
+
+The naming of common elements determines the order in which they get
+inserted. Files not ending in .xml are ignored. Elements that only
+apply to a client or server are stored in the corresponding sub
+directories, while the shared elements are in the
+"debug/scripting/datatypes/remoterules".
+
+It is a somewhat subjective choice which elements are stored in one
+file and which ones are split up. The three elements of a datatype
+definition (field list, profile, datatype) where split up because
+there might be multiple different profiles using the same field list
+and some users of these files might want to replace the default one.
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/00vcard-fieldlist.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/00vcard-fieldlist.xml
new file mode 100644 (file)
index 0000000..93f5792
--- /dev/null
@@ -0,0 +1,64 @@
+    <!-- list of internal fields representing vCard data -->
+    <fieldlist name="contacts">
+      <field name="SYNCLVL" type="integer" compare="never"/>
+      <field name="REV" type="timestamp" compare="never" age="yes"/>
+
+      <!-- Name elements -->
+      <field name="N_LAST" type="string" compare="always"/>
+      <field name="N_FIRST" type="string" compare="always"/>
+      <field name="N_MIDDLE" type="string" compare="always"/>
+      <field name="N_PREFIX" type="string" compare="conflict"/>
+      <field name="N_SUFFIX" type="string" compare="conflict"/>
+      <field name="NICKNAME" type="string" compare="conflict"/>
+      <field name="TITLE" type="string" compare="conflict" merge="fillempty"/>
+
+      <field name="FN" type="string" compare="conflict" merge="fillempty"/>
+
+      <!-- categories and classification -->
+      <field name="CATEGORIES" array="yes" type="string" compare="conflict"/>
+
+      <!-- organisation -->
+      <field name="ORG_NAME" type="string" compare="slowsync" merge="fillempty"/>
+      <field name="ORG_DIVISION" type="string" compare="conflict" merge="fillempty"/>
+
+      <!-- birthday -->
+      <field name="BDAY" type="date" compare="conflict" merge="fillempty"/>
+
+      <!-- telephone numbers -->
+      <field name="TEL"         array="yes" type="telephone" compare="conflict"/>
+      <field name="TEL_FLAGS"   array="yes" type="integer"   compare="conflict"/> <!-- offset 0 -->
+      <field name="TEL_LABEL"   array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
+      <field name="TEL_ID"      array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
+
+      <!-- emails -->
+      <field name="EMAIL"       array="yes" type="multiline" compare="conflict"/>
+      <field name="EMAIL_FLAGS" array="yes" type="integer"   compare="conflict"/> <!-- offset 0 -->
+      <field name="EMAIL_LABEL" array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
+      <field name="EMAIL_ID"    array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
+
+      <!-- web addresses -->
+      <field name="WEB"         array="yes" type="url" compare="conflict"/>
+      <field name="WEB_FLAGS"   array="yes" type="integer"   compare="conflict"/> <!-- offset 0 -->
+      <field name="WEB_LABEL"   array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
+      <field name="WEB_ID"      array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
+
+      <!-- home address -->
+      <field name="ADR_STREET"        array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_ADDTL"         array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_STREET_FLAGS"  array="yes" type="integer"   compare="conflict"/> <!-- offset 0 (from ADR_STREET_FLAGS) -->
+      <field name="ADR_STREET_LABEL"  array="yes" type="string"    compare="conflict"/> <!-- offset 1 -->
+      <field name="ADR_STREET_ID"     array="yes" type="integer"   compare="conflict"/> <!-- offset 2 -->
+      <field name="ADR_POBOX"         array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_CITY"          array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_REG"           array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_ZIP"           array="yes" type="multiline" compare="conflict"/>
+      <field name="ADR_COUNTRY"       array="yes" type="multiline" compare="conflict"/>
+
+      <!-- Note -->
+      <field name="NOTE" type="multiline" compare="conflict" merge="lines"/>
+
+      <!-- Photo -->
+      <field name="PHOTO" type="blob" compare="never" merge="fillempty"/>
+      <field name="PHOTO_TYPE" type="integer" compare="never" merge="fillempty"/>
+
+    </fieldlist>
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/01vcard-profile.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/01vcard-profile.xml
new file mode 100644 (file)
index 0000000..8c01221
--- /dev/null
@@ -0,0 +1,138 @@
+    <!-- vCard profile -->
+    <mimeprofile name="vCard" fieldlist="contacts">
+
+      <profile name="VCARD" nummandatory="0"> <!-- we allow records without "N" as Address book can store them -->
+        <property name="VERSION">
+          <value conversion="version"/>
+        </property>
+
+        <property onlyformode="standard" name="PRODID" mandatory="no">
+          <value conversion="prodid"/>
+        </property>
+
+        <property name="REV">
+          <value field="REV"/>
+        </property>
+
+        <property name="N" values="5" mandatory="yes"> <!-- Note: makes N parse and generate even if not in remote's CTCap -->
+          <value index="0" field="N_LAST"/>
+          <value index="1" field="N_FIRST"/>
+          <value index="2" field="N_MIDDLE"/>
+          <value index="3" field="N_PREFIX"/>
+          <value index="4" field="N_SUFFIX"/>
+        </property>
+
+        <property name="FN">
+          <value field="FN"/>
+        </property>
+
+        <property name="NICKNAME" onlyformode="standard">
+          <value field="NICKNAME"/>
+        </property>
+
+        <property name="TITLE">
+          <value field="TITLE"/>
+        </property>
+
+        <property name="CATEGORIES" values="list" valueseparator="," altvalueseparator=";" > <!-- non-standard, but 1:1 as in vCard 3.0 (NOT like in vCalendar 1.0, where separator is ";") -->
+          <value field="CATEGORIES"/>
+          <position field="CATEGORIES" repeat="array" increment="1" minshow="0"/>
+        </property>
+
+        <property name="ORG" values="2">
+          <value index="0" field="ORG_NAME"/>
+          <value index="1" field="ORG_DIVISION"/>
+        </property>
+
+        <property name="TEL">
+          <value field="TEL"/>
+          <position field="TEL" repeat="array" increment="1" minshow="1"/>
+          <parameter name="TYPE" default="yes" positional="no" show="yes">
+            <value field="TEL_FLAGS" conversion="multimix" combine=",">
+              <enum name="HOME"     value="B0"/>
+              <enum name="WORK"     value="B1"/>
+              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
+              <enum name="VOICE"    value="B3"/>
+              <enum name="CELL"     value="B4"/>
+              <enum name="FAX"      value="B5"/>
+              <enum name="PAGER"    value="B6"/>
+              <enum name="PREF"     value="B7"/>
+
+              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+            </value>
+          </parameter>
+        </property>
+
+        <property name="EMAIL">
+          <value field="EMAIL"/>
+          <position field="EMAIL" repeat="array" increment="1" minshow="1"/>
+          <parameter name="TYPE" default="yes" positional="no" show="yes">
+            <value field="EMAIL_FLAGS" conversion="multimix" combine=",">
+              <enum name="HOME"     value="B0"/>
+              <enum name="WORK"     value="B1"/>
+              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
+              <enum name="INTERNET" value="B3"/>
+
+              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+            </value>
+          </parameter>
+        </property>
+
+        <property name="URL">
+          <value field="WEB"/>
+          <position field="WEB" repeat="array" increment="1" minshow="1"/>
+          <parameter name="TYPE" default="yes" positional="no" show="yes">
+            <value field="WEB_FLAGS" conversion="multimix" combine=",">
+              <enum name="HOME"     value="B0"/>
+              <enum name="WORK"     value="B1"/>
+              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
+              <enum name="PREF"     value="B3"/>
+
+              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+            </value>
+          </parameter>
+        </property>
+
+        <property name="ADR" values="7">
+          <value index="0" field="ADR_POBOX"/>
+          <value index="1" field="ADR_ADDTL"/>
+          <value index="2" field="ADR_STREET"/>
+          <value index="3" field="ADR_CITY"/>
+          <value index="4" field="ADR_REG"/>
+          <value index="5" field="ADR_ZIP"/>
+          <value index="6" field="ADR_COUNTRY"/>
+          <position field="ADR_POBOX" repeat="array" increment="1" minshow="1"/>
+          <parameter name="TYPE" default="yes" positional="no" show="yes">
+            <value field="ADR_STREET_FLAGS" conversion="multimix" combine=",">
+              <enum name="HOME"     value="B0"/>
+              <enum name="WORK"     value="B1"/>
+              <enum mode="ignore"   value="B2"/> <!-- OTHER -->
+
+              <enum mode="prefix" name="X-CustomLabel-" value="1.L"/>
+              <enum mode="prefix" name="X-Synthesis-Ref" value="2.L"/>
+            </value>
+          </parameter>
+        </property>
+
+        <property name="BDAY">
+          <value field="BDAY"/>
+        </property>
+
+        <property name="NOTE" filter="no">
+          <value field="NOTE"/>
+        </property>
+
+        <property name="PHOTO" filter="no">
+          <value field="PHOTO" conversion="BLOB_B64"/>
+          <parameter name="TYPE" default="no" show="yes">
+            <value field="PHOTO_TYPE">
+              <enum name="JPEG" value="0"/>
+            </value>
+          </parameter>
+        </property>
+
+      </profile>
+    </mimeprofile>
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/02vcard-types.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/02vcard-types.xml
new file mode 100644 (file)
index 0000000..36515ad
--- /dev/null
@@ -0,0 +1,13 @@
+
+
+    <!-- vCard 2.1 datatype, using vCard profile defined above -->
+    <datatype name="vCard21" basetype="vcard">
+      <version>2.1</version>
+      <use mimeprofile="vCard"/>
+    </datatype>
+
+    <!-- vCard 3.0 datatype, using vCard profile defined above -->
+    <datatype name="vCard30" basetype="vcard">
+      <version>3.0</version>
+      <use mimeprofile="vCard"/>
+    </datatype>
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/10calendar-fieldlist.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/10calendar-fieldlist.xml
new file mode 100644 (file)
index 0000000..5858669
--- /dev/null
@@ -0,0 +1,65 @@
+    <!-- common field list for events and todos (both represented by vCalendar/iCalendar) -->
+    <fieldlist name="calendar">
+      <field name="SYNCLVL" type="integer" compare="never"/>
+      <field name="ISEVENT" type="integer" compare="always"/>
+
+      <field name="DMODIFIED" type="timestamp" compare="never" age="yes"/>
+      <field name="DCREATED" type="timestamp" compare="never"/>
+
+      <field name="DGENERATED" type="timestamp" compare="never"/>
+
+      <field name="UID" type="string" compare="never"/>
+
+      <field name="CATEGORIES" array="yes" type="string" compare="conflict" merge="fillempty"/>
+      <field name="CLASS" type="integer" compare="conflict" merge="fillempty"/>
+      <field name="TRANSP" type="integer" compare="conflict" merge="fillempty"/>
+
+      <field name="SUMMARY" type="multiline" compare="always"/>
+      <field name="DESCRIPTION" type="multiline" compare="slowsync" merge="lines"/>
+      <field name="LOCATION" type="multiline" compare="slowsync" merge="lines"/>
+
+      <!-- recurrence rule block, fields must be in that order, including
+           DTSTART as last field !! -->
+      <field name="RR_FREQ" type="string" compare="conflict"/>
+      <field name="RR_INTERVAL" type="integer" compare="conflict"/>
+      <field name="RR_FMASK" type="integer" compare="conflict"/>
+      <field name="RR_LMASK" type="integer" compare="conflict"/>
+      <field name="RR_END" type="timestamp" compare="conflict"/>
+
+      <!-- Note: DTSTART/DTEND are compared in the <comparescript>,
+                 therefore compare is set no "never" here -->
+      <field name="DTSTART" type="timestamp" compare="never"/>
+      <field name="DTEND" type="timestamp" compare="never"/>
+      <field name="DURATION" type="timestamp" compare="never"/>
+      <field name="COMPLETED" type="timestamp" compare="never"/>
+      <field name="DUE" type="timestamp" compare="never"/>
+
+      <field name="GEO_LAT" type="string" compare="never"/>
+      <field name="GEO_LONG" type="string" compare="never"/>
+
+      <field name="PRIORITY" type="integer" compare="conflict"/>
+      <field name="STATUS" type="integer" compare="conflict" merge="fillempty"/>
+
+      <field name="ALARM_TIME" type="timestamp" compare="conflict"/>
+      <field name="ALARM_SNOOZE" type="string" compare="conflict"/>
+      <field name="ALARM_REPEAT" type="string" compare="conflict"/>
+      <field name="ALARM_MSG" type="string" compare="conflict"/>
+      <field name="ALARM_ACTION" type="string" compare="conflict"/>
+      <field name="ALARM_REL" type="integer" compare="never"/>
+
+      <!-- non-standard -->
+      <field name="PARENT_UID" type="string" compare="never"/>
+
+      <!-- for events -->
+      <field name="EXDATES" array="yes" type="timestamp" compare="never"/>
+
+      <field name="ORIGSTART" array="no" type="timestamp" compare="never"/>
+      <field name="SEQNO" array="no" type="integer" compare="never"/>
+
+      <field name="ATTENDEES" array="yes" type="string" compare="never"/>
+      <field name="ATTENDEE_CNS" array="yes" type="string" compare="never"/>
+      <field name="ATTENDEE_PARTSTATS" array="yes" type="integer" compare="never"/>
+      <field name="ORGANIZER" array="no" type="string" compare="never"/>
+      <field name="ORGANIZER_CN" array="no" type="string" compare="never"/>
+
+    </fieldlist>
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/11calendar-profile.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/11calendar-profile.xml
new file mode 100644 (file)
index 0000000..b7d788a
--- /dev/null
@@ -0,0 +1,456 @@
+    <!-- vCalendar with VTODO and VEVENT variants -->
+    <mimeprofile name="vCalendar" fieldlist="calendar">
+
+      <vtimezonegenmode>current</vtimezonegenmode>
+      <tzidgenmode>olson</tzidgenmode>
+
+      <profile name="VCALENDAR" nummandatory="1">
+
+        <property name="VERSION" mandatory="yes">
+          <value conversion="version"/>
+        </property>
+
+        <property onlyformode="standard" name="PRODID" mandatory="no">
+          <value conversion="prodid"/>
+        </property>
+
+        <property onlyformode="old" name="TZ" filter="false" suppressempty="yes">
+          <value field="DTSTART" conversion="tz"/>
+        </property>
+
+        <property onlyformode="old" name="DAYLIGHT" mode="daylight" filter="false" suppressempty="yes">
+          <value field="DTSTART" conversion="daylight"/>
+        </property>
+
+        <property name="GEO" values="2" suppressempty="yes" onlyformode="old" valueseparator=",">
+          <!-- LON,LAT in vCalendar 1.0 -->
+          <value index="0" field="GEO_LAT"/>
+          <value index="1" field="GEO_LONG"/>
+        </property>
+
+        <subprofile onlyformode="standard" name="VTIMEZONE" mode="vtimezones"/>
+
+        <!-- sub-profile for tasks -->
+        <subprofile name="VTODO" nummandatory="1" showifselectedonly="yes" field="ISEVENT" value="0">
+
+          <property name="LAST-MODIFIED">
+            <value field="DMODIFIED"/>
+          </property>
+
+          <property name="DTSTAMP" suppressempty="yes" onlyformode="standard">
+            <value field="DGENERATED"/>
+          </property>
+
+          <property name="DCREATED" suppressempty="yes" onlyformode="old">
+            <value field="DCREATED"/>
+          </property>
+          <property name="CREATED" suppressempty="yes" onlyformode="standard">
+            <value field="DCREATED"/>
+          </property>
+
+          <property name="UID" suppressempty="yes">
+            <value field="UID"/>
+          </property>
+
+          <property name="SEQUENCE" suppressempty="yes">
+            <value field="SEQNO"/>
+          </property>
+
+          <property name="GEO" values="2" suppressempty="yes" onlyformode="standard" valueseparator=";">
+            <!-- LAT;LON in iCalendar 2.0 -->
+            <value index="0" field="GEO_LONG"/>
+            <value index="1" field="GEO_LAT"/>
+          </property>
+
+          <property onlyformode="standard" name="CATEGORIES" values="list" valueseparator="," suppressempty="yes">
+            <value field="CATEGORIES" />
+            <position field="CATEGORIES" repeat="array" minshow="0"/>
+          </property>
+
+          <property onlyformode="old" name="CATEGORIES" values="list" valueseparator=";" altvalueseparator="," suppressempty="yes">
+            <value field="CATEGORIES" />
+            <position field="CATEGORIES" repeat="array" minshow="0"/>
+          </property>
+
+          <property name="CLASS" suppressempty="yes">
+            <value field="CLASS">
+              <enum name="PUBLIC"       value="0"/>
+              <enum name="PRIVATE"      value="1"/>
+              <enum name="CONFIDENTIAL" value="2"/>
+            </value>
+          </property>
+
+          <property name="SUMMARY" mandatory="yes">
+            <value field="SUMMARY"/>
+          </property>
+
+          <property name="DESCRIPTION" mandatory="yes">
+            <value field="DESCRIPTION"/>
+          </property>
+
+          <property name="LOCATION" mandatory="no">
+            <value field="LOCATION"/>
+          </property>
+
+          <property name="DTSTART" suppressempty="yes" delayedparsing="1">
+            <value field="DTSTART" conversion="autodate"/>
+            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+              <value field="DTSTART" conversion="TZID"/>
+            </parameter>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+              <value field="DTSTART" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="COMPLETED" suppressempty="yes" delayedparsing="1">
+            <value field="COMPLETED" conversion="autoenddate"/>
+            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+              <value field="COMPLETED" conversion="TZID"/>
+            </parameter>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+              <value field="COMPLETED" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="DUE" suppressempty="yes" delayedparsing="1">
+            <value field="DUE" conversion="autodate"/>
+            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+              <value field="DUE" conversion="TZID"/>
+            </parameter>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+              <value field="DUE" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="PRIORITY" suppressempty="yes">
+            <value field="PRIORITY"/>
+          </property>
+
+          <property name="STATUS" onlyformode="standard" suppressempty="yes">
+            <value field="STATUS" conversion="emptyonly">
+              <enum name="COMPLETED"      value="0"/>
+              <enum name="NEEDS-ACTION"   value="1"/>
+              <enum name="IN-PROCESS"     value="2"/>
+              <enum name="CANCELLED"      value="3"/>
+              <enum name="ACCEPTED"       value="4"/>
+              <enum name="TENTATIVE"      value="5"/>
+              <enum name="DELEGATED"      value="6"/>
+              <enum name="DECLINED"       value="7"/>
+              <enum name="SENT"           value="8"/>
+              <enum name="CONFIRMED"      value="9"/>
+              <enum name="DRAFT"          value="10"/>
+              <enum name="FINAL"          value="11"/>
+            </value>
+          </property>
+
+          <property name="STATUS" onlyformode="old" suppressempty="yes">
+            <value field="STATUS" conversion="emptyonly">
+              <enum name="COMPLETED"      value="0"/>
+              <enum name="NEEDS ACTION"   value="1"/>
+              <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
+              <enum name="IN PROCESS"     value="2"/>
+              <enum name="CANCELLED"      value="3"/>
+              <enum name="ACCEPTED"       value="4"/>
+              <enum name="TENTATIVE"      value="5"/>
+              <enum name="DELEGATED"      value="6"/>
+              <enum name="DECLINED"       value="7"/>
+              <enum name="SENT"           value="8"/>
+              <enum name="CONFIRMED"      value="9"/>
+              <enum name="DRAFT"          value="10"/>
+              <enum name="FINAL"          value="11"/>
+            </value>
+          </property>
+
+
+          <!-- AALARM and DALARM both use the same fields -->
+          <property name="AALARM" onlyformode="old" values="4" suppressempty="yes">
+            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+          </property>
+          <property name="DALARM" onlyformode="old" values="4" suppressempty="yes">
+            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+          </property>
+
+          <subprofile onlyformode="standard" name="VALARM" nummandatory="1" field="ALARM_TIME">
+            <property name="TRIGGER" suppressempty="no" mandatory="yes">
+              <value field="ALARM_TIME"/>
+              <parameter name="VALUE" default="no" show="yes">
+                <value field="ALARM_TIME" conversion="FULLVALUETYPE"/>
+              </parameter>
+              <parameter name="RELATED" default="no" show="yes">
+                <value field="ALARM_REL">
+                  <enum mode="ignore" value="0"/>
+                  <enum name="START" value="1"/>
+                  <enum name="END"   value="2"/>
+                </value>
+              </parameter>
+            </property>
+            <property name="ACTION" suppressempty="yes" mandatory="yes">
+              <value field="ALARM_ACTION"/>
+            </property>
+            <property name="DESCRIPTION" suppressempty="yes">
+              <value field="ALARM_MSG"/>
+            </property>
+            <property name="REPEAT" suppressempty="yes">
+              <value field="ALARM_REPEAT"/>
+            </property>
+          </subprofile>
+
+          <property onlyformode="old" name="RELATED-TO" suppressempty="yes">
+            <value field="PARENT_UID"/>
+          </property>
+
+          <property onlyformode="standard" name="RELATED-TO" suppressempty="yes">
+            <value field="PARENT_UID"/>
+            <parameter onlyformode="standard" name="RELTYPE" default="no" positional="yes" show="yes">
+              <value>
+                <enum name="PARENT"/>
+                <enum mode="defaultvalue" name="other"/>
+              </value>
+              <position hasnot="other" shows="PARENT" field="PARENT_UID"/>
+            </parameter>
+          </property>
+
+        </subprofile>
+
+        <!-- sub-profile for event -->
+        <subprofile name="VEVENT" nummandatory="1" showifselectedonly="yes" field="ISEVENT" value="1">
+
+          <property name="LAST-MODIFIED">
+            <value field="DMODIFIED"/>
+          </property>
+
+          <property name="DTSTAMP" suppressempty="yes" onlyformode="standard">
+            <value field="DGENERATED"/>
+          </property>
+
+          <property name="DCREATED" suppressempty="yes" onlyformode="old">
+            <value field="DCREATED"/>
+          </property>
+          <property name="CREATED" suppressempty="yes" onlyformode="standard">
+            <value field="DCREATED"/>
+          </property>
+
+
+          <property name="UID" suppressempty="yes">
+            <value field="UID"/>
+          </property>
+
+          <property name="SEQUENCE" suppressempty="yes">
+            <value field="SEQNO"/>
+          </property>
+
+          <property name="GEO" values="2" suppressempty="yes" onlyformode="standard" valueseparator=";">
+            <!-- LAT;LON in iCalendar 2.0 -->
+            <value index="0" field="GEO_LONG"/>
+            <value index="1" field="GEO_LAT"/>
+          </property>
+
+          <property onlyformode="standard" name="CATEGORIES" values="list" valueseparator="," suppressempty="yes">
+            <value field="CATEGORIES" />
+            <position field="CATEGORIES" repeat="array" minshow="0"/>
+          </property>
+
+          <property onlyformode="old" name="CATEGORIES" values="list" valueseparator=";" altvalueseparator="," suppressempty="yes">
+            <value field="CATEGORIES" />
+            <position field="CATEGORIES" repeat="array" minshow="0"/>
+          </property>
+
+          <property name="CLASS" suppressempty="yes">
+            <value field="CLASS">
+              <enum name="PUBLIC"       value="0"/>
+              <enum name="PRIVATE"      value="1"/>
+              <enum name="CONFIDENTIAL" value="2"/>
+            </value>
+          </property>
+
+
+          <property name="TRANSP" suppressempty="yes" onlyformode="standard">
+            <value field="TRANSP">
+              <enum name="OPAQUE"       value="0"/>
+              <enum name="TRANSPARENT"  value="1"/>
+              <enum name="TENTATIVE"     value="2"/> <!-- according to Oracle de facto usage in vCalendar 1.0 -->
+              <enum name="OUT_OF_OFFICE" value="3"/> <!-- according to Oracle de facto usage in vCalendar 1.0 -->
+              <enum mode="defaultvalue" value="0"/>
+            </value>
+          </property>
+          <property name="TRANSP" suppressempty="yes" onlyformode="old">
+            <value field="TRANSP"/> <!-- directly numeric in vCalendar 1.0 -->
+          </property>
+
+
+          <property name="PRIORITY" suppressempty="yes">
+            <value field="PRIORITY"/>
+          </property>
+
+          <property name="SUMMARY" mandatory="yes">
+            <value field="SUMMARY"/>
+          </property>
+
+          <property name="DESCRIPTION" mandatory="yes">
+            <value field="DESCRIPTION"/>
+          </property>
+
+          <property name="LOCATION" mandatory="no">
+            <value field="LOCATION"/>
+          </property>
+
+          <property name="DTSTART" suppressempty="yes" delayedparsing="1">
+            <value field="DTSTART" conversion="autodate"/>
+            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+              <value field="DTSTART" conversion="TZID"/>
+            </parameter>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+              <value field="DTSTART" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <!-- recurrence rule (with delayed parsing, as it is dependent on DTSTART) -->
+          <property name="RRULE" suppressempty="yes" delayedparsing="2">
+            <!-- Note: RR_FREQ is the beginning of a block of fields
+                 suitable for the "rrule" conversion mode -->
+            <value field="RR_FREQ" conversion="rrule"/>
+          </property>
+
+          <!-- Symbian uses this, so it might make the client work with symbian-prepared servers better -->
+          <property name="X-RECURRENCE-ID" suppressempty="yes" onlyformode="old">
+            <value field="ORIGSTART" conversion="autodate"/>
+          </property>
+
+          <property name="RECURRENCE-ID" suppressempty="yes" onlyformode="standard" delayedparsing="1">
+            <value field="ORIGSTART" conversion="autodate"/>
+            <parameter name="TZID" default="no" show="yes">
+              <value field="ORIGSTART" conversion="TZID"/>
+            </parameter>
+            <parameter name="VALUE" default="no" show="yes">
+              <value field="ORIGSTART" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="EXDATE" values="list" suppressempty="yes" onlyformode="standard" delayedparsing="1" valueseparator="," altvalueseparator=";">
+            <value field="EXDATES"/>
+            <position field="EXDATES" repeat="array" increment="1" minshow="0"/>
+            <parameter name="TZID" default="no" show="yes">
+              <value field="EXDATES" conversion="TZID"/>
+            </parameter>
+          </property>
+
+          <property name="EXDATE" values="list" suppressempty="yes" onlyformode="old" delayedparsing="1" valueseparator=";" altvalueseparator=",">
+            <value field="EXDATES"/>
+            <position field="EXDATES" repeat="array" increment="1" minshow="0"/>
+          </property>
+
+
+          <property name="DTEND" suppressempty="yes" delayedparsing="1">
+            <value field="DTEND" conversion="autoenddate"/>
+            <parameter onlyformode="standard" name="TZID" default="no" show="yes">
+              <value field="DTEND" conversion="TZID"/>
+            </parameter>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="yes">
+              <value field="DTEND" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="DURATION" suppressempty="yes" delayedparsing="1" onlyformode="standard">
+            <value field="DURATION"/>
+            <parameter onlyformode="standard" name="VALUE" default="no" show="no">
+              <value field="DURATION" conversion="VALUETYPE"/>
+            </parameter>
+          </property>
+
+          <property name="ATTENDEE" suppressempty="yes" onlyformode="old">
+            <value field="ATTENDEES"/>
+            <parameter name="ROLE" default="no" positional="yes" show="yes">
+              <value>
+                <enum name="ORGANIZER"/>
+              </value>
+              <position has="ORGANIZER" field="ORGANIZER" overwriteempty="yes"/>
+              <position hasnot="ORGANIZER" field="ATTENDEES" repeat="array" increment="1" overwriteempty="yes"/>
+            </parameter>
+            <parameter name="STATUS" default="no" show="yes">
+              <value field="ATTENDEE_PARTSTATS">
+                <enum name="NEEDS ACTION"   value="1"/>
+                <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
+                <enum name="ACCEPTED"       value="4"/>
+                <enum name="DECLINED"       value="7"/>
+                <enum name="TENTATIVE"      value="5"/>
+                <enum name="DELEGATED"      value="6"/>
+              </value>
+            </parameter>
+          </property>
+
+          <property name="ATTENDEE" suppressempty="yes" onlyformode="standard">
+            <value field="ATTENDEES" conversion="mailto"/>
+            <position field="ATTENDEES" repeat="array" increment="1" minshow="0"/>
+            <parameter name="CN" default="no" show="yes" shownonempty="yes">
+              <value field="ATTENDEE_CNS"/>
+            </parameter>
+            <parameter name="PARTSTAT" default="no" show="yes">
+              <value field="ATTENDEE_PARTSTATS">
+                <enum name="NEEDS-ACTION"   value="1"/>
+                <enum mode="defaultvalue"   value="1"/> <!-- catch unknown, but also non-conformant NEEDS_ACTION -->
+                <enum name="ACCEPTED"       value="4"/>
+                <enum name="DECLINED"       value="7"/>
+                <enum name="TENTATIVE"      value="5"/>
+                <enum name="DELEGATED"      value="6"/>
+              </value>
+            </parameter>
+          </property>
+
+          <property name="ORGANIZER" suppressempty="yes" onlyformode="standard">
+            <value field="ORGANIZER" conversion="mailto"/>
+            <parameter name="CN" default="no" show="yes">
+              <value field="ORGANIZER_CN"/>
+            </parameter>
+          </property>
+
+
+          <!-- AALARM and DALARM both use the same fields -->
+          <property name="AALARM" onlyformode="old" values="4" suppressempty="yes">
+            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+          </property>
+          <property name="DALARM" onlyformode="old" values="4" suppressempty="yes">
+            <value index="0" field="ALARM_TIME" conversion="emptyonly"/>
+            <value index="1" field="ALARM_SNOOZE" conversion="emptyonly"/>
+            <value index="2" field="ALARM_REPEAT" conversion="emptyonly"/>
+            <value index="3" field="ALARM_MSG" conversion="emptyonly"/>
+          </property>
+
+          <subprofile onlyformode="standard" name="VALARM" nummandatory="1" field="ALARM_TIME">
+            <property name="TRIGGER" suppressempty="no" mandatory="yes">
+              <value field="ALARM_TIME"/>
+              <parameter name="VALUE" default="no" show="yes">
+                <value field="ALARM_TIME" conversion="FULLVALUETYPE"/>
+              </parameter>
+              <parameter name="RELATED" default="no" show="yes">
+                <value field="ALARM_REL">
+                  <enum mode="ignore" value="0"/>
+                  <enum name="START" value="1"/>
+                  <enum name="END"   value="2"/>
+                </value>
+              </parameter>
+            </property>
+            <property name="ACTION" suppressempty="yes" mandatory="yes">
+              <value field="ALARM_ACTION"/>
+            </property>
+            <property name="DESCRIPTION" suppressempty="yes">
+              <value field="ALARM_MSG"/>
+            </property>
+            <property name="REPEAT" suppressempty="yes">
+              <value field="ALARM_REPEAT"/>
+            </property>
+          </subprofile>
+
+        </subprofile>
+
+      </profile>
+    </mimeprofile>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/12calendar-types.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/12calendar-types.xml
new file mode 100644 (file)
index 0000000..15c64c0
--- /dev/null
@@ -0,0 +1,31 @@
+    <!-- vCalendar 1.0 datatype, using vCalendar profile defined above -->
+    <datatype name="vCalendar10" basetype="vcalendar">
+      <version>1.0</version>
+      <use mimeprofile="vCalendar"/>
+
+      <incomingscript><![CDATA[
+        $VCALENDAR_INCOMING_SCRIPT
+      ]]></incomingscript>
+
+      <outgoingscript><![CDATA[
+        $VCALENDAR_OUTGOING_SCRIPT
+      ]]></outgoingscript>
+
+    </datatype>
+
+
+    <!-- iCalendar 2.0 datatype, using vCalendar profile defined above -->
+    <datatype name="iCalendar20" basetype="vcalendar">
+      <version>2.0</version>
+      <use mimeprofile="vCalendar"/>
+
+      <incomingscript><![CDATA[
+        $VCALENDAR_INCOMING_SCRIPT
+      ]]></incomingscript>
+
+      <outgoingscript><![CDATA[
+        $VCALENDAR_OUTGOING_SCRIPT
+      ]]></outgoingscript>
+
+    </datatype>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/20note-fieldlist.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/20note-fieldlist.xml
new file mode 100644 (file)
index 0000000..8eda2a5
--- /dev/null
@@ -0,0 +1,7 @@
+    <!-- list of internal fields representing plain text note data -->
+    <fieldlist name="Note">
+      <field name="SYNCLVL" type="integer" compare="never"/>
+      <field name="SUBJECT" type="multiline" compare="always"/>
+      <field name="TEXT" type="multiline" compare="conflict" merge="lines"/>
+    </fieldlist>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/21note-profile.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/21note-profile.xml
new file mode 100644 (file)
index 0000000..8f5eb8c
--- /dev/null
@@ -0,0 +1,14 @@
+    <textprofile name="Note" fieldlist="Note">
+      <linemap field="SUBJECT">
+        <numlines>1</numlines>
+        <inheader>false</inheader>
+        <allowempty>true</allowempty>
+        <filterkeyword>SUBJECT</filterkeyword>
+      </linemap>
+      <linemap field="TEXT">
+        <numlines>0</numlines>
+        <inheader>false</inheader>
+        <allowempty>true</allowempty>
+      </linemap>
+    </textprofile>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/22notes-types.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/22notes-types.xml
new file mode 100644 (file)
index 0000000..ebd1dfd
--- /dev/null
@@ -0,0 +1,12 @@
+    <datatype name="note10" basetype="text">
+      <use profile="Note"/>
+      <typestring>text/plain</typestring>
+      <versionstring>1.0</versionstring>
+    </datatype>
+
+    <datatype name="note11" basetype="text">
+      <use profile="Note"/>
+      <typestring>text/plain</typestring>
+      <versionstring>1.1</versionstring>
+    </datatype>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/30bookmark-fieldlist.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/30bookmark-fieldlist.xml
new file mode 100644 (file)
index 0000000..cde7c53
--- /dev/null
@@ -0,0 +1,20 @@
+    <!-- list of internal fields representing vBookmark data -->
+    <fieldlist name="bookmarks">
+      <field name="REV" type="timestamp" compare="never" age="yes"/>
+      <field name="SYNCLVL" type="integer" compare="never"/>
+
+      <!-- Name -->
+      <field name="TITLE" type="string" compare="always"/>
+
+      <!-- categories and classification -->
+      <field name="CATEGORIES" type="string" compare="conflict" merge="fillempty"/>
+      <field name="CLASS" type="string" compare="conflict" merge="fillempty"/>
+
+      <!-- web addresses -->
+      <field name="URL" type="url" compare="slowsync" merge="fillempty"/>
+
+      <!-- Note -->
+      <field name="NOTE" type="multiline" compare="conflict" merge="lines"/>
+
+    </fieldlist>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/31bookmark-profile.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/31bookmark-profile.xml
new file mode 100644 (file)
index 0000000..1f9b73a
--- /dev/null
@@ -0,0 +1,38 @@
+    <!-- vBookmark profile -->
+    <mimeprofile name="vBookmark" fieldlist="bookmarks">
+
+      <profile name="VBKM" nummandatory="0">
+        <property name="VERSION">
+          <value conversion="version"/>
+        </property>
+
+        <property name="X-LAST-MODIFIED">
+          <value field="REV"/>
+        </property>
+
+        <property name="TITLE">
+          <value field="TITLE"/>
+        </property>
+
+        <property name="URL">
+          <value field="URL"/>
+        </property>
+
+        <!-- non-standard properties -->
+
+        <!-- inherit CATEGORIES from vCard 3.0, i.e. comma separated -->
+        <property name="CATEGORIES" values="list" valueseparator="," altvalueseparator=";">
+          <value field="CATEGORIES" combine=","/>
+        </property>
+
+        <property name="CLASS" suppressempty="yes">
+          <value field="CLASS"/>
+        </property>
+
+        <property name="NOTE" filter="no">
+          <value field="NOTE"/>
+        </property>
+
+      </profile>
+    </mimeprofile>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/32bookmark-type.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/32bookmark-type.xml
new file mode 100644 (file)
index 0000000..e7142ac
--- /dev/null
@@ -0,0 +1,7 @@
+    <!-- vBookmark datatype, using vBookmark profile defined above -->
+    <datatype name="vBookmark10" basetype="mimedir">
+      <typestring>text/x-vbookmark</typestring>
+      <versionstring>1.0</versionstring>
+      <use profile="vBookmark"/>
+    </datatype>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/server/40email-fieldlist.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/server/40email-fieldlist.xml
new file mode 100644 (file)
index 0000000..e3fb6a3
--- /dev/null
@@ -0,0 +1,24 @@
+    <!-- list of internal fields representing email data -->
+    <fieldlist name="email">
+      <field name="SYNCLVL" type="integer" compare="never"/>
+      <field name="MODIFIED" type="timestamp" compare="never" age="yes"/>
+      <field name="SENDER" type="multiline" compare="always"/>
+      <field name="RECEIVER" type="multiline" compare="always"/>
+      <field name="CARBONCOPY" type="multiline" compare="always"/>
+      <field name="BLINDCARBONCOPY" type="multiline" compare="always"/>
+      <field name="REPLY_TO" type="multiline" compare="never"/>
+      <field name="SUBJECT" type="multiline" compare="always"/>
+      <field name="PRIORITY" type="integer" compare="never"/>
+      <field name="MAILDATE" type="timestamp" compare="never"/>
+      <field name="STATUS" type="string" compare="never"/>
+      <field name="FOLDER" type="string" compare="conflict"/>
+      <field name="ISREAD" type="string" compare="never"/>
+      <field name="LIMIT" type="integer" compare="never"/>
+      <field name="BODY" type="multiline" compare="never"/>
+      <field name="ATT_COUNT" type="integer" compare="never"/>
+      <field name="ATT_NAMES" array="yes" type="string" compare="never"/>
+      <field name="ATT_MIMETYPES" array="yes" type="string" compare="never"/>
+      <field name="ATT_SIZES" array="yes" type="integer" compare="never"/>
+      <field name="ATT_CONTENTS" array="yes" type="blob" compare="never"/>
+    </fieldlist>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/server/41email-profile.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/server/41email-profile.xml
new file mode 100644 (file)
index 0000000..9f52d7b
--- /dev/null
@@ -0,0 +1,96 @@
+    <!-- this is the text profile used to generate and decode RFC2822/MIME-Multipart
+         email messages. -->
+    <textprofile name="rfc2822_email" fieldlist="email">
+
+      <mimemail>true</mimemail>
+      <!-- attachment configuration -->
+      <maxattachments>100</maxattachments>
+      <attachmentcountfield>ATT_COUNT</attachmentcountfield>
+      <attachmentmimetypesfield>ATT_MIMETYPES</attachmentmimetypesfield>
+      <attachmentsfield>ATT_CONTENTS</attachmentsfield>
+      <attachmentsizesfield>ATT_SIZES</attachmentsizesfield>
+      <attachmentnamesfield>ATT_NAMES</attachmentnamesfield>
+      <sizelimitfield>LIMIT</sizelimitfield>
+
+
+      <linemap field="SENDER">
+        <headertag>From:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+        <filterkeyword>FROM</filterkeyword>
+      </linemap>
+
+      <linemap field="RECEIVER">
+        <headertag>To:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+        <filterkeyword>TO</filterkeyword>
+      </linemap>
+
+      <linemap field="CARBONCOPY">
+        <headertag>Cc:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+        <filterkeyword>CC</filterkeyword>
+      </linemap>
+
+      <linemap field="BLINDCARBONCOPY">
+        <headertag>Bcc:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+        <filterkeyword>BCC</filterkeyword>
+      </linemap>
+
+      <linemap field="REPLY_TO">
+        <headertag>Reply-To:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="SUBJECT">
+        <headertag>Subject:</headertag>
+        <valuetype>rfc2047</valuetype>
+        <inheader>true</inheader>
+        <filterkeyword>SUBJECT</filterkeyword>
+      </linemap>
+
+      <linemap field="PRIORITY">
+        <headertag>X-Priority:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="MAILDATE">
+        <valuetype>date</valuetype>
+        <headertag>Date:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="STATUS">
+        <headertag>Status:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="FOLDER">
+        <headertag>X-Sync-Parent-Folder:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="ISREAD">
+        <headertag>X-Sync-Message-Read:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="MODIFIED">
+        <!-- note that this is a ISO8601 date -->
+        <headertag>X-Sync-Lastmodified:</headertag>
+        <inheader>true</inheader>
+      </linemap>
+
+      <linemap field="BODY">
+        <valuetype>body</valuetype>
+        <numlines>0</numlines>
+        <inheader>false</inheader>
+        <allowempty>true</allowempty>
+      </linemap>
+    </textprofile>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/server/42email-type-zipped.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/server/42email-type-zipped.xml
new file mode 100644 (file)
index 0000000..c560f0d
--- /dev/null
@@ -0,0 +1,43 @@
+    <!-- Note: This is a proprietary extension datatype for Synthesis AG Windows Mobile SyncML clients.
+         This format is a compressed form of the standard RFC2822 format. For one, the entire data
+         is compressed using the zip algorithm (<zippedbindata>), and secondly attachments are included
+         in binary form in the RFC2822 data stream rather than bandwidth wasting B64, adding a
+         "Content-Length:" header for each MIME part (<binaryparts>). -->
+
+    <datatype name="email_zipbin" basetype="text">
+      <use profile="rfc2822_email"/>
+      <typestring>application/x-zip-message</typestring> <!-- our own private zipped binary optimized format -->
+      <versionstring>1.1</versionstring>
+      <binaryparts>yes</binaryparts>
+      <zippedbindata>yes</zippedbindata>
+      <zipcompressionlevel>9</zipcompressionlevel> <!-- -1=default, 0=no compression, 1=fast & least effective ... 9=slow and most effective -->
+
+      <initscript><![CDATA[
+        $EMAIL_INIT_SCRIPT
+      ]]></initscript>
+
+      <processitemscript><![CDATA[
+        $EMAIL_PROCESSITEM_SCRIPT
+      ]]></processitemscript>
+
+
+      <mergescript><![CDATA[
+        $EMAIL_MERGE_SCRIPT
+      ]]></mergescript>
+
+
+      <outgoingscript><![CDATA[
+        $EMAIL_OUTGOING_SCRIPT
+      ]]></outgoingscript>
+
+      <filterinitscript><![CDATA[
+        $EMAIL_FILTERINIT_SCRIPT
+      ]]></filterinitscript>
+
+
+      <filterscript><![CDATA[
+        $EMAIL_FILTER_SCRIPT
+      ]]></filterscript>
+
+    </datatype>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/server/42email-type.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/server/42email-type.xml
new file mode 100644 (file)
index 0000000..1dcdf74
--- /dev/null
@@ -0,0 +1,34 @@
+    <datatype name="email" basetype="text">
+      <use profile="rfc2822_email"/>
+      <typestring>text/message</typestring> <!-- this is P800-like -->
+      <versionstring>1.0</versionstring>
+
+      <initscript><![CDATA[
+        $EMAIL_INIT_SCRIPT
+      ]]></initscript>
+
+      <processitemscript><![CDATA[
+        $EMAIL_PROCESSITEM_SCRIPT
+      ]]></processitemscript>
+
+
+      <mergescript><![CDATA[
+        $EMAIL_MERGE_SCRIPT
+      ]]></mergescript>
+
+
+      <outgoingscript><![CDATA[
+        $EMAIL_OUTGOING_SCRIPT
+      ]]></outgoingscript>
+
+      <filterinitscript><![CDATA[
+        $EMAIL_FILTERINIT_SCRIPT
+      ]]></filterinitscript>
+
+
+      <filterscript><![CDATA[
+        $EMAIL_FILTER_SCRIPT
+      ]]></filterscript>
+
+    </datatype>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/server/43email-sonyericsson.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/server/43email-sonyericsson.xml
new file mode 100644 (file)
index 0000000..af492df
--- /dev/null
@@ -0,0 +1,34 @@
+    <datatype name="email_sonyericsson" basetype="text">
+      <use profile="rfc2822_email"/>
+      <typestring>message/rfc822</typestring> <!-- this is M600i/P990-like -->
+      <versionstring>1.0</versionstring>
+
+      <initscript><![CDATA[
+        $EMAIL_INIT_SCRIPT
+      ]]></initscript>
+
+      <processitemscript><![CDATA[
+        $EMAIL_PROCESSITEM_SCRIPT
+      ]]></processitemscript>
+
+
+      <mergescript><![CDATA[
+        $EMAIL_MERGE_SCRIPT
+      ]]></mergescript>
+
+
+      <outgoingscript><![CDATA[
+        $EMAIL_OUTGOING_SCRIPT
+      ]]></outgoingscript>
+
+      <filterinitscript><![CDATA[
+        $EMAIL_FILTERINIT_SCRIPT
+      ]]></filterinitscript>
+
+
+      <filterscript><![CDATA[
+        $EMAIL_FILTER_SCRIPT
+      ]]></filterscript>
+
+    </datatype>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/datatypes/server/44email-nokia9500.xml b/src/synthesis/src/sysync_SDK/configs/datatypes/server/44email-nokia9500.xml
new file mode 100644 (file)
index 0000000..e9f1750
--- /dev/null
@@ -0,0 +1,135 @@
+    <datatype name="email_nokia9500" basetype="text">
+      <use profile="rfc2822_email"/>
+      <typestring>message/x-rfc822</typestring> <!-- this is Nokia 9500/9300-like -->
+      <versionstring>1.0</versionstring>
+
+      <initscript><![CDATA[
+        INTEGER ITEMLIMIT;
+        // default limit is limit of session
+        ITEMLIMIT = SIZELIMIT();
+      ]]></initscript>
+
+      <processitemscript><![CDATA[
+        INTEGER n;
+
+        // For Nokia Email, we must derive folder ID from source LocURI
+        // which has form "./somestrangenumber/folder/itemid"
+        // where folder can be "Inbox" or "Outbox"
+        if (FIND(REMOTEID(),"Inbox",0)!=UNASSIGNED) {
+          FOLDER="INBOX";
+        }
+        else if (FIND(REMOTEID(),"Outbox",0)!=UNASSIGNED) {
+          FOLDER="OUTBOX";
+        }
+        // pre-process item
+        if (UPPERCASE(FOLDER)=="INBOX") {
+          // In any case, prevent adding to inbox (delete remote items instead)
+          PREVENTADD();
+          // server always wins for inbox
+          CONFLICTSTRATEGY("server-wins");
+          if (SLOWSYNC()) {
+            // also prevent modifications in server
+            IGNOREUPDATE();
+          }
+          else {
+            // normal sync items going to inbox from client need special treatment
+            if (SYNCOP()=="add" || SYNCOP()=="replace") {
+              // make sure that existing server item will conflict with this item
+              if (LIMIT!=EMPTY && (LIMIT<0 || LIMIT>SIZELIMIT())) {
+                // force conflict only if this is a reload
+                FORCECONFLICT();
+              }
+              // make sure we never overwrite a body in the inbox
+              BODY = UNASSIGNED;
+              // delete always wins over replace in inbox (to avoid adds to inbox)
+              DELETEWINS();
+            }
+          }
+        }
+        else if (UPPERCASE(FOLDER)=="OUTBOX") {
+          // never try to change something in outbox
+          IGNOREUPDATE();
+          if (SYNCOP()!="delete") {
+            // - date of mail is NOW, set it such that a correct date is written to the DB
+            MAILDATE = DBNOW();
+            // MAILDATE = (INTEGER)DBNOW() - TIMEUNITS(120); // %%% backdate it 2 mins to make sure it does not get retransmitted
+            // - echo item as delete (this causes that it is moved to the "sent" folder in the 9500)
+            ECHOITEM("delete");
+          }
+          CONFLICTSTRATEGY("client-wins");
+        }
+        else {
+          // Other folder
+          // - silently discard incoming item for other folder than the above
+          //   except if it is a delete
+          if (SYNCOP()!="delete")
+            REJECTITEM(0);
+        }
+      ]]></processitemscript>
+
+
+      <mergescript><![CDATA[
+        // pre-process item
+        if (UPPERCASE(LOOSING.FOLDER)!="OUTBOX") {
+          // non-outbox (especially inbox) needs special merge to accomplish reload feature
+          // - loosing item is client's, winning is server's
+          if (LOOSING.LIMIT!=EMPTY) {
+            // loosing (remote) item specifies a new limit, override winning's default
+            WINNING.LIMIT=LOOSING.LIMIT;
+            SETWINNINGCHANGED(TRUE);
+          }
+          // make sure winning has right folder
+          WINNING.FOLDER=LOOSING.FOLDER;
+          // make sure a set read-flag gets propagated to server
+          if (LOOSING.ISREAD=="true") WINNING.ISREAD="true";
+          // merge other fields normally
+          MERGEFIELDS();
+          // make sure body does not get re-written to local DB even if merge would cause local update
+          LOOSING.BODY=UNASSIGNED;
+        }
+        else {
+          // normal merging in other folders
+          MERGEFIELDS();
+        }
+      ]]></mergescript>
+
+
+      <outgoingscript><![CDATA[
+        // we can only send to inbox or outbox
+        // - If we have no remote ID (=add command) prepare special Target item ID
+        //   containing target folder.
+        if (REMOTEID()==EMPTY) {
+        if (UPPERCASE(FOLDER)=="INBOX") {
+            SETREMOTEID(REMOTEDBNAME()+"/Inbox/");
+        }
+        else if (UPPERCASE(FOLDER)=="OUTBOX") {
+            SETREMOTEID(REMOTEDBNAME()+"/Outbox/");
+          }
+        }
+      ]]></outgoingscript>
+
+      <filterinitscript><![CDATA[
+        // check if we need to filter
+        INTEGER NEEDFILTER;
+
+        NEEDFILTER =
+          !DBHANDLESOPTS() && // only if DB cannot handle it
+          (STARTDATE()!=EMPTY); // and only if a start date is set (end date not needed as there are never future emails today)
+        SETFILTERALL(NEEDFILTER);
+        RETURN NEEDFILTER;
+      ]]></filterinitscript>
+
+
+      <filterscript><![CDATA[
+        INTEGER PASSES;
+
+        // check if item passes filter
+        PASSES=FALSE;
+        // Filter out anything not for Inbox or Outbox
+        if (UPPERCASE(FOLDER)!="INBOX" && UPPERCASE(FOLDER)!="OUTBOX") RETURN FALSE;
+        // Emails pass if they have a MAILDATE on or later than start date
+        PASSES = MAILDATE>=STARTDATE();
+        RETURN PASSES;
+      ]]></filterscript>
+
+    </datatype>
diff --git a/src/synthesis/src/sysync_SDK/configs/debug/00default.xml b/src/synthesis/src/sysync_SDK/configs/debug/00default.xml
new file mode 100644 (file)
index 0000000..5b24c4c
--- /dev/null
@@ -0,0 +1,31 @@
+    <!-- path where logfiles are stored -->
+    <!-- <logpath platform="linux">/your/log/directory</logpath> -->
+    <logflushmode>buffered</logflushmode> <!-- buffered is fastest mode, but may loose data on process abort. Other options: "flush" (after every line) or "openclose" (safest, slowest, like in 2.x server) -->
+    <!-- per session log -->
+    <sessionlogs>yes</sessionlogs> <!-- by default, create a session log file for every sync session (might be disabled for special users/devices in scripts) -->
+    <!-- debug format options -->
+    <logformat>html</logformat> <!-- html is nicely colored and easily viewable with a web browser. Other options: "xml", "text" -->
+    <folding>auto</folding> <!-- dynamic folding of blocks enabled, automatically expanded or collapsed default. Other options: "none", "expanded", "collapsed" -->
+    <timestamp>yes</timestamp> <!-- show timestamps for structure elements in log -->
+    <timestampall>no</timestampall> <!-- don't show timestamp for every log line -->
+    <timedsessionlognames>yes</timedsessionlognames> <!-- session logs also have the session start timestamp in the filename - makes them more easily sortable -->
+    <!-- thread logging mode -->
+    <subthreadmode>separate</subthreadmode> <!-- write log info from subthreads into separate log files. Other options: "suppress" -->
+    <!-- basic debug level selection -->
+    <enable option="extended"/> <!-- "extended" is a good choice for start testing. For production, use "normal" or "minimal" -->
+    <!-- <enable option="normal"/> --> <!-- "normal" provides rich debug info, but still in reasonable size  -->
+    <!-- <enable option="minimal"/> --> <!-- "minimal" just shows basic flow and error. Not suitable for debugging -->
+    <!-- <enable option="maximal"/> --> <!-- "maximal" can create VERY LARGE logs and cause HEAVY SLOWDOWN. Only for detail debugging  -->
+    <!-- <enable option="all"/> --> <!-- "all" shows EVERYTHING possible, and way too much for any normal situation. For hardcore debugging ONLY! -->
+    <!-- additional debug info switches -->
+    <enable option="userdata"/> <!-- Make this <disable ...> if you don't want user data in the logs -->
+    <disable option="scripts"/> <!-- Make this <enable ...> to show script execution in logs  -->
+    <disable option="match"/> <!-- Make this <enable ...> to show slow sync matching. CAN PRODUCE ENORMOUS LOGS and HEAVILY IMPACT PERFORMANCE for large slow syncs - use with care!  -->
+    <disable option="exotic"/> <!-- Make this <enable ...> to include very in-detail info. CAN PRODUCE ENORMOUS LOGS and HEAVILY IMPACT PERFORMANCE for large slow syncs - use with care!  -->
+    <!-- see manual for more debug info switches -->
+    <!-- global log options -->
+    <globallogs>no</globallogs> <!-- by default, do not log global session dispatching, creation etc. (not useful in multi-user operation) -->
+    <singlegloballog>no</singlegloballog> <!-- a new global log will be started for every start of the server/application -->
+    <!-- SyncML message dumping options -->
+    <msgdump>no</msgdump> <!-- do not dump syncml traffic 1:1 to files -->
+    <xmltranslate>no</xmltranslate> <!-- do not try to translate syncml traffic into XML (DO NOT SET THIS OPTION IN PRODUCTIVE SERVERS!) -->
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/00_t39m.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/00_t39m.xml
new file mode 100644 (file)
index 0000000..b2f8807
--- /dev/null
@@ -0,0 +1,11 @@
+    <remoterule name="t39m">
+      <!-- Rule for Ericsson T39m client -->
+      <manufacturer>Ericsson</manufacturer>
+      <software>R1A</software>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <inputcharset>ANSI</inputcharset>
+      <descriptivename>Ericsson T39m</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/01_t68.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/01_t68.xml
new file mode 100644 (file)
index 0000000..03bc6c5
--- /dev/null
@@ -0,0 +1,11 @@
+    <remoterule name="t68">
+      <!-- Rule for Ericsson T68 client -->
+      <manufacturer>Ericsson</manufacturer>
+      <software>R1B</software>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <inputcharset>ANSI</inputcharset>
+      <descriptivename>Ericsson T68</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/02_V3.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/02_V3.xml
new file mode 100644 (file)
index 0000000..952b13b
--- /dev/null
@@ -0,0 +1,14 @@
+    <remoterule name="V3">
+      <!-- Rule for Motorola V3 -->
+      <manufacturer>Motorola*</manufacturer>
+      <model>V3</model>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <quote8bitcontent>yes</quote8bitcontent>
+      <nocontentfolding>yes</nocontentfolding>
+      <outputcharset>ANSI</outputcharset>
+      <inputcharset>ANSI</inputcharset>
+      <descriptivename>Motorola V3</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/03_V3i.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/03_V3i.xml
new file mode 100644 (file)
index 0000000..f85500c
--- /dev/null
@@ -0,0 +1,13 @@
+    <remoterule name="V3i">
+      <!-- Rule for Motorola V3i -->
+      <manufacturer>Motorola*</manufacturer>
+      <model>V3i</model>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <quote8bitcontent>yes</quote8bitcontent>
+      <nocontentfolding>yes</nocontentfolding>
+      <outputcharset>ANSI</outputcharset>
+      <descriptivename>Motorola V3i</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/04_6230.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/04_6230.xml
new file mode 100644 (file)
index 0000000..d31334c
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="6230">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6230</model>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6230</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/05_9210.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/05_9210.xml
new file mode 100644 (file)
index 0000000..ef1b3cf
--- /dev/null
@@ -0,0 +1,14 @@
+    <remoterule name="9210">
+      <manufacturer>NOKIA</manufacturer>
+      <model>9210</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 9210</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/06_9210i.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/06_9210i.xml
new file mode 100644 (file)
index 0000000..5925ebb
--- /dev/null
@@ -0,0 +1,14 @@
+    <remoterule name="9210i">
+      <manufacturer>NOKIA</manufacturer>
+      <model>9210i</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 9210</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/07_3220.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/07_3220.xml
new file mode 100644 (file)
index 0000000..65968f8
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3220">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3220</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3220</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/08_3230.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/08_3230.xml
new file mode 100644 (file)
index 0000000..ac7fe01
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3230">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3230</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3230</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/09_3600.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/09_3600.xml
new file mode 100644 (file)
index 0000000..db3f2aa
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3600">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3600</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3600</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/10_3620.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/10_3620.xml
new file mode 100644 (file)
index 0000000..1cd67f3
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3620">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3620</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3620</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/11_3650.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/11_3650.xml
new file mode 100644 (file)
index 0000000..e06016a
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3650">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3650</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3650</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/12_3660.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/12_3660.xml
new file mode 100644 (file)
index 0000000..54b5d75
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="3660">
+      <manufacturer>NOKIA</manufacturer>
+      <model>3660</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 3660</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/13_6260.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/13_6260.xml
new file mode 100644 (file)
index 0000000..62a59ce
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="6260">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6260</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6260</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/14_6600.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/14_6600.xml
new file mode 100644 (file)
index 0000000..954902b
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="6600">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6600</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6600</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/15_6620.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/15_6620.xml
new file mode 100644 (file)
index 0000000..580a93e
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="6620">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6620</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6620</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/16_6630.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/16_6630.xml
new file mode 100644 (file)
index 0000000..f64994a
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="6630">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6630</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6630</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/17_6670.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/17_6670.xml
new file mode 100644 (file)
index 0000000..ef3bfdf
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="6670">
+      <manufacturer>NOKIA</manufacturer>
+      <model>6670</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 6670</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/18_7250.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/18_7250.xml
new file mode 100644 (file)
index 0000000..6f896e7
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="7250">
+      <manufacturer>NOKIA</manufacturer>
+      <model>7250</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 7250</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/19_7250i.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/19_7250i.xml
new file mode 100644 (file)
index 0000000..7a50e2a
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="7250i">
+      <manufacturer>NOKIA</manufacturer>
+      <model>7250i</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 7250i</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/20_7260.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/20_7260.xml
new file mode 100644 (file)
index 0000000..2fbf3ce
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="7260">
+      <manufacturer>NOKIA</manufacturer>
+      <model>7260</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 7260</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/21_7610.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/21_7610.xml
new file mode 100644 (file)
index 0000000..c7a74c5
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="7610">
+      <manufacturer>NOKIA</manufacturer>
+      <model>7610</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 7610</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/22_7650.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/22_7650.xml
new file mode 100644 (file)
index 0000000..e97bd6d
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="7650">
+      <manufacturer>NOKIA</manufacturer>
+      <model>7650</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia 7650</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/23_N-Gage.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/23_N-Gage.xml
new file mode 100644 (file)
index 0000000..e2e1c42
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="N-Gage">
+      <manufacturer>NOKIA</manufacturer>
+      <model>N-Gage</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia N-Gage</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/24_N-Gage_QD.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/24_N-Gage_QD.xml
new file mode 100644 (file)
index 0000000..73c299a
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="N-Gage QD">
+      <manufacturer>NOKIA</manufacturer>
+      <model>N-Gage QD</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Nokia N-Gage QD</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/25_9300.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/25_9300.xml
new file mode 100644 (file)
index 0000000..9dd949a
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="9300">
+      <manufacturer>NOKIA</manufacturer>
+      <model>9300</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <treatasutc>yes</treatasutc> <!-- needs 2.1.1 or later server -->
+      <descriptivename>Nokia 9300</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/26_9500.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/26_9500.xml
new file mode 100644 (file)
index 0000000..38a94a4
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="9500">
+      <manufacturer>NOKIA</manufacturer>
+      <model>9500</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <treatasutc>yes</treatasutc> <!-- needs 2.1.1 or later server -->
+      <descriptivename>Nokia 9500</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/27_E90.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/27_E90.xml
new file mode 100644 (file)
index 0000000..c9250f4
--- /dev/null
@@ -0,0 +1,14 @@
+    <remoterule name="E90">
+      <manufacturer>NOKIA</manufacturer>
+      <model>E90</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <ignoredevinfmaxsize>yes</ignoredevinfmaxsize>
+      <descriptivename>Nokia E90</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/28_X.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/28_X.xml
new file mode 100644 (file)
index 0000000..3977eed
--- /dev/null
@@ -0,0 +1,15 @@
+    <remoterule name="X">
+      <manufacturer>Sendo</manufacturer>
+      <model>X</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>Sendo X</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/29_SX1.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/29_SX1.xml
new file mode 100644 (file)
index 0000000..b30183a
--- /dev/null
@@ -0,0 +1,17 @@
+    <remoterule name="SX1">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>SX1</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+      ]]></rulescript>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <forcelocaltime>yes</forcelocaltime>
+      <!-- Note: SX1 crashes on contacts with empty properties -->
+      <noemptyproperties>yes</noemptyproperties>
+      <descriptivename>Siemens SX1</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/30_M55.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/30_M55.xml
new file mode 100644 (file)
index 0000000..f9a6e8f
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="M55">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>M55</model>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>Siemens M55</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/31_SL55.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/31_SL55.xml
new file mode 100644 (file)
index 0000000..13ce45d
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="SL55">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>SL55</model>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>Siemens SL55</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/32_S55.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/32_S55.xml
new file mode 100644 (file)
index 0000000..9f898c9
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="S55">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>S55</model>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>Siemens S55</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/33_S65.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/33_S65.xml
new file mode 100644 (file)
index 0000000..b58e65e
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="S65">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>S65</model>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>Siemens S65</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/34_SL65.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/34_SL65.xml
new file mode 100644 (file)
index 0000000..7119285
--- /dev/null
@@ -0,0 +1,10 @@
+    <remoterule name="SL65">
+      <manufacturer>SIEMENS</manufacturer>
+      <model>SL65</model>
+
+      <forcelocaltime>yes</forcelocaltime>
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>Siemens SL65</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/35_K700.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/35_K700.xml
new file mode 100644 (file)
index 0000000..45959fc
--- /dev/null
@@ -0,0 +1,14 @@
+    <remoterule name="K700">
+      <!-- Rule for SonyEricsson K700 (aka SEMC Phone) client -->
+      <manufacturer>SonyEricsson</manufacturer>
+      <model>SEMC Phone</model>
+      <software>R3B</software>
+
+      <!-- is a 1.1 client and claims UTC support, but it seems not to work ok
+      <forcelocaltime>yes</forcelocaltime>
+      -->
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>SonyEricsson K700</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/36_T610_T630.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/36_T610_T630.xml
new file mode 100644 (file)
index 0000000..6cc5177
--- /dev/null
@@ -0,0 +1,25 @@
+    <remoterule name="T610/T630">
+      <!-- Rule for SonyEricsson T610/T630 client -->
+      <manufacturer>SonyEricsson</manufacturer>
+      <software>R2B</software>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <descriptivename>SonyEricsson T610/T630</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in M600i vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your M600i is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your M600i has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         M600i devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the M600i is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/37_M600i.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/37_M600i.xml
new file mode 100644 (file)
index 0000000..ea086dc
--- /dev/null
@@ -0,0 +1,32 @@
+    <remoterule name="M600i">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>M600i</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson M600i</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in P800 vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your P800 is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your P800 has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         P800 devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the P800 is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/38_P800.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/38_P800.xml
new file mode 100644 (file)
index 0000000..49297f9
--- /dev/null
@@ -0,0 +1,32 @@
+    <remoterule name="P800">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>P800</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson P800</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in P900 vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your P900 is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your P900 has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         P900 devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the P900 is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/39_P900.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/39_P900.xml
new file mode 100644 (file)
index 0000000..eaa84ea
--- /dev/null
@@ -0,0 +1,32 @@
+    <remoterule name="P900">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>P900</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson P900</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in P910 vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your P910 is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your P910 has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         P910 devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the P910 is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/40_P910.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/40_P910.xml
new file mode 100644 (file)
index 0000000..1b186b1
--- /dev/null
@@ -0,0 +1,32 @@
+    <remoterule name="P910">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>P910</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson P910</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in P910i vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your P910i is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your P910i has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         P910i devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the P910i is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/41_P910i.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/41_P910i.xml
new file mode 100644 (file)
index 0000000..8b582ce
--- /dev/null
@@ -0,0 +1,32 @@
+    <remoterule name="P910i">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>P910i</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson P910i</descriptivename>
+    </remoterule>
+
+
+    <!-- Due to a problem in P990i vCalendar implementation,
+         the device does not calculate time zones correctly
+         in all cases.
+         If your P990i is brand new, you need to specify
+         "calendar?/o(Z)" as database path instead of just
+         "calendar". If your P990i has already been synced
+         at least once via the cradle and the software
+         delivered with the device, you must omit the
+         "?/o(Z)" appendix and specify just "calendar" instead.
+         Please also note that the o(Z) option for never-synced
+         P990i devices only works if your server's config is
+         derived from a recent version of our sample config.
+         Otherwise, just make sure the P990i is synced once
+         in the cradle before using SyncML.
+    -->
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/42_P990i.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/42_P990i.xml
new file mode 100644 (file)
index 0000000..8b1549c
--- /dev/null
@@ -0,0 +1,17 @@
+    <remoterule name="P990i">
+      <manufacturer>Sony Ericsson</manufacturer>
+      <model>P990i</model>
+
+      <rulescript><![CDATA[
+        // no dates before 1980
+        mindate=(TIMESTAMP)"19800101T000000Z";
+        // retransfer body when items are moved to sent box
+        retransfer_body=TRUE;
+      ]]></rulescript>
+
+      <treataslocaltime>no</treataslocaltime> <!-- can be set to yes to have /o(Z) mode (see comment above) as default, otherwise /o(z) is default -->
+      <forcelocaltime>yes</forcelocaltime>
+      <descriptivename>SonyEricsson P990i</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/43_t68i.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/43_t68i.xml
new file mode 100644 (file)
index 0000000..5d09334
--- /dev/null
@@ -0,0 +1,11 @@
+    <remoterule name="t68i">
+      <!-- Rule for SonyEricsson T68i client -->
+      <manufacturer>SonyEricsson</manufacturer>
+      <software>R2A</software>
+
+      <limitedfieldlengths>yes</limitedfieldlengths>
+      <inputcharset>ANSI</inputcharset>
+      <descriptivename>SonyEricsson T68i</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/44_Funambol_Outlook.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/44_Funambol_Outlook.xml
new file mode 100644 (file)
index 0000000..e343069
--- /dev/null
@@ -0,0 +1,9 @@
+    <remoterule name="Funambol_Outlook">
+      <!-- Rule for Funambol Outlook Sync Client -->
+      <model>Funambol Outlook Sync Client</model>
+
+      <lenientmode>yes</lenientmode> <!-- some status messages are missing at end of session -->
+      <descriptivename>Funambol Outlook Sync Client</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/remoterules/server/45_SyncJe_Outlook.xml b/src/synthesis/src/sysync_SDK/configs/remoterules/server/45_SyncJe_Outlook.xml
new file mode 100644 (file)
index 0000000..63c5e09
--- /dev/null
@@ -0,0 +1,9 @@
+    <remoterule name="SyncJe_Outlook">
+      <!-- Rule for NextHaus SyncJe Outlook Edition -->
+      <model>SyncJe Outlook Edition</model>
+
+      <ignorectcap>yes</ignorectcap> <!-- can do contact photo sync, but is missing in CTCap -->
+      <descriptivename>NextHaus SyncJe Outlook Client</descriptivename>
+    </remoterule>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/scripting/10newuid.xml b/src/synthesis/src/sysync_SDK/configs/scripting/10newuid.xml
new file mode 100644 (file)
index 0000000..6974d87
--- /dev/null
@@ -0,0 +1,8 @@
+    <function><![CDATA[
+      // create a UID
+      string newuid() {
+        return "syuid" + NUMFORMAT(RANDOM(1000000),6,"0") + "." + (string)MILLISECONDS(NOW());
+      }
+    ]]></function>
+
+
diff --git a/src/synthesis/src/sysync_SDK/configs/scripting/11calendar.xml b/src/synthesis/src/sysync_SDK/configs/scripting/11calendar.xml
new file mode 100644 (file)
index 0000000..adaea2b
--- /dev/null
@@ -0,0 +1,127 @@
+    <!-- define script macros for scripts that are used by both vCalendar 1.0 and iCalendar 2.0 -->
+
+    <macro name="VCALENDAR_INCOMING_SCRIPT"><![CDATA[
+      STRING MATCHES[];
+      STRING CAT,CN,EM;
+      INTEGER i;
+      TIMESTAMP ts;
+      // make sure we have all trailing and leading spaces eliminated
+      DESCRIPTION=NORMALIZED(DESCRIPTION);
+      SUMMARY=NORMALIZED(SUMMARY);
+      // eliminate description that is the same as summary
+      if (DESCRIPTION==SUMMARY) DESCRIPTION=EMPTY;
+      // calendar or todo
+      if (ISEVENT) {
+        // VEVENT
+        // - handle duration cases
+        if (ISDURATION(DURATION)) {
+          if (DTEND==EMPTY) DTEND = DTSTART + DURATION;
+          if (DTSTART==EMPTY) DTSTART = DTEND - DURATION;
+        }
+        // - detect alldays in vCalendar 1.0 (0:00-0:00 or 23:59 localtime)
+        i = ALLDAYCOUNT(DTSTART,DTEND,TRUE);
+        if (ITEMDATATYPE()=="vCalendar10" && i>0) {
+          // DTSTART and DTEND represent allday event, make them date-only values
+          // - convert start to user zone (or floating) so it represents midnight
+          DTSTART = CONVERTTOUSERZONE(DTSTART);
+          MAKEALLDAY(DTSTART,DTEND,i);
+        }
+        else {
+          // iCalendar 2.0 - only if DTSTART is a date-only value this really is an allday
+          if (ISDATEONLY(DTSTART)) {
+            // reshape to make sure we don't have invalid zero-duration alldays (old OCS 9 servers)
+            MAKEALLDAY(DTSTART,DTEND,i);
+          }
+        }
+        // - shape attendees (and make sure ATTENDEES[] is assigned even for empty email addresses)
+        i=0;
+        while(i<SIZE(ATTENDEES) || i<SIZE(ATTENDEE_CNS)) {
+          PARSEEMAILSPEC(ATTENDEES[i], CN, EM);
+          ATTENDEES[i] = EM; // pure email address
+          // in case we have no specific common name, use the one extracted from the email
+          // This catches the vCalendar 1.0 case and eventually ill-formed iCalendar 2.0 as well
+          if (ATTENDEE_CNS[i]==EMPTY)
+            ATTENDEE_CNS[i]=CN;
+          // default participation status to needs-action
+          if (ATTENDEE_PARTSTATS[i]==EMPTY)
+            ATTENDEE_PARTSTATS[i]=1; // 1=needs action
+          i=i+1;
+        }
+        // - shape organizer
+        PARSEEMAILSPEC(ORGANIZER, CN, EM);
+        ORGANIZER = EM; // pure email address
+        if (ORGANIZER_CN==EMPTY)
+          ORGANIZER_CN=CN;
+      }
+      else {
+        // VTODO
+        // - make sure we have at least a summary
+        if (SUMMARY==EMPTY) SUMMARY=DESCRIPTION; // use description if we don't have a summary
+        if (SUMMARY==EMPTY) SUMMARY="unnamed"; // set dummy summary if we still don't have one
+        // due shaping for non-iCalendar 2.0
+        if (ITEMDATATYPE()=="vCalendar10" && ALLDAYCOUNT(DUE,DUE,TRUE,TRUE)>0) {
+          DUE = DATEONLY(DUE);
+        }
+      }
+      // Common alarm handling
+      // - handle relative alarm time (as possible with VALARM TRIGGER)
+      if (ISDURATION(ALARM_TIME)) {
+        if (ALARM_REL==2)
+          ts = DTEND; // relative to end
+        else {
+          if (ISEVENT)
+            ts = DTSTART; // relative to start for events
+          else
+            ts = DUE; // relative to due for todos
+        }
+        // for now, make ALARM user(=system) zone in all cases
+        ALARM_TIME = CONVERTTOUSERZONE(POINTINTIME(ts),TRUE) + ALARM_TIME;
+      }
+    ]]></macro>
+
+
+    <macro name="VCALENDAR_OUTGOING_SCRIPT"><![CDATA[
+      // set UTC time of generation for iCalendar 2.0 DTSTAMP
+      DGENERATED = NOW();
+      // make sure we have all trailing and leading spaces eliminated
+      DESCRIPTION=NORMALIZED(DESCRIPTION);
+      SUMMARY=NORMALIZED(SUMMARY);
+      if (ISEVENT) {
+        // VEVENT
+        // - combine attendee email address and common name into single string for vCalendar 1.0
+        if (ITEMDATATYPE()=="vCalendar10") {
+          i=0;
+          while(i<SIZE(ATTENDEES)) {
+            ATTENDEES[i] = MAKEEMAILSPEC(ATTENDEE_CNS[i], ATTENDEES[i]);
+            i=i+1;
+          }
+          ORGANIZER = MAKEEMAILSPEC(ORGANIZER_CN, ORGANIZER);
+        }
+      }
+      else {
+        // VTODO
+        // - Nothing special so far
+      }
+      // make sure we have at least a summary
+      if (SUMMARY==EMPTY) SUMMARY=SUBSTR(DESCRIPTION,0,32); // derive from description
+      if (SUMMARY==EMPTY) SUMMARY="unnamed"; // in case description is empty as well
+      // do NOT send duration (some servers crash when doing so)
+      DURATION = UNASSIGNED;
+      // shape alarm
+      if (ALARM_TIME!=EMPTY) {
+        if (ITEMDATATYPE()=="iCalendar20") {
+          if (ALARM_ACTION==EMPTY) ALARM_ACTION = "AUDIO";
+          ALARM_TIME = CONVERTTOUSERZONE(ALARM_TIME,TRUE); // unfloat into user (system) zone, in case it is floating
+          ALARM_TIME = CONVERTTOZONE(ALARM_TIME,"UTC"); // must always be UTC by iCalendar 2.0 specs
+          // send as duration if we have non-empty non-date DTSTART
+          if (DTSTART!=EMPTY && !ISDATEONLY(DTSTART)) {
+            // make a duration (unfloat DTSTART into system zone in case it is floating first!)
+            ALARM_TIME = ALARM_TIME-CONVERTTOZONE(CONVERTTOUSERZONE(DTSTART,TRUE),"UTC");
+            ALARM_REL = 1; // relative to start
+          }
+        }
+        else {
+          if (ALARM_MSG==EMPTY) ALARM_MSG="alarm";
+        }
+      }
+    ]]></macro>
diff --git a/src/synthesis/src/sysync_SDK/configs/scripting/client/00timeout.xml b/src/synthesis/src/sysync_SDK/configs/scripting/client/00timeout.xml
new file mode 100644 (file)
index 0000000..c56df74
--- /dev/null
@@ -0,0 +1,2 @@
+    <looptimeout>5</looptimeout>
+
diff --git a/src/synthesis/src/sysync_SDK/configs/scripting/server/12email.xml b/src/synthesis/src/sysync_SDK/configs/scripting/server/12email.xml
new file mode 100644 (file)
index 0000000..5ca7286
--- /dev/null
@@ -0,0 +1,162 @@
+    <!-- The following email handling scripts are defined as script MACROS because they
+         are used in multiple <datatype> definitions below. This avoids duplicating these
+         script's source code in the config file -->
+
+    <macro name="EMAIL_INIT_SCRIPT"><![CDATA[
+      INTEGER ITEMLIMIT;
+      // default limit is limit of session
+      ITEMLIMIT = SIZELIMIT();
+    ]]></macro>
+
+
+    <macro name="EMAIL_PROCESSITEM_SCRIPT"><![CDATA[
+      // pre-process item
+      if (UPPERCASE(FOLDER)=="INBOX") {
+        // In any case, prevent adding to inbox (delete remote items instead)
+        PREVENTADD();
+        // server always wins for inbox
+        CONFLICTSTRATEGY("server-wins");
+        if (SLOWSYNC()) {
+          // also prevent modifications in server
+          IGNOREUPDATE();
+        }
+        else {
+          // normal sync items going to inbox from client need special treatment
+          if (SYNCOP()=="add" || SYNCOP()=="replace") {
+            // make sure that existing server item will conflict with this item
+            if (LIMIT!=EMPTY && (LIMIT<0 || LIMIT>SIZELIMIT())) {
+              // force conflict only if this is a reload
+              FORCECONFLICT();
+            }
+            // make sure we never overwrite a body in the inbox
+            BODY = UNASSIGNED;
+            // delete always wins over replace in inbox (to avoid adds to inbox)
+            DELETEWINS();
+          }
+        }
+      }
+      else if (UPPERCASE(FOLDER)=="OUTBOX") {
+        // never try to change something in outbox
+        IGNOREUPDATE();
+        if (SYNCOP()!="delete") {
+          // - date of mail is NOW, set it such that a correct date is written to the DB
+          MAILDATE = DBNOW();
+          // MAILDATE = (INTEGER)DBNOW() - TIMEUNITS(120); // %%% backdate it 2 mins to make sure it does not get retransmitted
+          // - echo item as replace (to force-move it to the sent folder)
+          ECHOITEM("replace");
+        }
+        CONFLICTSTRATEGY("client-wins");
+      }
+      else if (UPPERCASE(FOLDER)=="SENT") {
+        // never try to change something in sent folder
+        IGNOREUPDATE();
+        // Server has precedence in case of conflicts
+        CONFLICTSTRATEGY("server-wins");
+        // Implement reload capability for sent items as well
+        if (SLOWSYNC()) {
+          // do not add new sent items to the server in slowsync
+          PREVENTADD(); // causes extra sent items on the client to be deleted
+        }
+        else {
+          // make sure that existing server item will conflict with this item
+          if (SYNCOP()=="replace") {
+            if (LIMIT!=EMPTY && (LIMIT<0 || LIMIT>SIZELIMIT())) {
+              // force conflict only if this is a reload
+              FORCECONFLICT();
+              REJECTITEM(200); // but do not process the item further
+            }
+            else {
+              // silently ignore other types of changes
+              REJECTITEM(200);
+            }
+            // make sure we never overwrite a body in the sent folder
+            BODY = UNASSIGNED;
+          }
+        }
+      }
+      else {
+        // Other folder
+        // - silently discard incoming item for other folder than the above
+        //   except if it is a delete
+        if (SYNCOP()!="delete")
+          REJECTITEM(0);
+      }
+    ]]></macro>
+
+
+    <macro name="EMAIL_MERGE_SCRIPT"><![CDATA[
+      // pre-process item
+      if (UPPERCASE(LOOSING.FOLDER)!="OUTBOX") {
+        // non-outbox (especially inbox) needs special merge to accomplish reload feature
+        // - loosing item is client's, winning is server's
+        if (LOOSING.LIMIT!=EMPTY) {
+          // loosing (remote) item specifies a new limit, override winning's default
+          WINNING.LIMIT=LOOSING.LIMIT;
+          SETWINNINGCHANGED(TRUE);
+        }
+        // make sure winning has right folder
+        WINNING.FOLDER=LOOSING.FOLDER;
+        // make sure a set read-flag gets propagated to server
+        if (LOOSING.ISREAD=="true") WINNING.ISREAD="true";
+        // merge other fields normally
+        MERGEFIELDS();
+        // make sure body does not get re-written to local DB even if merge would cause local update
+        LOOSING.BODY=UNASSIGNED;
+      }
+      else {
+        // normal merging in other folders
+        MERGEFIELDS();
+      }
+    ]]></macro>
+
+    <macro name="EMAIL_OUTGOING_SCRIPT"><![CDATA[
+      // pre-process item
+      if (UPPERCASE(FOLDER)=="OUTBOX") {
+        // writing to outbox is always the ECHOITEM
+        // - cause item to move into "sent" folder
+        FOLDER = "sent";
+        if (!SESSIONVAR("retransfer_body")) {
+          // - prevent body retransfer, but not for dumb P800/P900/M600/P990 clients
+          BODY = UNASSIGNED; // prevent body transfer
+          ATT_COUNT = 0; // prevent attachment transfer
+          ATT_CONTENTS = UNASSIGNED;
+          // basically, this item is not limited (already complete on the client)
+          // even if contents are not sent
+          LIMIT = -1;
+          SETSIZELIMIT(-1);
+        }
+      }
+      else {
+        // outgoing item to any folder of of remote (inbox, sent...)
+        // - limit body to what is set in the LIMIT field
+        // %%% probably obsolete, as textitem will handle limit field automatically for >=V1.0.8.21
+        IF (LIMIT==EMPTY)
+          LIMIT = SIZELIMIT(); // if none set already, use default for this item (=default of datastore, if not SETSIZELIMIT() called before for this item generation)
+      }
+      // set limit for item generator
+      if (LIMIT!=EMPTY)
+        SETSIZELIMIT(LIMIT);
+    ]]></macro>
+
+
+    <macro name="EMAIL_FILTERINIT_SCRIPT"><![CDATA[
+      // check if we need to filter
+      INTEGER NEEDFILTER;
+
+      NEEDFILTER =
+        !DBHANDLESOPTS() && // only if DB cannot handle it
+        (STARTDATE()!=EMPTY); // and only if a start date is set (end date not needed as there are never future emails today)
+      SETFILTERALL(NEEDFILTER);
+      RETURN NEEDFILTER;
+    ]]></macro>
+
+
+    <macro name="EMAIL_FILTER_SCRIPT"><![CDATA[
+      INTEGER PASSES;
+
+      // check if item passes filter
+      PASSES=FALSE;
+      // Emails pass if they have a MAILDATE on or later than start date
+      PASSES = MAILDATE>=STARTDATE();
+      RETURN PASSES;
+    ]]></macro>
index 5b66d2d..9c3945f 100644 (file)
@@ -14,6 +14,7 @@
     <sessionlogs>yes</sessionlogs> <!-- by default, create a session log file for every sync session (might be disabled for special users/devices in scripts) -->
     <!-- debug format options -->
     <logformat>html</logformat> <!-- html is nicely colored and easily viewable with a web browser. Other options: "xml", "text" -->
+    <folding>auto</folding> <!-- dynamic folding of blocks enabled, automatically expanded or collapsed default. Other options: "none", "expanded", "collapsed" -->
     <timestamp>yes</timestamp> <!-- show timestamps for structure elements in log -->
     <timestampall>no</timestampall> <!-- don't show timestamp for every log line -->
     <timedsessionlognames>yes</timedsessionlognames> <!-- session logs also have the session start timestamp in the filename - makes them more easily sortable -->
@@ -28,6 +29,7 @@
     <!-- additional debug info switches -->
     <enable option="userdata"/> <!-- Make this <disable ...> if you don't want user data in the logs -->
     <disable option="scripts"/> <!-- Make this <enable ...> to show script execution in logs  -->
+    <disable option="match"/> <!-- Make this <enable ...> to show slow sync matching. CAN PRODUCE ENORMOUS LOGS and HEAVILY IMPACT PERFORMANCE for large slow syncs - use with care!  -->
     <disable option="exotic"/> <!-- Make this <enable ...> to include very in-detail info. CAN PRODUCE ENORMOUS LOGS and HEAVILY IMPACT PERFORMANCE for large slow syncs - use with care!  -->
     <!-- see manual for more debug info switches -->
     <!-- global log options -->
@@ -36,6 +38,7 @@
     <!-- SyncML message dumping options -->
     <msgdump>no</msgdump> <!-- do not dump syncml traffic 1:1 to files -->
     <xmltranslate>no</xmltranslate> <!-- do not try to translate syncml traffic into XML (DO NOT SET THIS OPTION IN PRODUCTIVE SERVERS!) -->
+
   </debug>
 
   <transport type="xpt">
@@ -55,7 +58,7 @@
     ]]></function>
 
 
-               <!-- define script macros for scripts that are used by both vCalendar 1.0 and iCalendar 2.0 -->
+    <!-- define script macros for scripts that are used by both vCalendar 1.0 and iCalendar 2.0 -->
 
     <macro name="VCALENDAR_INCOMING_SCRIPT"><![CDATA[
       STRING MATCHES[];
         if (SUMMARY==EMPTY) SUMMARY="unnamed"; // set dummy summary if we still don't have one
         // due shaping for non-iCalendar 2.0
         if (ITEMDATATYPE()=="vCalendar10" && ALLDAYCOUNT(DUE,DUE,TRUE,TRUE)>0) {
-               DUE = DATEONLY(DUE);
+          DUE = DATEONLY(DUE);
         }
       }
       // Common alarm handling
 
 
   <datatypes>
-
     <!-- list of internal fields representing vCard data -->
     <fieldlist name="contacts">
+      <field name="SYNCLVL" type="integer" compare="never"/>
       <field name="REV" type="timestamp" compare="never" age="yes"/>
 
       <!-- Name elements -->
       <field name="PHOTO_TYPE" type="integer" compare="never" merge="fillempty"/>
 
     </fieldlist>
-
     <!-- vCard profile -->
     <mimeprofile name="vCard" fieldlist="contacts">
 
       </profile>
     </mimeprofile>
 
+
     <!-- vCard 2.1 datatype, using vCard profile defined above -->
     <datatype name="vCard21" basetype="vcard">
       <version>2.1</version>
       <version>3.0</version>
       <use mimeprofile="vCard"/>
     </datatype>
-
-
     <!-- common field list for events and todos (both represented by vCalendar/iCalendar) -->
     <fieldlist name="calendar">
+      <field name="SYNCLVL" type="integer" compare="never"/>
       <field name="ISEVENT" type="integer" compare="always"/>
 
       <field name="DMODIFIED" type="timestamp" compare="never" age="yes"/>
       <field name="ORGANIZER_CN" array="no" type="string" compare="never"/>
 
     </fieldlist>
-
-
     <!-- vCalendar with VTODO and VEVENT variants -->
     <mimeprofile name="vCalendar" fieldlist="calendar">
 
 
         <subprofile onlyformode="standard" name="VTIMEZONE" mode="vtimezones"/>
 
-        <!-- sub-profile for todoz -->
+        <!-- sub-profile for tasks -->
         <subprofile name="VTODO" nummandatory="1" showifselectedonly="yes" field="ISEVENT" value="0">
 
           <property name="LAST-MODIFIED">
       </profile>
     </mimeprofile>
 
-
     <!-- vCalendar 1.0 datatype, using vCalendar profile defined above -->
     <datatype name="vCalendar10" basetype="vcalendar">
       <version>1.0</version>
 
     </datatype>
 
-
     <!-- list of internal fields representing plain text note data -->
     <fieldlist name="Note">
       <field name="SYNCLVL" type="integer" compare="never"/>
       <versionstring>1.1</versionstring>
     </datatype>
 
-
     <!-- list of internal fields representing vBookmark data -->
     <fieldlist name="bookmarks">
       <field name="REV" type="timestamp" compare="never" age="yes"/>
 
         <!-- non-standard properties -->
 
-        <property name="CATEGORIES">
-          <value field="CATEGORIES"/>
+        <!-- inherit CATEGORIES from vCard 3.0, i.e. comma separated -->
+        <property name="CATEGORIES" values="list" valueseparator="," altvalueseparator=";">
+          <value field="CATEGORIES" combine=","/>
         </property>
 
         <property name="CLASS" suppressempty="yes">
index 4ae74a6..55a0515 100755 (executable)
     <!-- SyncML message dumping options -->
     <msgdump>no</msgdump> <!-- do not dump syncml traffic 1:1 to files -->
     <xmltranslate>no</xmltranslate> <!-- do not try to translate syncml traffic into XML (DO NOT SET THIS OPTION IN PRODUCTIVE SERVERS!) -->
+
   </debug>
 
 
   <scripting>
-    <!-- Global script function definitions -->
-
     <function><![CDATA[
       // create a UID
       string newuid() {
@@ -52,7 +51,7 @@
     ]]></function>
 
 
-  <!-- define script macros for scripts that are used by both vCalendar 1.0 and iCalendar 2.0 -->
+    <!-- define script macros for scripts that are used by both vCalendar 1.0 and iCalendar 2.0 -->
 
     <macro name="VCALENDAR_INCOMING_SCRIPT"><![CDATA[
       STRING MATCHES[];
         }
       }
     ]]></macro>
-
-
     <!-- The following email handling scripts are defined as script MACROS because they
          are used in multiple <datatype> definitions below. This avoids duplicating these
          script's source code in the config file -->
 
 
   <datatypes>
-
     <!-- list of internal fields representing vCard data -->
     <fieldlist name="contacts">
       <field name="SYNCLVL" type="integer" compare="never"/>
       <field name="PHOTO_TYPE" type="integer" compare="never" merge="fillempty"/>
 
     </fieldlist>
-
     <!-- vCard profile -->
     <mimeprofile name="vCard" fieldlist="contacts">
 
       </profile>
     </mimeprofile>
 
+
     <!-- vCard 2.1 datatype, using vCard profile defined above -->
     <datatype name="vCard21" basetype="vcard">
       <version>2.1</version>
       <version>3.0</version>
       <use mimeprofile="vCard"/>
     </datatype>
-
-
     <!-- common field list for events and todos (both represented by vCalendar/iCalendar) -->
     <fieldlist name="calendar">
       <field name="SYNCLVL" type="integer" compare="never"/>
       <field name="ORGANIZER_CN" array="no" type="string" compare="never"/>
 
     </fieldlist>
-
-
     <!-- vCalendar with VTODO and VEVENT variants -->
     <mimeprofile name="vCalendar" fieldlist="calendar">
 
       </profile>
     </mimeprofile>
 
-
     <!-- vCalendar 1.0 datatype, using vCalendar profile defined above -->
     <datatype name="vCalendar10" basetype="vcalendar">
       <version>1.0</version>
 
     </datatype>
 
-
     <!-- list of internal fields representing plain text note data -->
     <fieldlist name="Note">
       <field name="SYNCLVL" type="integer" compare="never"/>
       </linemap>
     </textprofile>
 
-    <datatype name="note" basetype="text">
+    <datatype name="note10" basetype="text">
       <use profile="Note"/>
       <typestring>text/plain</typestring>
       <versionstring>1.0</versionstring>
       <versionstring>1.1</versionstring>
     </datatype>
 
-
     <!-- list of internal fields representing vBookmark data -->
     <fieldlist name="bookmarks">
       <field name="REV" type="timestamp" compare="never" age="yes"/>
     </fieldlist>
 
     <!-- vBookmark profile -->
-    <mimeprofile name="vbookmark" fieldlist="bookmarks">
+    <mimeprofile name="vBookmark" fieldlist="bookmarks">
 
       <profile name="VBKM" nummandatory="0">
         <property name="VERSION">
     </mimeprofile>
 
     <!-- vBookmark datatype, using vBookmark profile defined above -->
-    <datatype name="vbookmark" basetype="mimedir">
+    <datatype name="vBookmark10" basetype="mimedir">
       <typestring>text/x-vbookmark</typestring>
       <versionstring>1.0</versionstring>
-      <use profile="vbookmark"/>
+      <use profile="vBookmark"/>
     </datatype>
 
-
-
     <!-- list of internal fields representing email data -->
     <fieldlist name="email">
       <field name="SYNCLVL" type="integer" compare="never"/>
       <field name="ATT_CONTENTS" array="yes" type="blob" compare="never"/>
     </fieldlist>
 
-
     <!-- this is the text profile used to generate and decode RFC2822/MIME-Multipart
          email messages. -->
     <textprofile name="rfc2822_email" fieldlist="email">
       </linemap>
     </textprofile>
 
-
     <datatype name="email" basetype="text">
       <use profile="rfc2822_email"/>
       <typestring>text/message</typestring> <!-- this is P800-like -->
 
     </datatype>
 
-
     <!-- Note: This is a proprietary extension datatype for Synthesis AG Windows Mobile SyncML clients.
          This format is a compressed form of the standard RFC2822 format. For one, the entire data
          is compressed using the zip algorithm (<zippedbindata>), and secondly attachments are included
 
     </datatype>
 
-
-
     <datatype name="email_sonyericsson" basetype="text">
       <use profile="rfc2822_email"/>
       <typestring>message/rfc822</typestring> <!-- this is M600i/P990-like -->
 
     </datatype>
 
-
     <datatype name="email_nokia9500" basetype="text">
       <use profile="rfc2822_email"/>
       <typestring>message/x-rfc822</typestring> <!-- this is Nokia 9500/9300-like -->
       </fieldmap>
 
       <typesupport>
-        <use datatype="note" mode="rw" preferred="yes"/>
+        <use datatype="note10" mode="rw" preferred="yes"/>
         <use datatype="note11" mode="rw"/>
       </typesupport>
 
 
       <!-- datatypes supported by this datastore -->
       <typesupport>
-        <use datatype="vbookmark" mode="rw" preferred="yes"/>
+        <use datatype="vBookmark10" mode="rw" preferred="yes"/>
       </typesupport>
 
     </datastore>
     </remoterule>
 
 
-ÊÊ  <remoterule name="SyncJe_Outlook">
-ÊÊÊÊ  <!-- Rule for NextHaus SyncJe Outlook Edition -->
-ÊÊÊÊ  <model>SyncJe Outlook Edition</model>
+    <remoterule name="SyncJe_Outlook">
+      <!-- Rule for NextHaus SyncJe Outlook Edition -->
+      <model>SyncJe Outlook Edition</model>
 
-ÊÊÊÊ  <ignorectcap>yes</ignorectcap> <!-- can do contact photo sync, but is missing in CTCap -->
+      <ignorectcap>yes</ignorectcap> <!-- can do contact photo sync, but is missing in CTCap -->
       <descriptivename>NextHaus SyncJe Outlook Client</descriptivename>
-ÊÊ  </remoterule>
-
+    </remoterule>
 
     <!-- End of remote rule list. -->
 
diff --git a/src/synthesis/src/sysync_SDK/configs/update-samples.pl b/src/synthesis/src/sysync_SDK/configs/update-samples.pl
new file mode 100755 (executable)
index 0000000..0dbdb97
--- /dev/null
@@ -0,0 +1,64 @@
+#! /usr/bin/env perl
+
+sub basename {
+    $_ = shift;
+    s;.*/;;;
+    return $_;
+}
+
+# Concatenate all files ending in .xml in the given directory
+# plus those in a specific subdirectory for client or server.
+# Order lexicographic ascending of the base filename.
+
+sub readfragments {
+    my $dir = shift;
+    my $subdir = shift;
+    my @res = ();
+    
+    my @files = ();
+    if (opendir(my $dh, $dir)) {
+        foreach (grep (/.*\.xml$/, readdir($dh))) {
+            push @files, "$dir/$_";
+        }
+        closedir($dh);
+
+        if (opendir(my $dh, "$dir/$subdir")) {
+            foreach (grep (/.*\.xml$/, readdir($dh))) {
+                push @files, "$dir/$subdir/$_";
+            }
+            closedir($dh);
+        }
+    }
+
+    @files = sort { basename($a) <=> basename($b) } @files;
+    foreach (@files) {
+        open(IN, "<$_") || die "cannot read $_: $!";
+        push @res, <IN>;
+        close(IN);
+    }
+
+    return join("", @res);
+}
+
+# replace content of <debug>, <scripting>, <datatypes> and all <remoterule>s
+# with the corresponding shared and/or client/server .xml fragments
+sub update {
+    my $file = shift;
+    my $subdir = shift;
+
+    open(IN, "<$file") || die "cannot read $file: $!";
+    $_ = join("", <IN>);
+    close(IN) || die "closing $file: $!";
+
+    s;(<debug>\n).*(\n *</debug>);$1 . readfragments("debug", $subdir) . $2;se;
+    s;(<scripting>\n).*(\n *</scripting>);$1 . readfragments("scripting", $subdir) . $2;se;
+    s;(<datatypes>\n).*(\n *</datatypes>);$1 . readfragments("datatypes", $subdir) . $2;se;
+    s;(\n *)<remoterule>.*</remoterule>;$1 . readfragments("remoterules", $subdir);se;
+
+    open(OUT, ">$file") || die "cannot write $file: $!";
+    print OUT;
+    close(OUT) || die "closing $file: $!";
+}
+
+update("syncclient_sample_config.xml", "client");
+update("syncserv_sample_config.xml", "server");
index 027fe8c..c2b5a48 100644 (file)
@@ -1038,7 +1038,7 @@ void LocalTests::testLinkedItemsRemoveNormal() {
 
     deleteAll(createSourceA);
     std::string parent, child;
-    TestingSyncSourcePtr copy;
+    TestingSyncSourcePtr source, copy;
 
     // check that everything is empty, also resets change counter of sync source B
     SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
@@ -1060,10 +1060,19 @@ void LocalTests::testLinkedItemsRemoveNormal() {
 
     deleteItem(createSourceA, child);
 
+    SOURCE_ASSERT_NO_FAILURE(source.get(), source.reset(createSourceA()));
+    SOURCE_ASSERT_EQUAL(source.get(), 1, countItems(source.get()));
+    SOURCE_ASSERT_EQUAL(source.get(), 0, countNewItems(source.get()));
+    SOURCE_ASSERT_EQUAL(source.get(), 0, countUpdatedItems(source.get()));
+    SOURCE_ASSERT_EQUAL(source.get(), 0, countDeletedItems(source.get()));
+    CPPUNIT_ASSERT_NO_THROW(source.reset());
+
     SOURCE_ASSERT_NO_FAILURE(copy.get(), copy.reset(createSourceB()));
     SOURCE_ASSERT_EQUAL(copy.get(), 1, countItems(copy.get()));
     SOURCE_ASSERT_EQUAL(copy.get(), 0, countNewItems(copy.get()));
-    SOURCE_ASSERT_EQUAL(copy.get(), 0, countUpdatedItems(copy.get()));
+    // parent might have been updated
+    int updated = countUpdatedItems(copy.get());
+    SOURCE_ASSERT(copy.get(), 0 <= updated && updated <= 1);
     SOURCE_ASSERT_EQUAL(copy.get(), 1, countDeletedItems(copy.get()));
     SOURCE_ASSERT_EQUAL(copy.get(), 1, countEqual(listDeletedItems(copy.get()), child));
     CPPUNIT_ASSERT_NO_THROW(copy.reset());
@@ -3240,12 +3249,14 @@ SyncTests *ClientTest::createSyncTests(const std::string &name, std::vector<int>
 int ClientTest::dump(ClientTest &client, TestingSyncSource &source, const char *file)
 {
     BackupReport report;
-    VolatileConfigNode node;
+    boost::shared_ptr<ConfigNode> node(new VolatileConfigNode);
 
     rm_r(file);
     mkdir_p(file);
     CPPUNIT_ASSERT(source.getOperations().m_backupData);
-    source.getOperations().m_backupData(file, node, report);
+    source.getOperations().m_backupData(SyncSource::Operations::ConstBackupInfo(),
+                                        SyncSource::Operations::BackupInfo(SyncSource::Operations::BackupInfo::BACKUP_OTHER, file, node),
+                                        report);
     return 0;
 }
 
index 282ea8d..61ef438 100644 (file)
@@ -102,11 +102,16 @@ FFLAGS = @FFLAGS@
 FILE_CFLAGS = @FILE_CFLAGS@
 FILE_LIBS = @FILE_LIBS@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB216_CFLAGS = @GLIB216_CFLAGS@
+GLIB216_LIBS = @GLIB216_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GMOFILES = @GMOFILES@
 GMSGFMT = @GMSGFMT@
+GNOMEBLUETOOTH_CFLAGS = @GNOMEBLUETOOTH_CFLAGS@
+GNOMEBLUETOOTH_DIR = @GNOMEBLUETOOTH_DIR@
+GNOMEBLUETOOTH_LIBS = @GNOMEBLUETOOTH_LIBS@
 GOBJECT_CFLAGS = @GOBJECT_CFLAGS@
 GOBJECT_LIBS = @GOBJECT_LIBS@
 GREP = @GREP@
@@ -136,6 +141,8 @@ KEYRING_CFLAGS = @KEYRING_CFLAGS@
 KEYRING_LIBS = @KEYRING_LIBS@
 LDFLAGS = @LDFLAGS@
 LIBEXECDIR = @LIBEXECDIR@
+LIBICAL_AVAILABLE_CFLAGS = @LIBICAL_AVAILABLE_CFLAGS@
+LIBICAL_AVAILABLE_LIBS = @LIBICAL_AVAILABLE_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBOPENOBEX_CFLAGS = @LIBOPENOBEX_CFLAGS@
 LIBOPENOBEX_LIBS = @LIBOPENOBEX_LIBS@
@@ -155,6 +162,8 @@ MSGFMT = @MSGFMT@
 MSGFMT_OPTS = @MSGFMT_OPTS@
 MSGMERGE = @MSGMERGE@
 NMEDIT = @NMEDIT@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
 OBJEXT = @OBJEXT@
 PACKAGE = @PACKAGE@
 PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
index fbf3bfb..a90f2ff 100644 (file)
@@ -103,13 +103,12 @@ sub splitvalue {
   return join("", @res);
 }
 
-# parameters: file handle with input, width to use for reformatted lines
+# parameters: text, width to use for reformatted lines
 # returns list of lines without line breaks
 sub Normalize {
-  my $in = shift;
+  $_ = shift;
   my $width = shift;
 
-  $_ = join( "", <$in> );
   s/\r//g;
 
   my @items = ();
@@ -527,22 +526,66 @@ if($#ARGV > 1) {
 
   my ($file1, $file2) = ($ARGV[0], $ARGV[1]);
 
-  if (-d $file1) {
-      open(IN1, "-|:utf8", "find $file1 -type f -print0 | xargs -0 cat") || die "$file1: $!";
-  } else {
-      open(IN1, "<:utf8", $file1) || die "$file1: $!";
-  }
-  if (-d $file2) {
-      open(IN2, "-|:utf8", "find $file2 -type f -print0 | xargs -0 cat") || die "$file2: $!";
-  } else {
-      open(IN2, "<:utf8", $file2) || die "$file2: $!";
-  }
   my $singlewidth = int(($columns - 3) / 2);
   $columns = $singlewidth * 2 + 3;
-  my @normal1 = Normalize(*IN1{IO}, $singlewidth);
-  my @normal2 = Normalize(*IN2{IO}, $singlewidth);
-  close(IN1);
-  close(IN2);
+  my @normal1;
+  my @normal2;
+
+  if (-d $file1 && -d $file2) {
+      # Both "files" are really directories of individual files.
+      # Don't include files in the comparison which are known
+      # to be identical because the refer to the same inode.
+      # - build map from inode to filename
+      my %files1;
+      my %files2;
+      my @content1;
+      my @content2;
+      my $inode;
+      my $fullname;
+      my $entry;
+      opendir(my $dh, $file1) || die "cannot read $file1: $!";
+      foreach $entry (grep { -f "$file1/$_" } readdir($dh)) {
+          $fullname = "$file1/$entry";
+          $inode = (stat($fullname))[1];
+          $files1{$inode} = $entry;
+      }
+      closedir($dh);
+      # - remove common files, read others
+      opendir(my $dh, $file2) || die "cannot read $file2: $!";
+      foreach $entry (grep { -f "$file2/$_" } readdir($dh)) {
+          $fullname = "$file2/$entry";
+          $inode = (stat($fullname))[1];
+          if ($files1{$inode}) {
+              delete $files1{$inode};
+          } else {
+              open(IN, "<:utf8", "$fullname") || die "$fullname: $!";
+              push @content2, <IN>;
+          }
+      }
+      # - read remaining entries from first dir
+      foreach $entry (values %files1) {
+          $fullname = "$file1/$entry";
+          open(IN, "<:utf8", "$fullname") || die "$fullname: $!";
+          push @content1, <IN>;
+      }
+      @normal1 = Normalize(join("", @content1), $singlewidth);
+      @normal2 = Normalize(join("", @content2), $singlewidth);
+  } else {
+      if (-d $file1) {
+          open(IN1, "-|:utf8", "find $file1 -type f -print0 | xargs -0 cat") || die "$file1: $!";
+      } else {
+          open(IN1, "<:utf8", $file1) || die "$file1: $!";
+      }
+      if (-d $file2) {
+          open(IN2, "-|:utf8", "find $file2 -type f -print0 | xargs -0 cat") || die "$file2: $!";
+      } else {
+          open(IN2, "<:utf8", $file2) || die "$file2: $!";
+      }
+      @normal1 = Normalize(join("", <IN1>), $singlewidth);
+      @normal2 = Normalize(join("", <IN2>), $singlewidth);
+      close(IN1);
+      close(IN2);
+  }
 
   # Produce output where each line is marked as old (aka remove) with o,
   # as new (aka added) with n, and as unchanged with u at the beginning.
@@ -679,5 +722,5 @@ if($#ARGV > 1) {
     $in = *STDIN{IO};
   }
 
-  print STDOUT join("\n", Normalize($in, $columns)), "\n";
+  print STDOUT join("\n", Normalize(join("", <$in>), $columns)), "\n";
 }