From 542b61546644050a8d2dd2c910266f918431f65e Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 25 May 2012 14:42:34 +0200 Subject: SyncML TK: Added missing parsing error debug output for parsing --- src/syncml_tk/src/sml/xlt/all/xltmetinf.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/syncml_tk/src/sml/xlt/all/xltmetinf.c b/src/syncml_tk/src/sml/xlt/all/xltmetinf.c index f37a470..e171a45 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltmetinf.c +++ b/src/syncml_tk/src/sml/xlt/all/xltmetinf.c @@ -74,7 +74,7 @@ Ret_t buildMetInfAnchorCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { pScanner = pDecoder->scanner; if (*ppElem != NULL) - return SML_ERR_XLT_INVAL_SYNCML_DOC; + return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMetInfAnchorCmd"); if ((pAnchor = (SmlMetInfAnchorPtr_t)smlLibMalloc(sizeof(SmlMetInfAnchor_t))) == NULL) return SML_ERR_NOT_ENOUGH_SPACE; @@ -101,7 +101,7 @@ Ret_t buildMetInfAnchorCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { rc = buildPCData(pDecoder, (VoidPtr_t)&pAnchor->next); break; default: - rc = SML_ERR_XLT_INVAL_SYNCML_DOC; + rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMetInfAnchorCmd_2"); } if (rc != SML_ERR_OK) { smlFreeMetinfAnchor(pAnchor); @@ -127,7 +127,7 @@ Ret_t buildMetInfMemCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { pScanner = pDecoder->scanner; if (*ppElem != NULL) - return SML_ERR_XLT_INVAL_SYNCML_DOC; + return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC, pScanner, "buildMetInfMemCmd"); if ((pMem = (SmlMetInfMemPtr_t)smlLibMalloc(sizeof(SmlMetInfMem_t))) == NULL) return SML_ERR_NOT_ENOUGH_SPACE; @@ -166,7 +166,7 @@ Ret_t buildMetInfMemCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { break; default: - rc = SML_ERR_XLT_INVAL_SYNCML_DOC; + rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC, pScanner, "buildMetInfMemCmd_2"); } if (rc != SML_ERR_OK) { smlFreeMetinfMem(pMem); @@ -193,7 +193,7 @@ Ret_t buildMetInfMetInfCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { pScanner = pDecoder->scanner; if (*ppElem != NULL) - return SML_ERR_XLT_INVAL_SYNCML_DOC; + return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC, pScanner, "buildMetInfMetInfCmd"); if ((pMeta = (SmlMetInfMetInfPtr_t)smlLibMalloc(sizeof(SmlMetInfMetInf_t))) == NULL) return SML_ERR_NOT_ENOUGH_SPACE; @@ -257,7 +257,7 @@ Ret_t buildMetInfMetInfCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { break; default: - rc = SML_ERR_XLT_INVAL_SYNCML_DOC; + rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC, pScanner, "buildMetInfMetInfCmd_2"); } if (rc != SML_ERR_OK) { smlFreeMetinfMetinf(pMeta); -- cgit v1.2.3 From b65ac25efae85609d606bd45faee38c63155bbb4 Mon Sep 17 00:00:00 2001 From: Andris Pavenis Date: Fri, 25 May 2012 12:42:58 +0300 Subject: Fix configure option --enable-debug-logs help string --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 50e0903..390fada 100644 --- a/configure.in +++ b/configure.in @@ -28,7 +28,7 @@ PIC_CXXFLAGS="$lt_prog_compiler_pic_CXX" AC_SUBST(PIC_CXXFLAGS) AC_ARG_ENABLE(debug-logs, - AS_HELP_STRING([--debug-logs], + AS_HELP_STRING([--enable-debug-logs], [For developers: add links to call location to HTML log files. Depends on Doxygen (for HTML version of source) and g++ (for __PRETTY_FUNCTION__).]), enable_debug_logs="$enableval", enable_debug_logs="no") if test "$enable_debug_logs" = "yes"; then -- cgit v1.2.3 From 39366bd1955c87098dd91366817e9baed9ce6c15 Mon Sep 17 00:00:00 2001 From: Andris Pavenis Date: Tue, 29 May 2012 12:21:58 +0300 Subject: Workaround problem with Sony Xperia 2012 (S and perhaps also P and U) phones Data element in Alert command contains Next (syncml:metinf) directly instead of inside Anchor element. Handle this case. One could easily add also handle element Last in the same way, but it is not implemented yet. --- src/syncml_tk/src/sml/xlt/all/xltmetinf.c | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/syncml_tk/src/sml/xlt/all/xltmetinf.c b/src/syncml_tk/src/sml/xlt/all/xltmetinf.c index e171a45..8d88c44 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltmetinf.c +++ b/src/syncml_tk/src/sml/xlt/all/xltmetinf.c @@ -119,6 +119,37 @@ Ret_t buildMetInfAnchorCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { return SML_ERR_OK; } +Ret_t buildMetinfNextCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { + XltDecScannerPtr_t pScanner; + SmlMetInfAnchorPtr_t pAnchor; + Ret_t rc; + + pScanner = pDecoder->scanner; + + if (*ppElem == NULL) { + if ((pAnchor = (SmlMetInfAnchorPtr_t)smlLibMalloc(sizeof(SmlMetInfAnchor_t))) == NULL) + return SML_ERR_NOT_ENOUGH_SPACE; + smlLibMemset(pAnchor, 0, sizeof(SmlMetInfAnchor_t)); + } else { + pAnchor = (SmlMetInfAnchorPtr_t)*ppElem; + if (pAnchor->next != NULL) + return SML_ERR_XLT_INVAL_SYNCML_DOC; + } + + rc = buildPCData(pDecoder, (VoidPtr_t)&pAnchor->next); + + if (rc != SML_ERR_OK && ppElem == NULL) { + smlFreeMetinfAnchor(pAnchor); + return rc; + } + + if (ppElem == NULL) { + *ppElem = pAnchor; + } + + return SML_ERR_OK; +} + Ret_t buildMetInfMemCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { XltDecScannerPtr_t pScanner; SmlMetInfMemPtr_t pMem; @@ -256,6 +287,11 @@ Ret_t buildMetInfMetInfCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { rc = buildEmptyTag(pDecoder); // allow for instead of break; + /* Next without Anchor, Andris Pavenis 2012-05-29 */ + case TN_METINF_NEXT: + rc = buildMetinfNextCmd(pDecoder, (VoidPtr_t)&pMeta->anchor); + break; + default: rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC, pScanner, "buildMetInfMetInfCmd_2"); } -- cgit v1.2.3 From 2cde0445643124a4789bfec0c0f5275391055669 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Tue, 29 May 2012 21:34:53 +0200 Subject: libsynthesis 3.4.0.42 --- src/global_options.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/global_options.h b/src/global_options.h index 5013de1..fb978eb 100644 --- a/src/global_options.h +++ b/src/global_options.h @@ -76,8 +76,8 @@ #endif #ifndef SYSYNC_BUILDNUMBER -#define SYSYNC_BUILDNUMBER 41 -#define SYSYNC_BUILDNUMBER_TXT "41" +#define SYSYNC_BUILDNUMBER 42 +#define SYSYNC_BUILDNUMBER_TXT "42" #endif -- cgit v1.2.3 From 5bfb5a7a576e1c48a58266dd46560bb83fdc3db4 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Thu, 7 Jun 2012 12:32:45 +0200 Subject: SyncML TK (and other global) debug output: added LF when using CONSOLEINFO_LIBC mode (introduced in 13ff1e4149 (logging + Linux: enable console output)) Unified with identification in logs ("sysync"). --- src/sysync/debuglogger.cpp | 4 ++-- src/sysync/sysync_debug.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sysync/debuglogger.cpp b/src/sysync/debuglogger.cpp index 413a345..f50f8ea 100755 --- a/src/sysync/debuglogger.cpp +++ b/src/sysync/debuglogger.cpp @@ -83,7 +83,7 @@ cAppCharP const DbgOutDefaultPrefixes[numDbgOutFormats] = { "\n" "", "\n" - "Synthesis SyncML Engine " SYSYNC_FULL_VERSION_STRING " Log\n" + "SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING " Log\n" "\n" "\n" - "

Start of log - Synthesis SyncML Engine " SYSYNC_FULL_VERSION_STRING "

\n
    \n" + "

    Start of log - SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING "

    \n
      \n" }; cAppCharP const DbgOutDefaultSuffixes[numDbgOutFormats] = { diff --git a/src/sysync/sysync_debug.h b/src/sysync/sysync_debug.h index 61d4cdd..68dce5e 100755 --- a/src/sysync/sysync_debug.h +++ b/src/sysync/sysync_debug.h @@ -110,7 +110,7 @@ TDebugLogger *getDbgLogger(void); // Because a lot of libs log to stderr, include a unique prefix. // Assumes that all printf format strings are plain strings. #define CONSOLEPUTS(m) CONSOLE_PRINTF_VARARGS("%s", (m)) -#define CONSOLE_PRINTF_VARARGS(_m, _args...) fprintf(stderr, "SYSYNC " _m, ##_args) +#define CONSOLE_PRINTF_VARARGS(_m, _args...) fprintf(stderr, "SYSYNC " _m "\n", ##_args) #define CONSOLEPRINTF(m) CONSOLE_PRINTF_VARARGS m # else // CONSOLEINFO_LIBC #define CONSOLEPUTS(m) ConsolePuts(m) -- cgit v1.2.3 From c53eb2f7bb9b5eedcf0028cc1e65953c00857c48 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Tue, 19 Jun 2012 23:20:30 +0200 Subject: XCode upgrade version changed --- .../xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme index 4e2e45c..b1c96ea 100644 --- a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme +++ b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme @@ -1,5 +1,6 @@ Date: Fri, 27 Jul 2012 20:53:36 +0200 Subject: Delayed termination of sync session added - experimental for now --- src/sysync/syncagent.cpp | 15 +++++++++++---- src/sysync/syncagent.h | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index bcdaea8..908fc61 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -3161,6 +3161,15 @@ TSyError TSyncAgent::ServerSessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aI // handle pre-processed step command according to current engine state switch (fServerEngineState) { + // Almost done state + case ses_almostdone: + // everything done, except for termination of session + // - do it now + TerminateSession(); + // - now done + fServerEngineState = ses_done; + // fall through to done state + // Done state case ses_done : // session done, nothing happens any more @@ -3337,12 +3346,10 @@ TSyError TSyncAgent::ServerGeneratingStep(uInt16 &aStepCmd, TEngineProgressInfo // no more data to send aStepCmd = STEPCMD_OK; // need one more step to finish } - // in any case, if done, all susequent steps will return STEPCMD_DONE + // in any case, if done, subsequent steps will terminate the session and return STEPCMD_DONE if (done) { - // Session is done - TerminateSession(); // subsequent steps will all return STEPCMD_DONE - fServerEngineState = ses_done; + fServerEngineState = ses_almostdone; } // request reset fRequestSize = 0; diff --git a/src/sysync/syncagent.h b/src/sysync/syncagent.h index 6a2b990..5403c1e 100755 --- a/src/sysync/syncagent.h +++ b/src/sysync/syncagent.h @@ -61,6 +61,7 @@ typedef enum { ses_processing, ///< ready to perform next STEPCMD_STEP to process SyncML messages ses_generating, ///< ready to perform next STEPCMD_STEP to generate SyncML messages ses_dataready, ///< data is ready to be sent, waiting for STEPCMD_SENTDATA + ses_almostdone, ///< session done, but termination delayed until next step ses_done, ///< session done numServerEngineStates } TServerEngineState; -- cgit v1.2.3 From c03b7e57f628ab9bf23d42bed28fe6fc15658979 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 10 Aug 2012 22:51:03 +0200 Subject: timezones: since 9dd44b3366 (DAYLIGHT recognition adapted), TimeZoneContextToName() ignored the aPrefIdent argument and also its default value by overriding it with "o". This made databases store time zones as Olson names, rather than internal time zone names. However, the reverse function, TimeZoneNameToContext(), understands Olson names only when passed a new (as per 9dd44b3366) parameter olsonSupport as true. But as the default value is false, existing uses of TimeZoneNameToContext() could no longer convert names created by TimeZoneContextToName() back to a valid time zone, which essentially meant loosing time zone information in sync round trips. This patch reverts the override to aPrefIdent="o" in TimeZoneContextToName(), and additionally explicitly sets the new olsonSupport parameter where TimeZoneNameToContext() is used. --- src/DB_interfaces/api_db/pluginapids.cpp | 2 +- src/DB_interfaces/odbc_db/odbcapiagent.cpp | 4 ++-- src/sysync/configelement.cpp | 4 ++-- src/sysync/customimplds.cpp | 4 ++-- src/sysync/itemfield.cpp | 8 ++++---- src/sysync/mimedirprofile.cpp | 6 +++--- src/sysync/scriptcontext.cpp | 4 ++-- src/sysync/timezones.cpp | 6 ++++-- src/sysync_SDK/Sources/lineartime.cpp | 6 +++--- 9 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/DB_interfaces/api_db/pluginapids.cpp b/src/DB_interfaces/api_db/pluginapids.cpp index 2300dc9..0d3101b 100755 --- a/src/DB_interfaces/api_db/pluginapids.cpp +++ b/src/DB_interfaces/api_db/pluginapids.cpp @@ -401,7 +401,7 @@ bool TPluginApiDS::storeField( if (paramScan(aParams,"TZNAME",s)) { // convert to time zone context timecontext_t tctx; - TimeZoneNameToContext(s.c_str(), tctx, tsfP->getGZones()); + TimeZoneNameToContext(s.c_str(), tctx, tsfP->getGZones(), true); tsfP->moveToContext(tctx, true); // move to new context, bind floating (and float fixed, if TZNAME=FLOATING) } } diff --git a/src/DB_interfaces/odbc_db/odbcapiagent.cpp b/src/DB_interfaces/odbc_db/odbcapiagent.cpp index 86dc364..c2983b8 100644 --- a/src/DB_interfaces/odbc_db/odbcapiagent.cpp +++ b/src/DB_interfaces/odbc_db/odbcapiagent.cpp @@ -2560,7 +2560,7 @@ bool TODBCApiAgent::getColumnValueAsField( notnull=getColumnValueAsString(aStatement,aColIndex,val,aDataCharSet, lem_cstr); if (!notnull) goto assignzone; // assign TCTX_UNKNOWN // convert to context - TimeZoneNameToContext(val.c_str(), tctx, getSessionZones()); + TimeZoneNameToContext(val.c_str(), tctx, getSessionZones(), true); goto assignzone; assignoffs: tctx = TCTX_MINOFFSET(moffs); @@ -3145,7 +3145,7 @@ bool TODBCApiAgent::getSQLiteColValueAsField( // get zone name as string appendStringAsUTF8((const char *)sqlite3_column_text(aStatement,aColIndex), val, aDataCharSet, lem_cstr); // convert to context - TimeZoneNameToContext(val.c_str(), tctx, getSessionZones()); + TimeZoneNameToContext(val.c_str(), tctx, getSessionZones(), true); goto assignzone; assignoffs: tctx = TCTX_MINOFFSET(moffs); diff --git a/src/sysync/configelement.cpp b/src/sysync/configelement.cpp index 76aa29c..214efd0 100755 --- a/src/sysync/configelement.cpp +++ b/src/sysync/configelement.cpp @@ -804,8 +804,8 @@ bool TConfigElement::endElement(const char *aElementName, bool aIsDelegated) TzConvertTimestamp(*(fResultPtr.fTimestampP),tctx,TCTX_UTC,getSyncAppBase()->getAppZones()); break; case pamo_timezone: - // time zone by name - if (!TimeZoneNameToContext(fTempString.c_str(), *(fResultPtr.fTimeContextP), getSyncAppBase()->getAppZones())) + // time zone by name (internal or olson) + if (!TimeZoneNameToContext(fTempString.c_str(), *(fResultPtr.fTimeContextP), getSyncAppBase()->getAppZones(), true)) ReportError(false,"invalid/unknown timezone name"); break; case pamo_vtimezone: diff --git a/src/sysync/customimplds.cpp b/src/sysync/customimplds.cpp index 069c9ba..4bd7197 100755 --- a/src/sysync/customimplds.cpp +++ b/src/sysync/customimplds.cpp @@ -3687,8 +3687,8 @@ bool TCustomImplDS::storeField( timecontext_t tctx = TCTX_UNKNOWN; // modify time zone if params contain a TZNAME if (paramScan(aParams,"TZNAME",s)) { - // convert to time zone context - TimeZoneNameToContext(s.c_str(), tctx, tsfP->getGZones()); + // convert to time zone context (olson allowed) + TimeZoneNameToContext(s.c_str(), tctx, tsfP->getGZones(), true); } // now parse text string into field tsfP->setAsISO8601(aValue, tctx, false); diff --git a/src/sysync/itemfield.cpp b/src/sysync/itemfield.cpp index b7f68ed..ada6a71 100644 --- a/src/sysync/itemfield.cpp +++ b/src/sysync/itemfield.cpp @@ -1550,8 +1550,8 @@ bool TTimestampField::setAsRFC822date(cAppCharP aRFC822String, timecontext_t aDe } } else if (isalpha(*p)) { - // could be time zone name (if not, ignore zone spec) - aIgnoreZone = !TimeZoneNameToContext(p,fTimecontext,fGZonesP); + // could be time zone name, internal or olson name (if not, ignore zone spec) + aIgnoreZone = !TimeZoneNameToContext(p,fTimecontext,fGZonesP,true); } } // if no valid zone, use default @@ -2136,8 +2136,8 @@ TSyError TItemFieldKey::SetValueInternal( sval.assign((cAppCharP)aBuffer,aValSize); tctx = TCTX_UNKNOWN; if (!sval.empty()) { - // convert - if (!TimeZoneNameToContext(sval.c_str(), tctx, tsFldP->getGZones())) + // convert (internal or olson names allowed) + if (!TimeZoneNameToContext(sval.c_str(), tctx, tsFldP->getGZones(), true)) return LOCERR_BADPARAM; // bad timezone name } // set context diff --git a/src/sysync/mimedirprofile.cpp b/src/sysync/mimedirprofile.cpp index cb123e7..5b0ecc7 100644 --- a/src/sysync/mimedirprofile.cpp +++ b/src/sysync/mimedirprofile.cpp @@ -1533,9 +1533,9 @@ bool TMimeDirProfileHandler::fieldToMIMEString( tctx = TCTX_MINOFFSET(fldP->getAsInteger()); } else if (!fldP->isEmpty()) { - // string field can be timezone name or numeric minute offset + // string field can be timezone name (internal or olson) or numeric minute offset fldP->getAsString(s); - if (!TimeZoneNameToContext(s.c_str(),tctx,getSessionZones())) { + if (!TimeZoneNameToContext(s.c_str(),tctx,getSessionZones(), true)) { // if not recognized as time zone name, use integer value tctx = TCTX_MINOFFSET(fldP->getAsInteger()); } @@ -3486,7 +3486,7 @@ bool TMimeDirProfileHandler::MIMEStringToField( fPropTZIDtctx = tctx; goto timecontext; } - else if (TimeZoneNameToContext(aText, tctx, getSessionZones())) { + else if (TimeZoneNameToContext(aText, tctx, getSessionZones(), true)) { // found valid TZID property, save it so we can use it for all values of this property that don't specify their own TZ PDEBUGPRINTFX(DBG_ERROR,("Warning: TZID %s could be resolved against internal name, but appropriate VTIMEZONE is missing",aText)); fPropTZIDtctx=tctx; diff --git a/src/sysync/scriptcontext.cpp b/src/sysync/scriptcontext.cpp index b3af39c..88b29ea 100755 --- a/src/sysync/scriptcontext.cpp +++ b/src/sysync/scriptcontext.cpp @@ -341,8 +341,8 @@ public: tctx = TCTX_UNKNOWN; } else { - // search for timezone by name - if (!TimeZoneNameToContext(str.c_str(), tctx, aFuncContextP->getSession()->getSessionZones())) { + // search for timezone by name (internal or olson) + if (!TimeZoneNameToContext(str.c_str(), tctx, aFuncContextP->getSession()->getSessionZones(), true)) { // last attempt is parsing it as a ISO8601 offset spec ISO8601StrToContext(str.c_str(), tctx); } diff --git a/src/sysync/timezones.cpp b/src/sysync/timezones.cpp index 3f8796b..a2a2ecf 100755 --- a/src/sysync/timezones.cpp +++ b/src/sysync/timezones.cpp @@ -889,11 +889,13 @@ bool TimeZoneContextToName( timecontext_t aContext, string &aName, GZones* g, tz_entry t; aName= "UNKNOWN"; + #ifdef ANDROID // setting it via param does not work currently for some reasons, switch it on permanently for Android -//#ifdef ANDROID -- + // BUT NOT GENERALLY - IT CHANGES SEMANTICS OF THE WHOLE TIME ZONE NAMING SYSTEM! + #warning "%%% overriding passed aPrefIdent argument with constant, forcing olson name output" aPrefIdent= "o"; //__android_log_print( ANDROID_LOG_DEBUG, "ContextToName", "pref='%s' / aContext=%d\n", aPrefIdent, aContext ); -//#endif -- + #endif // if aPrefIndent contains "o", this means we'd like to see olson name, if possible // %%% for now, we can return olson for the built-ins only diff --git a/src/sysync_SDK/Sources/lineartime.cpp b/src/sysync_SDK/Sources/lineartime.cpp index 6c8b6f5..f402b24 100755 --- a/src/sysync_SDK/Sources/lineartime.cpp +++ b/src/sysync_SDK/Sources/lineartime.cpp @@ -61,8 +61,8 @@ int timeConv(int argc, const char *argv[]) intime = getSystemNowAs(incontext,&zones); } else { - // get input context from name - if (!TimeZoneNameToContext(inzone.c_str(),incontext,&zones)) + // get input context from name (internal or olson) + if (!TimeZoneNameToContext(inzone.c_str(),incontext,&zones, true)) incontext=TCTX_UNKNOWN; // input time from 3rd argument if (argc!=3) { @@ -95,7 +95,7 @@ int timeConv(int argc, const char *argv[]) s.erase(); ContextToISO8601StrAppend(s, stdoffs, true); CONSOLEPRINTF(("Input time zone represented as TZ/DAYLIGHT :\n\nTZ:%s\nDAYLIGHT:%s\n",s.c_str(),z.c_str())); } - else if (!TimeZoneNameToContext(outzone.c_str(),outcontext,&zones)) + else if (!TimeZoneNameToContext(outzone.c_str(),outcontext,&zones, true)) outcontext=TCTX_UNKNOWN; // now show CONSOLEPRINTF(("")); -- cgit v1.2.3 From 064e876c340f59205561e0153a6f11ff018fd138 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 10 Aug 2012 22:53:36 +0200 Subject: Engine Version 3.4.0.44 --- src/global_options.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/global_options.h b/src/global_options.h index fb978eb..80a1a99 100644 --- a/src/global_options.h +++ b/src/global_options.h @@ -76,8 +76,8 @@ #endif #ifndef SYSYNC_BUILDNUMBER -#define SYSYNC_BUILDNUMBER 42 -#define SYSYNC_BUILDNUMBER_TXT "42" +#define SYSYNC_BUILDNUMBER 44 +#define SYSYNC_BUILDNUMBER_TXT "44" #endif -- cgit v1.2.3 From 740694cb927204f98e65d79a99f2f0de0ab58b52 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Thu, 23 Aug 2012 18:17:21 +0200 Subject: Cocoa wrapper: added conditionals to allow using in ARC code --- src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m b/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m index 7fcd54f..dab2816 100644 --- a/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m +++ b/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m @@ -32,7 +32,9 @@ } } // done + #if !__has_feature(objc_arc) [super dealloc]; + #endif } // dealloc @@ -87,7 +89,9 @@ SettingsKey *sessionKey = [self newOpenSessionKeyWithMode:0 err:&sta]; if (sta==LOCERR_OK) { [sessionKey setStringValueByName:"debugMsg" toValue:aMessage]; + #if !__has_feature(objc_arc) [sessionKey release]; + #endif } } @@ -98,7 +102,9 @@ SettingsKey *sessionKey = [self newOpenSessionKeyWithMode:0 err:&sta]; if (sta==LOCERR_OK) { [sessionKey setStringValueByName:"errorMsg" toValue:aMessage]; + #if !__has_feature(objc_arc) [sessionKey release]; + #endif } } @@ -345,7 +351,9 @@ DBGNSLOG(@"SySync engine platform = '%@'\n", [engineInfo stringValueByName:"platform"]); DBGNSLOG(@"library product name = '%@'\n", [engineInfo stringValueByName:"name"]); DBGNSLOG(@"version comment = '%@'\n", [engineInfo stringValueByName:"comment"]); + #if !__has_feature(objc_arc) [engineInfo release]; + #endif } } } @@ -353,7 +361,9 @@ sta=LOCERR_OUTOFMEM; // probably... // kill or go if (sta!=LOCERR_OK) { + #if !__has_feature(objc_arc) [self dealloc]; + #endif return nil; } // ok @@ -395,7 +405,9 @@ fCI = NULL; DisconnectEngine_Var = NULL; // done + #if !__has_feature(objc_arc) [super dealloc]; + #endif } -- cgit v1.2.3 From 75c78b805e95f38dd3e61d2f5d7371b5f52cd703 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 24 Aug 2012 11:30:28 +0200 Subject: Cocoa wrapper: result of super should be assigned to self in init (Analyzer warning silenced) --- src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m b/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m index dab2816..88a0f36 100644 --- a/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m +++ b/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m @@ -9,7 +9,7 @@ - (id)initWithCI:(UI_Call_In)aCI andSessionHandle:(SessionH)aSessionH; { - if ([super init]!=nil) { + if ((self = [super init])) { fCI = aCI; fSessionH = aSessionH; // also prepare tunnel wrapper @@ -301,7 +301,7 @@ // empty prefix if none specified if (aEntryPointPrefix==nil) aEntryPointPrefix = @""; // init superclass and DLL - if (self == [super init]) { + if ((self = [super init])) { ConnectEngine_Func ConnectEngine_Var = NULL; #ifdef SYSYNC_ENGINE_STATIC // static: just directly use the entry point. -- cgit v1.2.3 From 1b18b78f0e1fbc91c6a88616dccedc5f0dcefe38 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Wed, 29 Aug 2012 10:51:20 +0200 Subject: engine: item counting corrected (before, items that could not be sent in the current message and were queued for the next message were not counted in the statistics and also not reported via pev_itemsent progress event) --- src/sysync/stdlogicds.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sysync/stdlogicds.cpp b/src/sysync/stdlogicds.cpp index 1020e54..372e90c 100644 --- a/src/sysync/stdlogicds.cpp +++ b/src/sysync/stdlogicds.cpp @@ -880,15 +880,16 @@ bool TStdLogicDS::logicGenerateSyncCommandsAsServer( syncopcmdP=NULL; // possibly, we have a NULL command here (e.g. in case it could not be generated due to MaxObjSize restrictions) if (cmdP) { + // We pass the command to the issue mechanism - last chance to count is here. + // Note: the command might be queued and actually sent in a subsequent message. + fItemsSent++; // overall counter for statistics + itemcount++; // per message counter + DB_PROGRESS_EVENT(this,pev_itemsent,fItemsSent,getNumberOfChanges(),0); // send event (but no check for abort) + // now issue if (!fSessionP->issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) { alldone=false; // issue failed (no room in message), not finished so far - break; + break; // stop trying to issue more commands } - // count item sent - fItemsSent++; // overall counter for statistics - itemcount++; // per message counter - // send event (but no check for abort) - DB_PROGRESS_EVENT(this,pev_itemsent,fItemsSent,getNumberOfChanges(),0); } }; // while not aborted and not message full // we are not done until all aNextMessageCommands are also out -- cgit v1.2.3 From 04abaaf30b19f747569b719492a901b13b38c8b7 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Thu, 30 Aug 2012 14:08:59 +0200 Subject: engine: previous patch was accidentally server-only, same correction needed for client case as well. --- src/sysync/stdlogicds.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/sysync/stdlogicds.cpp b/src/sysync/stdlogicds.cpp index 372e90c..6a8d75d 100644 --- a/src/sysync/stdlogicds.cpp +++ b/src/sysync/stdlogicds.cpp @@ -1040,19 +1040,23 @@ bool TStdLogicDS::logicGenerateSyncCommandsAsClient( syncopcmdP=NULL; // possibly, we have a NULL command here (e.g. in case it could not be generated due to MaxObjSize restrictions) if (cmdP) { - if (!fSessionP->issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) { - alldone=false; // issue failed (no room in message), not finished so far - break; - } - // count item sent + // We pass the command to the issue mechanism - last chance to count is here. + // Note: the command might be queued and actually sent in a subsequent message. fItemsSent++; - // send event and check for abort #ifdef PROGRESS_EVENTS + // send progress event and check for abort if (!DB_PROGRESS_EVENT(this,pev_itemsent,fItemsSent,getNumberOfChanges(),0)) { implEndDataRead(); // terminate reading fSessionP->AbortSession(500,true,LOCERR_USERABORT); return false; // error } + #endif + // issue now + if (!fSessionP->issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) { + alldone=false; // issue failed (no room in message), not finished so far + break; + } + #ifdef PROGRESS_EVENTS // check for "soft" suspension if (!SESSION_PROGRESS_EVENT(fSessionP,pev_suspendcheck,NULL,0,0,0)) { fSessionP->SuspendSession(LOCERR_USERSUSPEND); -- cgit v1.2.3 From 78730b76d728e456bc63a54abe15a925051469f0 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Thu, 2 Feb 2012 17:41:44 +0100 Subject: Abort sync of the datastore when filters specified for SyncML version < 1.2 Before, we only sent back a 406 status, but tried to continue sync of the affected datastore. --- src/sysync/localengineds.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index 0e269bd..e8d3565 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -3512,6 +3512,7 @@ localstatus TLocalEngineDS::engGenerateClientSyncAlert( if (!fRemoteRecordFilterQuery.empty() || false /* %%% field level filter */) { if (fSessionP->getSyncMLVersion() Date: Thu, 2 Feb 2012 17:43:09 +0100 Subject: engine 3.4.0.41: Added "errorMsg" and "debugMsg" write-only session key values - these can be used to write messages into the debug log (such as transport level problems) --- src/global_options.h | 4 ++-- src/sysync/syncagent.cpp | 33 +++++++++++++++++++++++++++ src/sysync_SDK/sdk_sources_cocoa/SyncEngine.h | 3 +++ src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m | 25 ++++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/global_options.h b/src/global_options.h index d71866f..39be1bb 100644 --- a/src/global_options.h +++ b/src/global_options.h @@ -76,8 +76,8 @@ #endif #ifndef SYSYNC_BUILDNUMBER -#define SYSYNC_BUILDNUMBER 40 -#define SYSYNC_BUILDNUMBER_TXT "40" +#define SYSYNC_BUILDNUMBER 41 +#define SYSYNC_BUILDNUMBER_TXT "41" #endif diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index fd61ea4..921e385 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -3761,6 +3761,36 @@ static TSyError readDisplayAlert( #endif // SYSYNC_CLIENT +// - write error message into session log +TSyError writeErrorMsg( + TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, + cAppPointer aBuffer, memSize aValSize +) +{ + TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); + string msg; + msg.assign((cAppCharP)aBuffer, aValSize); + POBJDEBUGPRINTFX(mykeyP->fAgentP,DBG_ERROR,("external Error: %s",msg.c_str())); + return LOCERR_OK; +} // writeErrorMsg + + +// - write debug message into session log +TSyError writeDebugMsg( + TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, + cAppPointer aBuffer, memSize aValSize +) +{ + TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); + string msg; + msg.assign((cAppCharP)aBuffer, aValSize); + POBJDEBUGPRINTFX(mykeyP->fAgentP,DBG_HOT,("external Message: %s",msg.c_str())); + return LOCERR_OK; +} // writeDebugMsg + + + + // accessor table for server session key static const TStructFieldInfo ServerParamFieldInfos[] = { @@ -3784,6 +3814,9 @@ static const TStructFieldInfo ServerParamFieldInfos[] = { "displayalert", VALTYPE_TEXT, false, 0, 0, &readDisplayAlert, NULL }, #endif #endif + // write into debug log + { "errorMsg", VALTYPE_TEXT, true, 0, 0, NULL, &writeErrorMsg }, + { "debugMsg", VALTYPE_TEXT, true, 0, 0, NULL, &writeDebugMsg }, }; // get table describing the fields in the struct diff --git a/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.h b/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.h index f4b2d55..ffb94be 100644 --- a/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.h +++ b/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.h @@ -26,6 +26,9 @@ // read and write the SyncML messages - (TSyError)getSyncMLBufferForSend:(BOOL)aForSend buffer:(appPointer *)aBuffer bufSize:(memSize *)aBufSize; - (TSyError)retSyncMLBufferForSend:(BOOL)aForSend processed:(memSize)aProcessed; +// writing to session log +- (void)debugLog:(NSString *)aMessage; +- (void)errorLog:(NSString *)aMessage; // Tunnel DB API - (TSyError)startDataReadWithLastToken:(cAppCharP)aLastToken andResumeToken:(cAppCharP)aResumeToken; - (TSyError)readNextItemAsKey:(SettingsKey *)aItemKey diff --git a/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m b/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m index e44bbf2..7fcd54f 100644 --- a/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m +++ b/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m @@ -80,6 +80,31 @@ } +// writing to session log +- (void)debugLog:(NSString *)aMessage +{ + TSyError sta; + SettingsKey *sessionKey = [self newOpenSessionKeyWithMode:0 err:&sta]; + if (sta==LOCERR_OK) { + [sessionKey setStringValueByName:"debugMsg" toValue:aMessage]; + [sessionKey release]; + } +} + + +- (void)errorLog:(NSString *)aMessage +{ + TSyError sta; + SettingsKey *sessionKey = [self newOpenSessionKeyWithMode:0 err:&sta]; + if (sta==LOCERR_OK) { + [sessionKey setStringValueByName:"errorMsg" toValue:aMessage]; + [sessionKey release]; + } +} + + + + // Tunnel DB API - (TSyError)startDataReadWithLastToken:(cAppCharP)aLastToken andResumeToken:(cAppCharP)aResumeToken -- cgit v1.2.3 From b5c422de0cc1c49de07c4bbce64e043a8b539425 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Mon, 6 Feb 2012 11:44:22 +0100 Subject: sync agent: code refactoring for allFromClientOnly The code for setting allFromClientOnly occurred twice -> moved into separate method. --- src/sysync/syncagent.cpp | 46 ++++++++++++++++++++++------------------------ src/sysync/syncagent.h | 4 ++++ 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index 921e385..c77873a 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -1438,6 +1438,26 @@ bool TSyncAgent::ClientMessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand } // TSyncAgent::ClientMessageStarted +bool TSyncAgent::checkAllFromClientOnly() +{ + bool allFromClientOnly=false; + // Note: the map phase will not take place, if all datastores are in + // send-to-server-only mode and we are not in non-conformant old + // synthesis-compatible fCompleteFromClientOnly mode. + if (!fCompleteFromClientOnly) { + // let all local datastores know that message has ended + allFromClientOnly=true; + for (TLocalDataStorePContainer::iterator pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { + // check sync modes + if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) { + allFromClientOnly=false; + break; + } + } + } + return allFromClientOnly; +} + // determines new package states and sets fInProgress void TSyncAgent::ClientMessageEnded(bool aIncomingFinal) { @@ -1469,20 +1489,7 @@ void TSyncAgent::ClientMessageEnded(bool aIncomingFinal) } else { fInProgress=true; // assume we need to continue - // Note: the map phase will not take place, if all datastores are in - // send-to-server-only mode and we are not in non-conformant old - // synthesis-compatible fCompleteFromClientOnly mode. - if (!fCompleteFromClientOnly) { - // let all local datastores know that message has ended - allFromClientOnly=true; - for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { - // check sync modes - if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) { - allFromClientOnly=false; - break; - } - } - } + allFromClientOnly=checkAllFromClientOnly(); // new outgoing state is determined by the incomingState of this message // (which is the answer to the message of the previous outgoing package) if (fNewOutgoingPackage && fIncomingState!=psta_idle) { @@ -1947,16 +1954,7 @@ void TSyncAgent::ServerMessageEnded(bool aIncomingFinal) // no next phase anyway // find out if this is a shortened session (no map phase) due to // from-client-only in all datastores - if (!fCompleteFromClientOnly) { - allFromClientOnly=true; - for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { - // check sync modes - if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) { - allFromClientOnly=false; - break; - } - } - } + allFromClientOnly=checkAllFromClientOnly(); // determine what package comes next switch (fIncomingState) { case psta_init : diff --git a/src/sysync/syncagent.h b/src/sysync/syncagent.h index 324f98b..7186391 100755 --- a/src/sysync/syncagent.h +++ b/src/sysync/syncagent.h @@ -493,6 +493,10 @@ protected: bool fAbortRequested; #endif // ENGINE_LIBRARY + // true if all active stores are in "from client only" mode and + // the engine is not in the fCompleteFromClientOnly mode + // => map phase can be skipped + bool checkAllFromClientOnly(); }; // TSyncAgent -- cgit v1.2.3 From 789ff025b15de225cf844edec35467ac71e03871 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Mon, 6 Feb 2012 11:46:27 +0100 Subject: plugin API: pass firstReadNextItem=true on first call The firstReadNextItem parameter of ReadNextItem() was always false. The plugin was implicitly expected to start at the beginning. Now the parameter is "true" once for the first call. Plugins which ignore the parameter continue to work as before. Plugins which check it should reset their "list items state", as required by the API. The main reason for changing this is the new "use plugin instance in multiple SyncML sessions" feature: if enabled for the data store, listing items with ReadNextItem() will be done multiple times and then reseting the "list item state" is essential. --- src/DB_interfaces/api_db/pluginapids.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/DB_interfaces/api_db/pluginapids.cpp b/src/DB_interfaces/api_db/pluginapids.cpp index 6dcf2b9..2300dc9 100755 --- a/src/DB_interfaces/api_db/pluginapids.cpp +++ b/src/DB_interfaces/api_db/pluginapids.cpp @@ -1071,6 +1071,9 @@ localstatus TPluginApiDS::apiReadSyncSet(bool aNeedAll) // (we won't retrieve anything in case of slow refresh, because after zapping there's nothing left by definition) if (!fRefreshOnly || (fSlowSync && apiNeedSyncSetToZap()) || (!fSlowSync && implNeedSyncSetToRetrieve())) { SYSYNC_TRY { + // true for initial ReadNextItem*() call, false later on + bool firstReadNextItem=true; + // read the items #if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT) TMultiFieldItem *mfitemP = NULL; @@ -1101,7 +1104,7 @@ localstatus TPluginApiDS::apiReadSyncSet(bool aNeedAll) } // as key (Note: will be functional key but w/o any fields in case we pass NULL item pointer) TDBItemKey *itemKeyP = newDBItemKey(mfitemP); - dberr=fDBApi_Data.ReadNextItemAsKey(itemAndParentID, (KeyH)itemKeyP, itemstatus); + dberr=fDBApi_Data.ReadNextItemAsKey(itemAndParentID, (KeyH)itemKeyP, itemstatus, firstReadNextItem); // check if plugin wrote something to our key. If so, we assume this is the item and save // it, EVEN IF we did not request getting item data. if (!itemKeyP->isWritten()) { @@ -1122,11 +1125,12 @@ localstatus TPluginApiDS::apiReadSyncSet(bool aNeedAll) #ifdef DBAPI_TEXTITEMS { // as text item - dberr=fDBApi_Data.ReadNextItem(itemAndParentID, itemData, itemstatus); + dberr=fDBApi_Data.ReadNextItem(itemAndParentID, itemData, itemstatus, firstReadNextItem); } #else return LOCERR_WRONGUSAGE; // completely wrong usage - should never happen as compatibility is tested at module connect #endif + firstReadNextItem=false; if (dberr!=LOCERR_OK) { PDEBUGPRINTFX(DBG_ERROR,("DBapi::ReadNextItem fatal error = %hd",dberr)); #if defined(DBAPI_ASKEYITEMS) && defined(ENGINEINTERFACE_SUPPORT) -- cgit v1.2.3 From 2dffd37d179a38a565690d15768a51db847eaf83 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 7 Feb 2012 10:42:30 +0100 Subject: TCustomImplDS::implMarkOnlyUngeneratedForResume: fixed endless loop The while() loop gets stuck if any entry in the map has the "delete" member set, because then the code does a "continue" which doesn't move the pointer beyond that entry. Fixed by using a for() loop. Found while experimenting with better 409 conflict handling in the binfile client, where one artificial local ID is marked as deleted. --- src/sysync/customimplds.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/sysync/customimplds.cpp b/src/sysync/customimplds.cpp index 027174a..7b97bea 100755 --- a/src/sysync/customimplds.cpp +++ b/src/sysync/customimplds.cpp @@ -1791,13 +1791,10 @@ void TCustomImplDS::implMarkOnlyUngeneratedForResume(void) bool getPrepared = fGetPhasePrepared; // now flag all deletes that need resuming if (getPhase==gph_deleted) { - // if we are still in "deleted" phase, add these first - if (!getPrepared) - pos=fMapTable.begin(); - else - pos=fDeleteMapPos; - // now mark pending deletes - while (pos!=fMapTable.end()) { + // now mark pending deletes; if we are still in "deleted" phase, add these first + for (pos = !getPrepared ? fMapTable.begin() : fDeleteMapPos; + pos!=fMapTable.end(); + ++pos) { // check only undeleted map entries // Note: non-normal maps are always in deleted state in fMapTable, so these will be skipped as well if ((*pos).deleted) continue; @@ -1806,8 +1803,6 @@ void TCustomImplDS::implMarkOnlyUngeneratedForResume(void) // mark this as pending for resume (*pos).markforresume=true; } - // next - ++pos; } getPhase=gph_added_changed; getPrepared=false; -- cgit v1.2.3 From 2df6c96ed9e358f5c307455c832b6af86eca066b Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 7 Feb 2012 10:56:52 +0100 Subject: DB_Conflict (409): different implementation in binfileimplds.cpp The previous approach to handling a 409 status for an (= item conflicts with existing item) in the binfile client was to update that existing item and then tell the server that its item maps to an existing client item. This leads to the situation where the server has two client IDs mapping to the same item on the server and one server item with no corresponding client item. Servers get confused by this. For example, the Synthesis engine itself then sends a with empty IDs to the client in the next sync. It also has the disadvantage that the client cannot ask the server to delete the redundant item, because its requests must include a client ID, which the redundant item doesn't have. Furthermore, the server was sent a redundant in the case that it already had an item that was in sync in the client (= item didn't have to be written in local storage). Therefore this commit implements a different approach: - the new server item which triggers the 409 is used to update the existing item, as before - if and only if the local item gets modified, it will be sent as update for the older server item already mapped to it (there must be such an item, because the client must have told the server about that local item, and that server item is now out-dated) - the new server item is mapped to a fake client ID which is marked as deleted; in the next sync, that mapping is used to delete the new server item Once a second sync is done, client and server are in sync again, with the latest data as determined by the client stored in the servers older item and the newer item deleted. Generating a fake client ID is a bit hacky at the moment. The code for numeric IDs is entirely untested, the code for string IDs doesn't check for (unlikely?!) collisions. --- src/sysync/binfileimplds.cpp | 57 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/src/sysync/binfileimplds.cpp b/src/sysync/binfileimplds.cpp index ea8422e..09ec0aa 100755 --- a/src/sysync/binfileimplds.cpp +++ b/src/sysync/binfileimplds.cpp @@ -1907,6 +1907,7 @@ bool TBinfileImplDS::implProcessItem( bool ok; bool receiveOnly=false; // default to normal two-way bool reportAsChangedInNextSync=false; + bool reportAsDeletedInNextSync=false; TMultiFieldItem *augmentedItemP = NULL; SYSYNC_TRY { @@ -1936,18 +1937,30 @@ bool TBinfileImplDS::implProcessItem( if (augmentedItemP==NULL) sta = DB_Error; // no item found, DB error else { - // if merged version differs from what was received, we'll need to - // sync it back in next session - if (changedNewVersion) reportAsChangedInNextSync = true; // store augmented version back to DB only if modified if (changedDBVersion) { STR_TO_LOCALID(augmentedItemP->getLocalID(),localid); sta = updateItemByID(localid,augmentedItemP); + // we'll need to sync it back in the next session, + // because the server's copy is now older than the + // client's copy + if (sta == LOCERR_OK) reportAsChangedInNextSync = true; } else { sta = LOCERR_OK; } } + // The server now has two items where the client only has + // one. If we were to tell the server the same local ID + // for both of the server's items, it would get confused. + // For example, the Synthesis engine then sent a Delete + // request with empty client and server ID. + // + // Instead, let the rest of the code below proceed (to get + // the state of the existing local item right), and then + // create a unique, fake local ID that we assign to the + // item and copy into the change log as "deleted item". + if (sta == LOCERR_OK) reportAsDeletedInNextSync = true; } else { statuscode=sta; @@ -2091,6 +2104,44 @@ bool TBinfileImplDS::implProcessItem( // Note: changeLogPostflight() will check these for temporary localids and finalize them when needed fChangeLog.newRecord(affectedentryP); } + if (reportAsDeletedInNextSync) { + #ifdef NUMERIC_LOCALIDS + // assumes that valid local IDs are positive + // and can hold values in the range of -1 to -RAND_MAX-1 + localid_t fakelocalid = -rand() - 1; + #else + // TODO: check for collisions with other fake IDs + char fakelocalid[STRING_LOCALID_MAXLEN]; + sprintf(fakelocalid, "fake-%d", rand()); + #endif + memset(&newentry, 0, sizeof(newentry)); + ASSIGN_LOCALID_TO_FLD(newentry.dbrecordid,fakelocalid); + ASSIGN_LOCALID_TO_ITEM(*aItemP,fakelocalid); + newentry.flags = chgl_deleted; + // send during next sync + newentry.modcount_created = + newentry.modcount = fCurrentModCount+1; + #ifdef NUMERIC_LOCALIDS + DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,( + "fake new entry %ld : localID=%ld, flags=0x%X, modcount=modcount_created=%ld, new dataCRC=0x%hX", + (long)fChangeLog.getNumRecords(), + newentry.dbrecordid, + (int)newentry.flags, + (long)newentry.modcount, + newentry.dataCRC + )); + #else + DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,( + "fake new entry %ld : localID='%s', flags=0x%X, modcount=modcount_created=%ld, new dataCRC=0x%hX", + (long)fChangeLog.getNumRecords(), + newentry.dbrecordid, + (int)newentry.flags, + (long)newentry.modcount, + newentry.dataCRC + )); + #endif + fChangeLog.newRecord(&newentry); + } // done ok=true; goto done; -- cgit v1.2.3 From 250b31738da56f499a528142835b1bf83070f8c6 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 7 Feb 2012 15:46:29 +0100 Subject: SyncML extensions: multiple cycles in the same session SyncML limits the data exchange to "client sends changes, server sends changes back". If the client detects that it has further changes for the server while already in the second phase, it will have to wait for the next SyncML session before it can send these changes. This leads to a bad user experience, because the user expectation is that client and server are in sync after a sync session. Examples when this becomes a problem: - 409 conflict detected in binfile client (tested with SyncEvolution Client::Sync::eds_event::testAddBothSides[Refresh] and a SyncEvolution server with file backend which doesn't detect duplicates based on UID) - temporary error prevents applying a change, needs to be retried (untested at the moment) - PBAB use case: advanced client does a sync with a subset of the data (vCard without PHOTO, while retrieving PHOTO data in the background), then sends updates with more data (modified/new PHOTOs) later on (also untested) This commit introduces a new mode where SyncML client and server allow multiple read/write cycles inside the same session. This extends the SyncML 1.2 standard in the following way: - in refresh-from-client mode, client and server always enter the map phase although the standard says that it can and must be skipped; the Synthesis engine already supported that non-standard behavior via the fCompleteFromClientOnly flag (aka in the config and remote rules) - the client, if it has more changes for the server and hasn't encountered problem, finishes the current cycle internally and then sends a new in the next message to start the next cycle, instead of ending the map phase and package with a tag - the server starts at the beginning of a new SyncML session when it gets that , instead of rejecting it as a protocol violation - the first sync mode is done as set by the app; any following one falls back to incremental mode: - slow sync -> two-way sync or one-way if data direction was limited - refresh sync -> one-way sync in same direction - one-way sync -> do it again To simplify testing, this new functionality is currently limited to sync sessions with only a single active data store. The new mode has to be enabled *on both sides* with the config option. When only enabled on the client side, the unexpected will be rejected by the serer. When only enabled on the server side, the client might be surprised by the non-standard map phase behavior. SyncEvolution enables this new mode unconditionally with a remote rule when talking to itself. Ideally this should be replaced with a DevInf extension, both for "SyncML engine supports the mode" and "data store supports restarting". Data stores must be able to deal with repeated cycles in the same session. Typically this consists of reseting some state so that running another sync cycle doesn't reuse obsolete information. The binfile DS supports this now, but the underlying data store in the app might not be ready for it yet. Currently the engine does not check this. The binfile client automatically recognizes the 409 conflict case and enters a new sync cycle to get the server in sync with the client. In addition, any client can be told to restart the sync by setting the new "restartsync" session variable (PBAP use case). This has to happen before the client considers the session completed, i.e. before EndDataWrite() is called. It would be better to do this with a per-datastore flag, but it wasn't obvious how to expose such a flag via the engine API. While looking at the code it was noticed that issueCustomEndPut() is not called when running in standard-compliant refresh-from-client mode, because the code is only invoked at the end of the map phase, which is skipped in that mode. A bug?! Not fixed. --- src/sysync/binfileimplds.cpp | 55 +++++++++++++++ src/sysync/binfileimplds.h | 7 ++ src/sysync/customimplds.cpp | 3 + src/sysync/localengineds.h | 4 ++ src/sysync/syncagent.cpp | 157 ++++++++++++++++++++++++++++++++++++++++--- src/sysync/syncagent.h | 17 +++++ src/sysync/syncsession.cpp | 13 ++++ src/sysync/syncsession.h | 3 + 8 files changed, 248 insertions(+), 11 deletions(-) diff --git a/src/sysync/binfileimplds.cpp b/src/sysync/binfileimplds.cpp index 09ec0aa..70a2196 100755 --- a/src/sysync/binfileimplds.cpp +++ b/src/sysync/binfileimplds.cpp @@ -331,6 +331,7 @@ TBinfileImplDS::TBinfileImplDS( // no changelog loaded yet (needed here because InternalResetDataStore will test it) fLoadedChangeLog=NULL; fLoadedChangeLogEntries=0; // just to make sure + fHasPendingChanges=false; fPreviousToRemoteModCount=0; fPreviousSuspendModCount=0; fCurrentModCount=0; @@ -363,6 +364,8 @@ void TBinfileImplDS::InternalResetDataStore(void) fPreflighted=false; // forget loaded changelog forgetChangeLog(); + // no pending changes known yet + fHasPendingChanges=0; // unknown number of changes fNumberOfLocalChanges=-1; } // TBinfileImplDS::InternalResetDataStore @@ -398,6 +401,11 @@ localstatus TBinfileImplDS::dsBeforeStateChange(TLocalEngineDSState aOldState,TL /// inform logic of happened state change localstatus TBinfileImplDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLocalEngineDSState aNewState) { + if (aNewState == dssta_completed) { + // free resources which are not valid when restarting, + // in particular the change log + InternalResetDataStore(); + } return inherited::dsAfterStateChange(aOldState, aNewState); } // TBinfileImplDS::dsAfterStateChange @@ -1108,6 +1116,51 @@ sInt32 TBinfileImplDS::getNumberOfChanges(void) return inherited::getNumberOfChanges(); } +bool TBinfileImplDS::hasPendingChangesForNextSync() +{ + if (binfileDSActive() && IS_CLIENT) { + // shortcut? + if (fHasPendingChanges) { + PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("sync session known to be incomplete")); + return true; + } + // also check change log, just to be sure; + // note that new entries in the change log that were + // added during the current sync session are not yet loaded, + // and that entries in memory may still need to be flushed + // to disk + if (fLoadedChangeLog) { + for (uInt32 si=0; si fCurrentModCount) { +#ifdef NUMERIC_LOCALIDS + PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,( + "%ld : localID=%ld, flags=0x%X, modcount=%ld, modcount_created=%ld => pending change", + (long)si, + fLoadedChangeLog[si].dbrecordid, + (int)fLoadedChangeLog[si].flags, + (long)fLoadedChangeLog[si].modcount, + (long)fLoadedChangeLog[si].modcount_created + )); +#else + PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,( + "%ld : localID='%s', flags=0x%X, modcount=%ld, modcount_created=%ld => pending change", + (long)si, + fLoadedChangeLog[si].dbrecordid, + (int)fLoadedChangeLog[si].flags, + (long)fLoadedChangeLog[si].modcount, + (long)fLoadedChangeLog[si].modcount_created + )); +#endif + return true; + } + } + } + return false; + } + return inherited::getNumberOfChanges(); +} + + /// sync login (into this database) /// @note might be called several times (auth retries at beginning of session) @@ -2058,6 +2111,7 @@ bool TBinfileImplDS::implProcessItem( // make sure NEXT sync will catch this again, as stored version is different // from what we received (merged with pre-existing duplicate) affectedentryP->modcount=fCurrentModCount+1; + fHasPendingChanges=true; } else { // just current mod count @@ -2105,6 +2159,7 @@ bool TBinfileImplDS::implProcessItem( fChangeLog.newRecord(affectedentryP); } if (reportAsDeletedInNextSync) { + fHasPendingChanges=true; #ifdef NUMERIC_LOCALIDS // assumes that valid local IDs are positive // and can hold values in the range of -1 to -RAND_MAX-1 diff --git a/src/sysync/binfileimplds.h b/src/sysync/binfileimplds.h index 07beb49..04bff07 100755 --- a/src/sysync/binfileimplds.h +++ b/src/sysync/binfileimplds.h @@ -779,6 +779,9 @@ protected: /// called for SyncML 1.1 if remote wants number of changes. /// Must return -1 no NOC value can be returned virtual sInt32 getNumberOfChanges(void); + /// checks change log for already known changes + virtual bool hasPendingChangesForNextSync(); + // Simple custom DB access interface methods /// sync login (into this database) /// @note might be called several times (auth retries at beginning of session) @@ -967,6 +970,10 @@ private: // - entire change log, loaded into memory for quick reference during write phase TChangeLogEntry *fLoadedChangeLog; uInt32 fLoadedChangeLogEntries; + // - true if there are known pending changes for the next sync + // (necessary for hasPendingChangesForNextSync() + // because not all of the change log is always in memory) + bool fHasPendingChanges; #endif // CHECKSUM_CHANGELOG /// pending maps for Resume TBinFile fPendingMaps; ///< pending map binfile diff --git a/src/sysync/customimplds.cpp b/src/sysync/customimplds.cpp index 7b97bea..094d2bb 100755 --- a/src/sysync/customimplds.cpp +++ b/src/sysync/customimplds.cpp @@ -1113,6 +1113,9 @@ localstatus TCustomImplDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLoc fAgentP->fScriptContextDatastore=this; TScriptContext::execute(fScriptContextP,fConfigP->fSyncEndScript,fConfigP->getDSFuncTableP(),fAgentP); #endif + // reset in case that we restart + DeleteSyncSet(); + fSyncSetLoaded=false; } // let inherited do its stuff as well return inherited::dsAfterStateChange(aOldState,aNewState); diff --git a/src/sysync/localengineds.h b/src/sysync/localengineds.h index 7d34917..e1680a1 100755 --- a/src/sysync/localengineds.h +++ b/src/sysync/localengineds.h @@ -558,6 +558,10 @@ public: // called for >=SyncML 1.1 if remote wants number of changes. // Must return -1 if no NOC value can be returned virtual sInt32 getNumberOfChanges(void) { return -1; /* no NOC supported */ }; + /// Called at end of sync to determine whether the store already knows + /// that it has more changes for the server in the next sync session. + /// For example, the TBinFileImplDS looks at its change log to determine that. + virtual bool hasPendingChangesForNextSync() { return false; } /// test abort status, datastore is aborted also when session is just suspended bool isAborted(void); /// abort status code with local error code prefix if cause was local diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index c77873a..b7b38c8 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -636,6 +636,8 @@ TSyncAgent::TSyncAgent( fIncomingBytes = 0; fOutgoingBytes = 0; #endif + fRestartSyncOnce = false; + fRestartingSync = false; // Specific for Client or Server if (IS_CLIENT) { @@ -963,7 +965,80 @@ localstatus TSyncAgent::processAnswer(void) return LOCERR_OK; } // TSyncAgentBase::processAnswer +bool TSyncAgent::restartSync() +{ + if (IS_CLIENT) { + // Restarting needs to be done if: + // - server supports it (currently assumed to be possible in non-standard + // fCompleteFromClientOnly mode - TODO: introduce DevInf extension for this) + // - we only have one datastore (intentional simplification for now) + // - that datastore supports restarting a sync (= multiple read/write cycles) + // one client and server side (TODO: DevInf extension for server side) + // - datastore hasn't failed in current iteration + // - client has pending changes: + // - server temporarily rejected a change, queued for resending + // - an item added by the server was merged with another + // item locally (might have an updated queued, need to send delete) + // - change on client failed temporarily + // - the app or a datastore asked for a restart via the "restartsync" + if (fCompleteFromClientOnly && + !getenv("LIBSYNTHESIS_NO_RESTART")) { + bool restartPossible=true; // ... unless proven otherwise below + bool restartNecessary=fRestartSyncOnce; // one reason for restarting: requested by app + int numActive = 0; + + // clear the flag after we checked it + fRestartSyncOnce=false; + if (fRestartSyncOnce) + PDEBUGPRINTFX(DBG_SESSION,("try to restart sync as requested by app")); + + for (TLocalDataStorePContainer::iterator pos=fLocalDataStores.begin(); + pos!=fLocalDataStores.end(); + ++pos) { + TLocalEngineDS *localDS = *pos; + if (localDS->isActive()) { + numActive++; + if (!localDS->canRestart()) { + PDEBUGPRINTFX(DBG_SESSION,("cannot restart, %s does not support it", + localDS->getName())); + restartPossible=false; + break; + } + if (localDS->isAborted()) { + PDEBUGPRINTFX(DBG_SESSION,("cannot restart, %s faileed", + localDS->getName())); + restartPossible=false; + break; + } + } + // check for pending local changes in the client + if (localDS->numUnsentMaps() > 0) { + PDEBUGPRINTFX(DBG_SESSION,("try to restart, %s has pending map entries", + localDS->getName())); + restartNecessary=true; + } + // TODO: detect temporarily failed items (server sent an add/update/delete + // that we had to reject temporarily and where we expect the server to + // resend the request) + // detect pending changes (for example, 409 handling in binfile client) + if (localDS->hasPendingChangesForNextSync()) { + PDEBUGPRINTFX(DBG_SESSION,("try to restart, %s has pending changes", + localDS->getName())); + restartNecessary=true; + } + } + if (numActive > 1) { + PDEBUGPRINTFX(DBG_SESSION,("cannot restart, %d data stores active", numActive)); + restartPossible=false; + } + + return restartPossible && restartNecessary; + } + } + + return false; +} // let session produce (or finish producing) next message into // SML workspace @@ -1344,15 +1419,33 @@ localstatus TSyncAgent::NextMessage(bool &aDone) fOutgoingAlert222Count = 0; } - // send custom end-of session puts + // Normally the package will be closed normally when the client is + // ready. The exception is if we might want to restart the sync + // session. This is checked below. + bool finalprevented = false; + + // send custom end-of session puts or restart session if (!isSuspending() && outgoingfinal && fOutgoingState==psta_map) { // End of outgoing map package; let custom PUTs which may transmit some session statistics etc. happen now + // TODO: this code is not reached when fOutgoingState==psta_map is skipped + // by ClientMessageEnded() (see "All datastores in from-client-only mode, and no need to answer: skip map phase"). + // A bug? For restarting a sync, the problem is avoided by + // disabling the standard-compliant "skip map phase" in favor + // of "let client enter map phase" mode (fCompleteFromClientOnly). issueCustomEndPut(); + + if (restartSync()) { + // don't allow if we are going to restart sync in + // ClientMessageEnded(), also remember that we did that + finalprevented=true; + // flag for TSyncAgent::ClientMessageEnded() + fRestartingSync=true; + } } // message complete, now finish it FinishMessage( outgoingfinal, // allowed if possible - false // final not prevented + finalprevented // final not allowed when restarting sync ); // Note, now fNewOutgoingPackage is set (by FinishMessage()) // if next message will be responded to with a new package @@ -1526,7 +1619,11 @@ void TSyncAgent::ClientMessageEnded(bool aIncomingFinal) } // New incoming state is simply derived from the incoming state of // this message - if (aIncomingFinal && fIncomingState!=psta_idle) { + if (fRestartingSync) { + PDEBUGPRINTFX(DBG_HOT,("MessageEnded: restart sync")); + fOutgoingState=psta_init; + fIncomingState=psta_init; + } else if (aIncomingFinal && fIncomingState!=psta_idle) { if (fIncomingState==psta_init) { // switch to sync fIncomingState=psta_sync; @@ -1562,20 +1659,36 @@ void TSyncAgent::ClientMessageEnded(bool aIncomingFinal) )); // let all local datastores know that message has ended for (pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { + TLocalEngineDS *localDS = *pos; // let them know - (*pos)->engEndOfMessage(); + localDS->engEndOfMessage(); + if (fRestartingSync) { + // finish current session as far as the datastore is concerned + localDS->engFinishDataStoreSync(LOCERR_OK); + + // and start again + localDS->changeState(dssta_adminready); + localDS->fFirstTimeSync = false; + + // unsetting flow sync leads to new sync mode: + // - slow sync -> two-way sync or one-way if data direction was limited + // - refresh sync -> one-way sync in same direction + // - one-way sync -> do it again + localDS->fSlowSync = false; + } // Show state of local datastores PDEBUGPRINTFX(DBG_HOT,( "Local Datastore '%s': %sState=%s, %s%s sync, %s%s", - (*pos)->getName(), - (*pos)->isAborted() ? "ABORTED - " : "", - (*pos)->getDSStateName(), - (*pos)->isResuming() ? "RESUMED " : "", - (*pos)->isSlowSync() ? "SLOW" : "normal", - SyncModeDescriptions[(*pos)->getSyncMode()], - (*pos)->fServerAlerted ? ", Server-Alerted" : "" + localDS->getName(), + localDS->isAborted() ? "ABORTED - " : "", + localDS->getDSStateName(), + localDS->isResuming() ? "RESUMED " : "", + localDS->isSlowSync() ? "SLOW" : "normal", + SyncModeDescriptions[localDS->getSyncMode()], + localDS->fServerAlerted ? ", Server-Alerted" : "" )); } + fRestartingSync = false; // thread might end here, so stop profiling TP_STOP(fTPInfo); } // TSyncAgent::ClientMessageEnded @@ -3758,6 +3871,27 @@ static TSyError readDisplayAlert( #endif // SYSYNC_CLIENT +static TSyError readRestartSync( + TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, + appPointer aBuffer, memSize aBufSize, memSize &aValSize +) +{ + TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); + return TStructFieldsKey::returnInt(mykeyP->fAgentP->fRestartSyncOnce, sizeof(bool), aBuffer, aBufSize, aValSize); +} // readRestartsync + + +// - write respURI enable flag +static TSyError writeRestartSync( + TStructFieldsKey *aStructFieldsKeyP, const TStructFieldInfo *aFldInfoP, + cAppPointer aBuffer, memSize aValSize +) +{ + TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); + mykeyP->fAgentP->fRestartSyncOnce = *((uInt8P)aBuffer); + return LOCERR_OK; +} // writeRestartSync + // - write error message into session log TSyError writeErrorMsg( @@ -3812,6 +3946,7 @@ static const TStructFieldInfo ServerParamFieldInfos[] = { "displayalert", VALTYPE_TEXT, false, 0, 0, &readDisplayAlert, NULL }, #endif #endif + { "restartsync", VALTYPE_INT8, true, 0, 0, &readRestartSync, &writeRestartSync } // write into debug log { "errorMsg", VALTYPE_TEXT, true, 0, 0, NULL, &writeErrorMsg }, { "debugMsg", VALTYPE_TEXT, true, 0, 0, NULL, &writeDebugMsg }, diff --git a/src/sysync/syncagent.h b/src/sysync/syncagent.h index 7186391..d341f46 100755 --- a/src/sysync/syncagent.h +++ b/src/sysync/syncagent.h @@ -424,6 +424,10 @@ public: public: // respuri bool fUseRespURI; + // flag controlled by app or datastore via "restartsync" session + // variable: if true, the agent will try to restart the sync and + // then clear the flag + bool fRestartSyncOnce; protected: // access to config TAgentConfig *getServerConfig(void); @@ -444,6 +448,9 @@ protected: virtual void getNextNonce(const char *aDeviceID, string &aNextNonce); // - get nonce string, which is expected to be used by remote party for MD5 auth. virtual void getAuthNonce(const char *aDeviceID, string &aAuthNonce); + // restarting a sync session is allowed in non-standard mode + // TODO: introduce DevInf extensions and use that instead + virtual bool allowAlertAfterMap() { return fCompleteFromClientOnly; } #ifdef ENGINE_LIBRARY // Engine interface // - process step @@ -493,6 +500,16 @@ protected: bool fAbortRequested; #endif // ENGINE_LIBRARY + // instead of sending at the end of the last message, + // the agent is going to ask the server for another sync session; + // set in NextMessage() for restarting in ClientMessageEnded() + bool fRestartingSync; + + // true if the SyncML client shall continue the session + // by sending another package with , instead of closing + // the session + bool restartSync(); + // true if all active stores are in "from client only" mode and // the engine is not in the fCompleteFromClientOnly mode // => map phase can be skipped diff --git a/src/sysync/syncsession.cpp b/src/sysync/syncsession.cpp index 9accd9d..4c30944 100644 --- a/src/sysync/syncsession.cpp +++ b/src/sysync/syncsession.cpp @@ -4893,6 +4893,15 @@ TSmlCommand *TSyncSession::processAlertItem( // Sync resume alert case 225: { // Synchronisation initialisation alerts + bool restarting = false; + if (allowAlertAfterMap() && fIncomingState==psta_map) { + // reset to state that allows a sync to start + PDEBUGPRINTFX(DBG_HOT,("process alert: restart sync")); + fIncomingState = psta_init; + fOutgoingState = psta_init; + restarting = true; + } + // - test if context is ok if (fIncomingState!=psta_init && fIncomingState!=psta_initsync) { // Sync alert only allowed in init package or combined init/sync @@ -4937,6 +4946,10 @@ TSmlCommand *TSyncSession::processAlertItem( // - let datastore process alert and generate additional alert if needed // NOTE: this might generate a PUT command if remote needs to see our // devInf (config changed since last sync) + if (restarting) { + // reset datastore first + datastoreP->engFinishDataStoreSync(LOCERR_OK); + } alertresponsecmdP=datastoreP->engProcessSyncAlert( NULL, // not as subdatastore aAlertCode, // the alert code diff --git a/src/sysync/syncsession.h b/src/sysync/syncsession.h index 080ea32..b795e51 100755 --- a/src/sysync/syncsession.h +++ b/src/sysync/syncsession.h @@ -837,6 +837,9 @@ protected: virtual localstatus checkRemoteSpecifics(SmlDevInfDevInfPtr_t aDevInfP, SmlDevInfDevInfPtr_t *aOverrideDevInfP); // - remote device is analyzed, possibly save status virtual void remoteAnalyzed(void) { /* nop */ }; + // - tell session whether it may accept an in the map + // phase and restart the sync + virtual bool allowAlertAfterMap() { return false; } // SyncML Toolkit interface InstanceID_t fSmlWorkspaceID; // SyncML toolkit workspace instance ID SmlEncoding_t fEncoding; // Current encoding type in SyncML toolkit instance -- cgit v1.2.3 From e17d205da835c7754ad64d9f948687f57e1f806a Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 7 Feb 2012 15:50:36 +0100 Subject: datastore: explicitly tell the engine whether restarting a sync is supported The non-standard "multiple sync cycles in the same session" mode depends on data stores which support multiple read/write cycles and properly report changes at the start of additional cycles. The engine will automatically run multiple cycles if it deems that necessary to get client and server in sync. This commit introduces the necessary flag that must be set for a store before the engine can be sure that it is safe to run more than one cycle. Not currently checked by the engine, though! --- src/sysync/localengineds.cpp | 2 ++ src/sysync/localengineds.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index e8d3565..94a5d2e 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -936,6 +936,8 @@ bool TLocalDSConfig::localStartElement(const char *aElementName, const char **aA expectEnum(sizeof(fFirstTimeStrategy),&fFirstTimeStrategy,conflictStrategyNames,numConflictStrategies); else if (strucmp(aElementName,"readonly")==0) expectBool(fReadOnly); + else if (strucmp(aElementName,"canrestart")==0) + expectBool(fCanRestart); else if (strucmp(aElementName,"reportupdates")==0) expectBool(fReportUpdates); else if (strucmp(aElementName,"deletewins")==0) diff --git a/src/sysync/localengineds.h b/src/sysync/localengineds.h index e1680a1..f8a4a8e 100755 --- a/src/sysync/localengineds.h +++ b/src/sysync/localengineds.h @@ -139,6 +139,7 @@ public: TConflictResolution fFirstTimeStrategy; // - options bool fReadOnly; // if set, datastore will not write any data (only maps) to local DB (but fake successful status to remote) + bool fCanRestart; // if set, then the datastore is able to participate in multiple sync sessions; in other words after a successful read/write cycle it is possible to restart at the reading phase bool fReportUpdates; // if set(normal case), updates of server items will be sent to client (can be set to false for example for emails) bool fResendFailing; // if set, items that receive a failure status from the remote will be resent in the next session (if DS 1.2 suspend marks supported by the DB) bool fDeleteWins; // if set, in a replace/delete conflict the delete wins (also see DELETEWINS()) @@ -577,6 +578,7 @@ public: bool isResuming(void) { return fResuming; }; ///< true if resuming a previous session bool isRefreshOnly(void) { return fRefreshOnly; }; ///< true if only refreshing with data from remote (no send to remote) bool isReadOnly(void) { return fReadOnly; }; ///< true if only reading from local datastore (and ignoring any updates from remote) + bool canRestart(void) { return fDSConfigP && fDSConfigP->fCanRestart; } /// get remote datastore related to this datastore TRemoteDataStore *getRemoteDatastore(void) { return fRemoteDatastoreP; }; /// return remote view of local URI (might be different from what we might think it is locally) -- cgit v1.2.3 From e518faf000581f393c0a75beb32c2db889c9b45e Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Sat, 25 Feb 2012 20:10:08 +0100 Subject: libsynthesis: fRestartSyncOnce must be present in client-only builds as well --- src/sysync/syncagent.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sysync/syncagent.h b/src/sysync/syncagent.h index d341f46..6a2b990 100755 --- a/src/sysync/syncagent.h +++ b/src/sysync/syncagent.h @@ -420,14 +420,14 @@ public: bool fServerHasSINCEBEFORE; #endif // SYSYNC_CLIENT - #ifdef SYSYNC_SERVER -public: - // respuri - bool fUseRespURI; // flag controlled by app or datastore via "restartsync" session // variable: if true, the agent will try to restart the sync and // then clear the flag bool fRestartSyncOnce; + #ifdef SYSYNC_SERVER +public: + // respuri + bool fUseRespURI; protected: // access to config TAgentConfig *getServerConfig(void); -- cgit v1.2.3 From 04cc23eeec7da58683e8f31f340ddc31d92c25f9 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Thu, 16 Feb 2012 18:32:53 +0100 Subject: XCode 4.3: applied recommended project settings updates (= now using LLDB, no longer GDB, and disable THUMB support), added scheme for client-only build of engine --- src/sysync_ios.xcodeproj/project.pbxproj | 8 ++- .../static_clientEngine_opensource_ios.xcscheme | 58 ++++++++++++++++++++++ .../static_combiEngine_opensource_ios.xcscheme | 6 ++- 3 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme diff --git a/src/sysync_ios.xcodeproj/project.pbxproj b/src/sysync_ios.xcodeproj/project.pbxproj index e73a3a7..5215b22 100644 --- a/src/sysync_ios.xcodeproj/project.pbxproj +++ b/src/sysync_ios.xcodeproj/project.pbxproj @@ -1770,7 +1770,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0420; + LastUpgradeCheck = 0430; ORGANIZATIONNAME = plan44.ch; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "sysync_ios" */; @@ -2111,6 +2111,7 @@ armv7, ); GCC_C_LANGUAGE_STANDARD = c99; + GCC_THUMB_SUPPORT = NO; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; GENERATE_MASTER_OBJECT_FILE = YES; @@ -2127,6 +2128,7 @@ armv6, armv7, ); + GCC_THUMB_SUPPORT = NO; GENERATE_MASTER_OBJECT_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 3.0; SDKROOT = iphoneos; @@ -2142,6 +2144,7 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; + GCC_THUMB_SUPPORT = NO; PRODUCT_NAME = sysync_combi_ios; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/Targets/ReleasedProducts/combiEngine_opensource_ios"; @@ -2157,6 +2160,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_C_LANGUAGE_STANDARD = c99; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_THUMB_SUPPORT = NO; PRODUCT_NAME = sysync_combi_ios; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/Targets/ReleasedProducts/combiEngine_opensource_ios"; @@ -2174,6 +2178,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREFIX_HEADER = prefix_file.h; GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; + GCC_THUMB_SUPPORT = NO; PRODUCT_NAME = sysync_client_ios; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/Targets/ReleasedProducts/clientEngine_opensource_ios"; @@ -2190,6 +2195,7 @@ GCC_C_LANGUAGE_STANDARD = c99; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_PREFIX_HEADER = prefix_file.h; + GCC_THUMB_SUPPORT = NO; PRODUCT_NAME = sysync_client_ios; SKIP_INSTALL = YES; USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/Targets/ReleasedProducts/clientEngine_opensource_ios"; diff --git a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme new file mode 100644 index 0000000..4e2e45c --- /dev/null +++ b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme index 01402be..04507cc 100644 --- a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme +++ b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme @@ -1,5 +1,6 @@ -- cgit v1.2.3 From 4067643855bfba818e2a15f8dd5e8a985cfbbb49 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Fri, 2 Mar 2012 17:04:05 +0100 Subject: SyncML TK: support (hexa-)decimal character entities in XML The Funambol Android client uses XML instead of WBXML and encodes some characters, like the at sign, with decimal character entities: @ This was not handled by the SyncML TK, which only supports some named entities and #43 as a special decimal entity, thus resulting in a 0x200b = SML_ERR_XLT_INVAL_SYNCML_DOC error. Fixed by fully supporting all decimal and hexadecimal character entities by converting the string value into integer and, with sanity checks, into a char. xmlHTMLEntity() now has to return a computed value and therefore its signature had to be changed away from returning a pointer to the result. Works because it never returned anything other than a char anyway. --- src/syncml_tk/src/sml/xlt/all/xltdecxml.c | 57 ++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/src/syncml_tk/src/sml/xlt/all/xltdecxml.c b/src/syncml_tk/src/sml/xlt/all/xltdecxml.c index 427bf25..6129c40 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltdecxml.c +++ b/src/syncml_tk/src/sml/xlt/all/xltdecxml.c @@ -614,11 +614,11 @@ xmlStringConst(xmlScannerPrivPtr_t pScanner, String_t *value) /** * parse HTML entity (like &) * - * pScanner->pos must pointer to &. Afterwards begin - * points to the character and len is set to 1. + * pScanner->pos must pointer to &. Afterwards entity is set + * to the character. */ static Ret_t -xmlHTMLEntity(xmlScannerPrivPtr_t pScanner, MemPtr_t *begin, int *len) +xmlHTMLEntity(xmlScannerPrivPtr_t pScanner, char *entityptr) { // start of an HTML entity: decode just that single character static const struct { @@ -634,6 +634,7 @@ xmlHTMLEntity(xmlScannerPrivPtr_t pScanner, MemPtr_t *begin, int *len) }; MemPtr_t entity = pScanner->pos + 1; int i; + int len; while (*pScanner->pos != ';') { if (pScanner->pos >= pScanner->bufend) { @@ -645,29 +646,46 @@ xmlHTMLEntity(xmlScannerPrivPtr_t pScanner, MemPtr_t *begin, int *len) } // remember length and skip ; - *len = pScanner->pos - entity; + len = pScanner->pos - entity; if (!readBytes(pScanner, 1)) { return SML_DECODEERROR(SML_ERR_XLT_END_OF_BUFFER,pScanner,"xmlHTMLEntity"); } // identify the entity - *begin = NULL; for (i = 0; i < sizeof(entities)/sizeof(entities[0]); i++) { - if (!strncmp(entities[i].entity, (char *)entity, *len) && - entities[i].entity[*len] == 0) { - *begin = (MemPtr_t)&entities[i].character; - break; + if (!strncmp(entities[i].entity, (char *)entity, len) && + entities[i].entity[len] == 0) { + *entityptr = entities[i].character; + return SML_ERR_OK; } } - if (*begin) { - // found it, continue below by copying it - *len = 1; + + // not one of the pre-defined entities + if (len >= 2 && *(char *)entity == '#') { + long value; + char *end; + // Either decimal or hexadecimal character reference. + if (((char *)entity)[1] == 'x') { + // hex + value = strtol((char *)entity + 2, &end, 16); + } else { + // dec + value = strtol((char *)entity + 1, &end, 10); + } + if (end != (char *)entity + len) { + // some character in the entity didn't decode in the expected base system + return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"xmlHTMLEntity"); + } + if (value < 0 || value > 255) { + // invalid value range + return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"xmlHTMLEntity"); + } + *entityptr = (unsigned char)value; + return SML_ERR_OK; } else { // invalid entity: abort!? return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"xmlHTMLEntity"); } - - return SML_ERR_OK; } @@ -682,7 +700,7 @@ xmlCharData(xmlScannerPrivPtr_t pScanner) SmlPcdataPtr_t pPCData; MemPtr_t begin; int len; - + char entity; pPCData = (SmlPcdataPtr_t)smlLibMalloc(sizeof(SmlPcdata_t)); if (pPCData == NULL) @@ -705,11 +723,13 @@ xmlCharData(xmlScannerPrivPtr_t pScanner) } if (*pScanner->pos == '&') { - Ret_t ret = xmlHTMLEntity(pScanner, &begin, &len); + Ret_t ret = xmlHTMLEntity(pScanner, &entity); if (ret) { smlLibFree(pPCData); return ret; } + begin = (MemPtr_t)&entity; + len = 1; } else { while (*pScanner->pos != '<' && (*pScanner->pos != '&')) { @@ -1098,6 +1118,7 @@ xmlSkipPCDATA(xmlScannerPrivPtr_t pScanner) Ret_t rc; String_t _tagString = NULL; String_t _tagString2 = NULL; + char entity; /* Check wether this PCData might contain a subdtd. ** We assume a Sub DTD starts with '<' as first char. @@ -1155,11 +1176,13 @@ xmlSkipPCDATA(xmlScannerPrivPtr_t pScanner) begin = pScanner->pos; if (*pScanner->pos == '&') { - Ret_t ret = xmlHTMLEntity(pScanner, &begin, &len); + Ret_t ret = xmlHTMLEntity(pScanner, &entity); if (ret) { smlLibFree(_tagString2); return ret; } + begin = (MemPtr_t)&entity; + len = 1; } else { // Read Pcdata content until end tag appears or we run into something which // requires special decoding: CDATA or HTML entity -- cgit v1.2.3 From ad4c57575874da6c5074bac3b719006baa1b0d4a Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Mon, 27 Feb 2012 16:47:33 +0100 Subject: logging + Linux: enable console output In some cases (outside of sync session, inside SyncML Toolkit), printing cannot fall back to logging settings because they don't exist yet (outside sync session) or are conceptually not available (toolkit). This patch enables these messages because they help to understand parsing errors inside the toolkit. They go to stderr and from there are redirected into log files and normal stdout by SyncEvolution. CONSOLEINFO_LIBC is needed because libsmltk.so should not depend on symbols in libsynthesis.so. Calling printf directly in the macros allows adding a common prefix and also revealed string format conversion mismatches (fixed separately). However, even correct messages trigger a lot of these messages. This needs to be checked before really merging the patch. --- .../combiEngine_opensource_linux/target_options.h | 5 +++++ src/syncapps/serverEngine_custom/combi_product_options.h | 2 +- src/syncml_tk/src/sml/xlt/all/xltdec.c | 8 ++++---- src/sysync/localengineds.cpp | 16 ++++++++-------- src/sysync/stdlogicds.cpp | 6 +++--- src/sysync/syncagent.cpp | 2 +- src/sysync/syncsession.cpp | 2 +- src/sysync/sysync_debug.h | 13 +++++++++++++ 8 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/Targets/ReleasedProducts/combiEngine_opensource_linux/target_options.h b/src/Targets/ReleasedProducts/combiEngine_opensource_linux/target_options.h index d6fcd66..c6f8b09 100644 --- a/src/Targets/ReleasedProducts/combiEngine_opensource_linux/target_options.h +++ b/src/Targets/ReleasedProducts/combiEngine_opensource_linux/target_options.h @@ -23,6 +23,11 @@ #define RELEASE_SYDEBUG 2 // extended DBG included //#define OPTIONAL_SYDEBUG 1 +// enable printing to console via libc by default, +// might be turned off again in product_options.h +#define CONSOLEINFO +#define CONSOLEINFO_LIBC + // Eval limit options // ================== diff --git a/src/syncapps/serverEngine_custom/combi_product_options.h b/src/syncapps/serverEngine_custom/combi_product_options.h index 2813aa7..4fb889b 100644 --- a/src/syncapps/serverEngine_custom/combi_product_options.h +++ b/src/syncapps/serverEngine_custom/combi_product_options.h @@ -67,8 +67,8 @@ #define SYDEBUG RELEASE_SYDEBUG #else #undef SYDEBUG // absolutely no debug code for release! + #undef CONSOLEINFO #endif - #undef CONSOLEINFO #else #define SYDEBUG 2 // 3=including XPT trace, 4=including memory leak detection #undef CONSOLEINFO diff --git a/src/syncml_tk/src/sml/xlt/all/xltdec.c b/src/syncml_tk/src/sml/xlt/all/xltdec.c index abf0dca..a07f24c 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltdec.c +++ b/src/syncml_tk/src/sml/xlt/all/xltdec.c @@ -252,8 +252,8 @@ Ret_t show_decode_error(Ret_t aRc, XltDecScannerPtr_t aScanner, char *aRoutineNa b = aScanner->getPos(aScanner); SMLERRPRINTFX(DBG_ERROR,( "- Tag start at 0x%lX, scanner pos at 0x%lX%s, data:", - aScanner->curtok->start, - b, + (unsigned long)aScanner->curtok->start, + (unsigned long)b, aScanner->finished ? " (finished)" : "" )); if (b!=NULL) { @@ -265,14 +265,14 @@ Ret_t show_decode_error(Ret_t aRc, XltDecScannerPtr_t aScanner, char *aRoutineNa } *p=0; b-=numBytesShow; // rewind - SMLERRPRINTFX(DBG_ERROR,(hexshow)); + SMLERRPRINTFX(DBG_ERROR,("%s", hexshow)); p=hexshow; for (i=0; i=0x20) && (*b<0x7F) ? *b : '_'; b++; } *p=0; - SMLERRPRINTFX(DBG_ERROR,(hexshow)); + SMLERRPRINTFX(DBG_ERROR,("%s", hexshow)); } } else { diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index 94a5d2e..e39ab8d 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -4548,16 +4548,16 @@ void TLocalEngineDS::showStatistics(void) else { CONSOLEPRINTF((" on Client on Server")); } - CONSOLEPRINTF((" Added: %9ld %9ld",fLocalItemsAdded,fRemoteItemsAdded)); - CONSOLEPRINTF((" Deleted: %9ld %9ld",fLocalItemsDeleted,fRemoteItemsDeleted)); - CONSOLEPRINTF((" Updated: %9ld %9ld",fLocalItemsUpdated,fRemoteItemsUpdated)); - CONSOLEPRINTF((" Rejected with error: %9ld %9ld",fLocalItemsError,fRemoteItemsError)); + CONSOLEPRINTF((" Added: %9ld %9ld",(long)fLocalItemsAdded,(long)fRemoteItemsAdded)); + CONSOLEPRINTF((" Deleted: %9ld %9ld",(long)fLocalItemsDeleted,(long)fRemoteItemsDeleted)); + CONSOLEPRINTF((" Updated: %9ld %9ld",(long)fLocalItemsUpdated,(long)fRemoteItemsUpdated)); + CONSOLEPRINTF((" Rejected with error: %9ld %9ld",(long)fLocalItemsError,(long)fRemoteItemsError)); #ifdef SYSYNC_SERVER if (IS_SERVER) { - CONSOLEPRINTF((" SlowSync Matches: %9ld",fSlowSyncMatches)); - CONSOLEPRINTF((" Server won Conflicts: %9ld",fConflictsServerWins)); - CONSOLEPRINTF((" Client won Conflicts: %9ld",fConflictsClientWins)); - CONSOLEPRINTF((" Conflicts with Duplication: %9ld",fConflictsDuplicated)); + CONSOLEPRINTF((" SlowSync Matches: %9ld",(long)fSlowSyncMatches)); + CONSOLEPRINTF((" Server won Conflicts: %9ld",(long)fConflictsServerWins)); + CONSOLEPRINTF((" Client won Conflicts: %9ld",(long)fConflictsClientWins)); + CONSOLEPRINTF((" Conflicts with Duplication: %9ld",(long)fConflictsDuplicated)); } #endif } diff --git a/src/sysync/stdlogicds.cpp b/src/sysync/stdlogicds.cpp index 2a41ad1..1020e54 100644 --- a/src/sysync/stdlogicds.cpp +++ b/src/sysync/stdlogicds.cpp @@ -402,7 +402,7 @@ localstatus TStdLogicDS::performStartSync(void) sta=implEndDataRead(); // show items PDEBUGPRINTFX(DBG_HOT,("%s: number of local items involved in %ssync = %ld",getName(), fSlowSync ? "slow " : "",(long)fItems.size())); - CONSOLEPRINTF((" %ld local items are new/changed/deleted for this sync",fItems.size())); + CONSOLEPRINTF((" %ld local items are new/changed/deleted for this sync",(long)fItems.size())); if (PDEBUGTEST(DBG_DATA+DBG_DETAILS)) { PDEBUGBLOCKFMTCOLL(("SyncSet","Items involved in Sync","datastore=%s",getName())); for (TSyncItemPContainer::iterator pos=fItems.begin();pos!=fItems.end();pos++) { @@ -587,8 +587,8 @@ localstatus TStdLogicDS::startDataAccessForServer(void) // this will not work, warn (but no longer abort session, as Siemens S55 guys don't like that) CONSOLEPRINTF(( "Warning: Synchronisation involves more items (%ld) than client can possibly manage (%ld", - (sInt32)fItems.size(), - (sInt32)getRemoteDatastore()->getMaxID() + (long)fItems.size(), + (long)getRemoteDatastore()->getMaxID() )); PDEBUGPRINTFX(DBG_ERROR,( "Warning: Synchronisation involves more items (%ld) than client can possibly manage (%ld)", diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index b7b38c8..e9252ff 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -2416,7 +2416,7 @@ bool TSyncAgent::EndRequest(bool &aHasData, string &aRespURI, uInt32 aReqBytes) (long)t, (long)fRequestMinTime-t )); - CONSOLEPRINTF((" ...delaying response by %ld seconds because requestmintime is set to %ld",fRequestMinTime,fRequestMinTime-t)); + CONSOLEPRINTF((" ...delaying response by %ld seconds because requestmintime is set to %ld",(long)fRequestMinTime,(long)(fRequestMinTime-t))); sleepLineartime((lineartime_t)(fRequestMinTime-t)*secondToLinearTimeFactor); } } diff --git a/src/sysync/syncsession.cpp b/src/sysync/syncsession.cpp index 4c30944..fa3b1ac 100644 --- a/src/sysync/syncsession.cpp +++ b/src/sysync/syncsession.cpp @@ -2634,7 +2634,7 @@ Ret_t TSyncSession::processHeader(TSyncHeader *aSyncHdrP) PDEBUGPRINTFX(DBG_HOT,("- Response to be sent to URI='%s'",fRespondURI.empty() ? "[none specified, back to source]" : fRespondURI.c_str())); PDEBUGPRINTFX(DBG_HOT,("- Target (Local party) : URI='%s' DisplayName='%s'",fLocalURI.c_str(),fLocalName.c_str())); #endif - CONSOLEPRINTF(("> SyncML message #%ld received from '%s'",fIncomingMsgID,fRemoteURI.c_str())); + CONSOLEPRINTF(("> SyncML message #%ld received from '%s'",(long)fIncomingMsgID,fRemoteURI.c_str())); // - UTC support is implied for SyncML 1.0 (as most devices support it, and // there is was no way to signal it in 1.0). if (!fRemoteDevInfKnown && fSyncMLVersion==syncml_vers_1_0) fRemoteCanHandleUTC=true; diff --git a/src/sysync/sysync_debug.h b/src/sysync/sysync_debug.h index 749f07c..61d4cdd 100755 --- a/src/sysync/sysync_debug.h +++ b/src/sysync/sysync_debug.h @@ -16,6 +16,9 @@ #include "generic_types.h" +#if defined(CONSOLEINFO) && defined(CONSOLEINFO_LIBC) +# include +#endif #ifdef __cplusplus using namespace std; @@ -101,8 +104,18 @@ TDebugLogger *getDbgLogger(void); // output to console macro #ifdef CONSOLEINFO +# ifdef CONSOLEINFO_LIBC + // Short-circuit all of the intermediate layers and use libc directly; + // useful to avoid dependencies in libsmltk on libsynthesis. + // Because a lot of libs log to stderr, include a unique prefix. + // Assumes that all printf format strings are plain strings. + #define CONSOLEPUTS(m) CONSOLE_PRINTF_VARARGS("%s", (m)) +#define CONSOLE_PRINTF_VARARGS(_m, _args...) fprintf(stderr, "SYSYNC " _m, ##_args) + #define CONSOLEPRINTF(m) CONSOLE_PRINTF_VARARGS m +# else // CONSOLEINFO_LIBC #define CONSOLEPUTS(m) ConsolePuts(m) #define CONSOLEPRINTF(m) ConsolePrintf m +# endif // CONSOLEINFO_LIBC #else #define CONSOLEPUTS(m) #define CONSOLEPRINTF(m) -- cgit v1.2.3 From 1e2de0787507bc3fb40f5e293c375e19cd052a16 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 23 Feb 2012 17:58:53 +0100 Subject: SyncML extensions: multiple cycles + DevInf The PUT of DevInf in the first message was repeated in each cycle because it wasn't checked whether the DevInf had already been sent (is never true there in a normal SyncML session). When restarting syncing, resending the DevInf is not useful. It is uncertain how changes to the DevInf should be handled, so they are simply not supported and sending the DevInf multiple times is prevented by checking fRemoteGotDevinf. --- src/sysync/syncagent.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index e9252ff..123e357 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -1195,10 +1195,11 @@ localstatus TSyncAgent::NextMessage(bool &aDone) // - mustSendDevInf() returns true signalling an external condition that suggests sending devInf (like changed config) // - any datastore is doing first time sync // - fPutDevInfAtSlowSync is true and any datastore is doing slow sync - if ( - mustSendDevInf() || - anyfirstsyncs || - (anyslowsyncs && static_cast(getRootConfig()->fAgentConfigP)->fPutDevInfAtSlowSync) + // - not already sent (can be true here in later sync cycles) + if (!fRemoteGotDevinf && + (mustSendDevInf() || + anyfirstsyncs || + (anyslowsyncs && static_cast(getRootConfig()->fAgentConfigP)->fPutDevInfAtSlowSync)) ) { TDevInfPutCommand *putcmdP = new TDevInfPutCommand(this); issueRootPtr(putcmdP); -- cgit v1.2.3 From 4d66ad2277f00b8711e2b9cdcba469f41d4f2075 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Fri, 24 Feb 2012 10:56:32 +0100 Subject: restart sync: extend DevInf and check whether datastores support restarts DevInf/Datastore/SyncCap is defined as a list of PCDATA strings. Only the values for the standard DS sync modes are defined, other values are explicitly allowed. We use that extension mechanism to report additional flags for datastores. The first such flag is "does the datastore support restarts", by adding and checking for X-SYNTHESIS-RESTART in the SyncCap. Internally the flag is now stored in SyncDataStore, where it is set when receiving a suitable DevInf (remote datastore) or it was set in the config (local datastore). The logic which restarts a sync was adapted accordingly. Instead of assuming that restarting is possible with the peer if was set for it, actually check the remote datastore's "can restart" flag. The expectation is that this flag is not set unless both datastore and SyncML engine support it, so there's no need for a separate global flag. --- src/sysync/localengineds.cpp | 9 +++++++++ src/sysync/localengineds.h | 1 - src/sysync/remotedatastore.cpp | 19 +++++++++++++++++++ src/sysync/syncagent.cpp | 16 +++++++++++----- src/sysync/syncdatastore.cpp | 1 + src/sysync/syncdatastore.h | 2 ++ 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index e39ab8d..f58c7ca 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -1160,6 +1160,7 @@ void TLocalEngineDS::InternalResetDataStore(void) fRefreshOnly=false; fReadOnly=false; fReportUpdates=fDSConfigP->fReportUpdates; // update reporting according to what is configured + fCanRestart=fDSConfigP->fCanRestart; fServerAlerted=false; fResuming=false; #ifdef SUPERDATASTORES @@ -3767,6 +3768,14 @@ SmlDevInfSyncCapPtr_t TLocalEngineDS::newDevInfSyncCap(uInt32 aSyncCapMask) addPCDataToList(synctypeP,&(synccapP->synctype)); } } + // Now add non-standard synccaps. + // From the spec: "Other values can also be specified." + // Values are PCDATA, so we can use plain strings. + // Corresponding code in TRemoteDataStore::setDatastoreDevInf(). + if (canRestart()) { + synctypeP=newPCDataString("X-SYNTHESIS-RESTART"); + addPCDataToList(synctypeP,&(synccapP->synctype)); + } // return it return synccapP; } // TLocalEngineDS::newDevInfSyncCap diff --git a/src/sysync/localengineds.h b/src/sysync/localengineds.h index f8a4a8e..dd0826b 100755 --- a/src/sysync/localengineds.h +++ b/src/sysync/localengineds.h @@ -578,7 +578,6 @@ public: bool isResuming(void) { return fResuming; }; ///< true if resuming a previous session bool isRefreshOnly(void) { return fRefreshOnly; }; ///< true if only refreshing with data from remote (no send to remote) bool isReadOnly(void) { return fReadOnly; }; ///< true if only reading from local datastore (and ignoring any updates from remote) - bool canRestart(void) { return fDSConfigP && fDSConfigP->fCanRestart; } /// get remote datastore related to this datastore TRemoteDataStore *getRemoteDatastore(void) { return fRemoteDatastoreP; }; /// return remote view of local URI (might be different from what we might think it is locally) diff --git a/src/sysync/remotedatastore.cpp b/src/sysync/remotedatastore.cpp index 6283e78..e69e528 100755 --- a/src/sysync/remotedatastore.cpp +++ b/src/sysync/remotedatastore.cpp @@ -250,6 +250,25 @@ bool TRemoteDataStore::setDatastoreDevInf( } PDEBUGPRINTFX(DBG_REMOTEINFO,("DevInf provides DSMem: MaxMem=" PRINTF_LLD ", MaxID=" PRINTF_LLD,PRINTF_LLD_ARG(fFreeMemory),PRINTF_LLD_ARG(fMaxID))); } + // - SyncCap - standard types currently ignored, only + // out own extensions are relevant. + // Corresponding code in TLocalEngineDS::newDevInfSyncCap() + if (aDataStoreDevInfP->synccap) { + SmlPcdataListPtr_t stlP = aDataStoreDevInfP->synccap->synctype; + // loop through list + PDEBUGBLOCKDESC("RemoteSyncTypes", "Analyzing remote types listed in datastore level SyncCap"); + while (stlP) { + if (stlP->data) { + const char *type = smlPCDataToCharP(stlP->data); + PDEBUGPRINTFX(DBG_REMOTEINFO,("SyncType='%s'", type)); + if (!strcmp(type, "X-SYNTHESIS-RESTART")) { + fCanRestart = true; + } + } + stlP = stlP->next; + } + PDEBUGENDBLOCK("RemoteSyncTypes"); + } } SYSYNC_CATCH (...) DEBUGPRINTFX(DBG_ERROR,("******** setDatastoreDevInf caused exception")); diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index 123e357..b336265 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -969,11 +969,10 @@ bool TSyncAgent::restartSync() { if (IS_CLIENT) { // Restarting needs to be done if: - // - server supports it (currently assumed to be possible in non-standard - // fCompleteFromClientOnly mode - TODO: introduce DevInf extension for this) // - we only have one datastore (intentional simplification for now) // - that datastore supports restarting a sync (= multiple read/write cycles) - // one client and server side (TODO: DevInf extension for server side) + // on client and server side (expected not be set if the engine itself on + // either side doesn't support it, so that is not checked separately) // - datastore hasn't failed in current iteration // - client has pending changes: // - server temporarily rejected a change, queued for resending @@ -981,8 +980,7 @@ bool TSyncAgent::restartSync() // item locally (might have an updated queued, need to send delete) // - change on client failed temporarily // - the app or a datastore asked for a restart via the "restartsync" - if (fCompleteFromClientOnly && - !getenv("LIBSYNTHESIS_NO_RESTART")) { + if (!getenv("LIBSYNTHESIS_NO_RESTART")) { bool restartPossible=true; // ... unless proven otherwise below bool restartNecessary=fRestartSyncOnce; // one reason for restarting: requested by app int numActive = 0; @@ -1010,6 +1008,14 @@ bool TSyncAgent::restartSync() restartPossible=false; break; } + if (!localDS->getRemoteDatastore() || + !localDS->getRemoteDatastore()->canRestart()) { + PDEBUGPRINTFX(DBG_SESSION,("cannot restart, remote datastore %s matching with %s does not support it", + localDS->getRemoteDatastore()->getName(), + localDS->getName())); + restartPossible=false; + break; + } } // check for pending local changes in the client if (localDS->numUnsentMaps() > 0) { diff --git a/src/sysync/syncdatastore.cpp b/src/sysync/syncdatastore.cpp index 1f9dbc2..99cde8a 100755 --- a/src/sysync/syncdatastore.cpp +++ b/src/sysync/syncdatastore.cpp @@ -60,6 +60,7 @@ void TSyncDataStore::init(TSyncSession *aSessionP) fMaxID = numeric_limits<__typeof__(fMaxID)>::max(); fFreeID = fMaxID; #endif + fCanRestart = false; // rest ist like reset InternalResetDataStore(); } // TSyncDataStore::init diff --git a/src/sysync/syncdatastore.h b/src/sysync/syncdatastore.h index 0672341..d3336ad 100755 --- a/src/sysync/syncdatastore.h +++ b/src/sysync/syncdatastore.h @@ -77,6 +77,7 @@ public: sInt64 getMaxMemory(void) { return fMaxMemory; }; sInt64 getFreeID(void) { return fFreeID; }; sInt64 getMaxID(void) { return fMaxID; }; + bool canRestart() { return fCanRestart; } // - session TSyncSession *getSession(void) { return fSessionP; }; // description structure of datastore (NULL if not available) @@ -116,6 +117,7 @@ protected: sInt64 fFreeMemory; // number of free bytes sInt64 fMaxID; // maximum number of ID sInt64 fFreeID; // free IDs + bool fCanRestart; // if set, then the datastore is able to participate in multiple sync sessions; in other words after a successful read/write cycle it is possible to restart at the reading phase public: // Type of items in this datastore (read-only, can be used by multiple Datastores simultaneously) // - receiving types (also used as default item type) -- cgit v1.2.3 From 32a65970edd0a63b17f3d54575a4e7b4e4b4bf17 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Wed, 29 Feb 2012 17:40:45 +0100 Subject: TLocalDSConfig: fCanRestart might have been undefined It was undefined if not mentioned explicitly in the config. Now it is set to false when clearing the instance. --- src/sysync/localengineds.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index f58c7ca..31a30b0 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -880,6 +880,7 @@ void TLocalDSConfig::clear(void) // options fLocalDBTypeID=0; fReadOnly=false; + fCanRestart=false; fReportUpdates=true; fDeleteWins=false; // replace wins over delete by default fResendFailing=true; // resend failing items in next session by default -- cgit v1.2.3 From ccdf7b85415c76d3a2b2c0a2925252b56427df9f Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Wed, 29 Feb 2012 17:45:21 +0100 Subject: restarting sync: support multiple datastores Removed the limitation to one active datastore. Testing showed that there was indeed a problem: when processing the Alert which triggers the restart, the "we are restarting" flag was local and thus only applied to the first datastore for which such an Alert was received. All further Alerts then are processed when the session state was already reset and thus the other datastores were not reset, causing undesired slow syncs (anchor not written). Fixed by remembering the "we are restarting" flag as part of the session. For good measure the flag is reset again when the restarted sync enters the "sync" phase. --- src/sysync/syncagent.cpp | 18 +++++++++--------- src/sysync/syncsession.cpp | 12 ++++++------ src/sysync/syncsession.h | 3 +++ 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index b336265..3109813 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -969,17 +969,17 @@ bool TSyncAgent::restartSync() { if (IS_CLIENT) { // Restarting needs to be done if: - // - we only have one datastore (intentional simplification for now) - // - that datastore supports restarting a sync (= multiple read/write cycles) + // - all datastores support restarting a sync (= multiple read/write cycles) // on client and server side (expected not be set if the engine itself on // either side doesn't support it, so that is not checked separately) - // - datastore hasn't failed in current iteration + // - no datastore has failed in current iteration // - client has pending changes: // - server temporarily rejected a change, queued for resending // - an item added by the server was merged with another // item locally (might have an updated queued, need to send delete) // - change on client failed temporarily // - the app or a datastore asked for a restart via the "restartsync" + // session variable if (!getenv("LIBSYNTHESIS_NO_RESTART")) { bool restartPossible=true; // ... unless proven otherwise below bool restartNecessary=fRestartSyncOnce; // one reason for restarting: requested by app @@ -1034,11 +1034,6 @@ bool TSyncAgent::restartSync() } } - if (numActive > 1) { - PDEBUGPRINTFX(DBG_SESSION,("cannot restart, %d data stores active", numActive)); - restartPossible=false; - } - return restartPossible && restartNecessary; } } @@ -1249,7 +1244,10 @@ localstatus TSyncAgent::NextMessage(bool &aDone) } } // append sync phase if we have combined init/sync - if (fOutgoingState==psta_initsync) fOutgoingState=psta_sync; + if (fOutgoingState==psta_initsync) { + fOutgoingState=psta_sync; + fRestarting=false; + } } // process sync/syncop/map generating phases after init if (!isSuspending()) { @@ -1599,6 +1597,7 @@ void TSyncAgent::ClientMessageEnded(bool aIncomingFinal) // so client enters sync state now (but holds back sync until server // has finished init) fOutgoingState=psta_sync; + fRestarting=false; } else if (fIncomingState==psta_sync || fIncomingState==psta_initsync) { // server has started (or already finished) sending statuses for our @@ -2237,6 +2236,7 @@ void TSyncAgent::ServerMessageEnded(bool aIncomingFinal) // will set outgoing state from init to init-sync while processing message, // so no transition needs to be detected here newoutgoingstate=psta_sync; + fRestarting=false; } // - sync to map else if ( diff --git a/src/sysync/syncsession.cpp b/src/sysync/syncsession.cpp index fa3b1ac..074abce 100644 --- a/src/sysync/syncsession.cpp +++ b/src/sysync/syncsession.cpp @@ -1565,6 +1565,7 @@ void TSyncSession::InternalResetSessionEx(bool terminationCall) fIncomingState=psta_idle; // no incoming package status yet fCmdIncomingState=psta_idle; fOutgoingState=psta_idle; // no outgoing package status yet + fRestarting=false; fNextMessageRequests=0; // no pending next message requests fFakeFinalFlag=false; // special flag to work around broken resume implementations fNewOutgoingPackage=true; // first message will be first in outgoing package @@ -4893,13 +4894,12 @@ TSmlCommand *TSyncSession::processAlertItem( // Sync resume alert case 225: { // Synchronisation initialisation alerts - bool restarting = false; if (allowAlertAfterMap() && fIncomingState==psta_map) { // reset to state that allows a sync to start PDEBUGPRINTFX(DBG_HOT,("process alert: restart sync")); fIncomingState = psta_init; fOutgoingState = psta_init; - restarting = true; + fRestarting = true; } // - test if context is ok @@ -4942,14 +4942,14 @@ TSmlCommand *TSyncSession::processAlertItem( const char *sourceURI = smlSrcTargLocURIToCharP(aItemP->source); // get Filter SmlFilterPtr_t targetFilter = aItemP->target ? aItemP->target->filter : NULL; + if (fRestarting) { + // reset datastore first + datastoreP->engFinishDataStoreSync(LOCERR_OK); + } // alert datastore of requested sync // - let datastore process alert and generate additional alert if needed // NOTE: this might generate a PUT command if remote needs to see our // devInf (config changed since last sync) - if (restarting) { - // reset datastore first - datastoreP->engFinishDataStoreSync(LOCERR_OK); - } alertresponsecmdP=datastoreP->engProcessSyncAlert( NULL, // not as subdatastore aAlertCode, // the alert code diff --git a/src/sysync/syncsession.h b/src/sysync/syncsession.h index b795e51..6fa780b 100755 --- a/src/sysync/syncsession.h +++ b/src/sysync/syncsession.h @@ -898,6 +898,9 @@ protected: TPackageStates fIncomingState; // incoming package state TPackageStates fCmdIncomingState; // while executing commands: state when command was received (actual might be different due to queueing) TPackageStates fOutgoingState; // outgoing package state + bool fRestarting; // Set to true in TSyncSession::processAlertItem() while processing the first Alert from a + // client which requests another sync cycle. Applies to all further Alerts, cleared + // when entering fOutgoingState==psta_sync again. bool fFakeFinalFlag; // special flag to work around broken resume implementations bool fNewOutgoingPackage; // set if first outgoing message in outgoing package bool fNeedToAnswer; // set if an answer to currently processed message is needed (will be set by issuing of first non-synchdr-status) -- cgit v1.2.3 From 85f7fd380169dccd29e2e8775b460962d02924e2 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Fri, 2 Mar 2012 12:50:04 +0000 Subject: source link: always allow config options An app using libsynthesis cannot determine whether the source link feature was enabled when compiling libsynthesis, but it has to add the config entries for it to make it work if it was enabled, at least on Linux ("doxygen" must be chosen there). This triggers error messages to the console if console printing is enabled and the source link feature is off. To avoid that, let's allow the config options and check them for correctness regardless whether the feature is enabled. It might also be good to use a sensible default and thus remove the need to set the options at all, but that is a different issue. --- src/sysync/debuglogger.h | 2 -- src/sysync/syncappbase.cpp | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sysync/debuglogger.h b/src/sysync/debuglogger.h index f0b107a..98fa7c0 100755 --- a/src/sysync/debuglogger.h +++ b/src/sysync/debuglogger.h @@ -97,10 +97,8 @@ public: bool fThreadIDForAll; ///< include thread ID information for every message TDbgFlushModes fFlushMode; ///< how and when to flush TDbgFoldingModes fFoldingMode; ///< if and how to fold HTML output - #ifdef SYDEBUG_LOCATION TDbgSourceModes fSourceLinkMode; ///< if and how to link with source code string fSourceRootPath; ///< defines root path for source links - #endif bool fAppend; ///< if set, existing debug files will not be overwritten, but appended to TDbgSubthreadModes fSubThreadMode; ///< how to handle debug messages from subthreads uInt32 fSubThreadBufferMax; ///< how much to buffer for subthread maximally diff --git a/src/sysync/syncappbase.cpp b/src/sysync/syncappbase.cpp index 962a5aa..890da37 100755 --- a/src/sysync/syncappbase.cpp +++ b/src/sysync/syncappbase.cpp @@ -920,12 +920,13 @@ bool TDebugConfig::localStartElement(const char *aElementName, const char **aAtt expectEnum(sizeof(fSessionDbgLoggerOptions.fOutputFormat),&fSessionDbgLoggerOptions.fOutputFormat,DbgOutFormatNames,numDbgOutFormats); else if (strucmp(aElementName,"folding")==0) expectEnum(sizeof(fSessionDbgLoggerOptions.fFoldingMode),&fSessionDbgLoggerOptions.fFoldingMode,DbgFoldingModeNames,numDbgFoldingModes); - #ifdef SYDEBUG_LOCATION + // source link settings always available, even if feature is not: + // allows adding the setting to configs unconditionally, without + // triggering config parser errors else if (strucmp(aElementName,"sourcelink")==0) expectEnum(sizeof(fSessionDbgLoggerOptions.fSourceLinkMode),&fSessionDbgLoggerOptions.fSourceLinkMode,DbgSourceModeNames,numDbgSourceModes); else if (strucmp(aElementName,"sourcebase")==0) expectPath(fSessionDbgLoggerOptions.fSourceRootPath); - #endif else if (strucmp(aElementName,"indentstring")==0) expectCString(fSessionDbgLoggerOptions.fIndentString); else if (strucmp(aElementName,"fileprefix")==0) -- cgit v1.2.3 From 38913414f25aa46194fecdd600fb283b94201c78 Mon Sep 17 00:00:00 2001 From: Beat Forster Date: Sun, 4 Mar 2012 16:53:07 +0100 Subject: vCalendar 1.0 DAYLIGHT recognition improved, now works with floating DST start/end time specs as well - Fill_tChange() works now for floating as well (no offset adding) - FoundTZ() / TimeZoneNameToContext() with optional param (Android platform had already Olson support, that's why the Fill_tChange() problem was not there - vtimezone with Olson name search as last step, if all other things do not help --- src/platform_adapters/macosx/platform_timezones.mm | 5 +- src/sysync/timezones.cpp | 108 ++++++++++++++------- src/sysync/timezones.h | 7 +- src/sysync/vtimezone.cpp | 27 +++++- 4 files changed, 102 insertions(+), 45 deletions(-) diff --git a/src/platform_adapters/macosx/platform_timezones.mm b/src/platform_adapters/macosx/platform_timezones.mm index 346d1d2..926d135 100644 --- a/src/platform_adapters/macosx/platform_timezones.mm +++ b/src/platform_adapters/macosx/platform_timezones.mm @@ -106,11 +106,12 @@ bool getSystemTimeZoneContext(timecontext_t &aContext, GZones* aGZones) t.bias = 0; t.biasDST = 0; t.dst.wMonth= 0; + sInt16 y; lineartime_t stdTime, dstTime; if (getSystemTimeZone( t.name, t.bias,t.biasDST, stdTime,dstTime )) { - Get_tChange( stdTime, t.std ); - Get_tChange( dstTime, t.dst ); + Get_tChange( stdTime, t.std, y ); + Get_tChange( dstTime, t.dst, y ); } // search entry, first by rule return ContextForEntry(aContext, t, false, aGZones); diff --git a/src/sysync/timezones.cpp b/src/sysync/timezones.cpp index fdb169c..3f8796b 100755 --- a/src/sysync/timezones.cpp +++ b/src/sysync/timezones.cpp @@ -46,7 +46,7 @@ namespace sysync { -static bool tzcmp( const tz_entry &t, const tz_entry &tzi ); +static bool tzcmp ( const tz_entry &t, const tz_entry &tzi, bool olsonSupport ); static bool YearFit( const tz_entry &t, const tz_entry &tzi, GZones* g ); // ---- global structure ----------------------------------------------------------- @@ -80,9 +80,9 @@ const class tzdata : public std::vector )); // allow olson names for Android - #ifdef ANDROID + //#ifdef ANDROID -- if (t.olsonName!=NULL) back().location= t.olsonName; - #endif + //#endif -- } //push_back(tz_entry("unknown", 0, 0, "x", "", tChange( 0, 0,0, 0,0), tChange( 0, 0,0, 0,0))); // 0 #endif @@ -167,7 +167,11 @@ bool GZones::matchTZ(const tz_entry &aTZ, TDebugLogger *aLogP, timecontext_t &aC { if (aTZ.ident=="x") return false; // comparison with this type is not possible - bool rule_match = tzcmp(fTZ, aTZ) && YearFit(fTZ, aTZ, fG); + bool olsonSupport= false; + #ifdef ANDROID + olsonSupport= true; + #endif + bool rule_match = tzcmp( fTZ, aTZ, olsonSupport ) && YearFit(fTZ, aTZ, fG); // start of a group is an entry that has a dynYear empty or set to "CUR" (for dynamically created entries) if (aTZ.dynYear.empty() || aTZ.dynYear == "CUR") @@ -430,9 +434,9 @@ bool GetTZ( timecontext_t aContext, tz_entry &t, GZones* g, int year ) -void Get_tChange( lineartime_t tim, tChange &v, bool asDate ) +void Get_tChange( lineartime_t tim, tChange &v, sInt16 &y, bool asDate ) { - sInt16 y, day, d, sec, ms; + sInt16 day, d, sec, ms; lineartime2date( tim, &y, &v.wMonth, &day ); lineartime2time( tim, &v.wHour, &v.wMinute, &sec, &ms ); @@ -455,7 +459,7 @@ void Get_tChange( lineartime_t tim, tChange &v, bool asDate ) -static bool Fill_tChange( string iso8601, int bias, int biasDST, tChange &tc, bool isDST ) +static bool Fill_tChange( string iso8601, int bias, int biasDST, tChange &tc, sInt16 &y, bool isDST ) { lineartime_t l; timecontext_t c; @@ -464,11 +468,13 @@ static bool Fill_tChange( string iso8601, int bias, int biasDST, tChange &tc, bo string::size_type rslt= ISO8601StrToTimestamp( iso8601.c_str(), l, c ); if (rslt!=iso8601.length()) return false; - int bMins = bias; - if (!isDST) bMins+= biasDST; - l+= seconds2lineartime( bMins*SecsPerMin ); + if (iso8601[ rslt-1 ]=='Z') { // to it for UTC only + int bMins = bias; + if (!isDST) bMins+= biasDST; + l+= seconds2lineartime( bMins*SecsPerMin ); + } // if - Get_tChange( l, tc ); + Get_tChange( l, tc, y ); /* lineartime2date( l, &y, &tc.wMonth, &day ); @@ -487,6 +493,20 @@ static bool Fill_tChange( string iso8601, int bias, int biasDST, tChange &tc, bo } // Fill_tChange + +// Get int value as string +static string IntStr( sInt32 i ) +{ + const int FLen= 15; /* max length of (internal) item name */ + char f[ FLen ]; + // cheating: this printf format assumes that sInt32 == int + sprintf ( f, "%d", int(i) ); + string s= f; + return s; +} // IntStr + + + bool GetTZ( string std, string dst, int bias, int biasDST, tz_entry &t, GZones* g ) { t.name = ""; @@ -495,8 +515,20 @@ bool GetTZ( string std, string dst, int bias, int biasDST, tz_entry &t, GZones* t.ident = ""; t.dynYear= ""; - return Fill_tChange( std, bias,biasDST, t.std, false ) && - Fill_tChange( dst, bias,biasDST, t.dst, true ); + sInt16 y; + bool ok= Fill_tChange( std, bias,biasDST, t.std, y, false ) && + Fill_tChange( dst, bias,biasDST, t.dst, y, true ); + if (ok) t.dynYear= IntStr( y ); + +//printf( "tS m=%d dw=%d n=%d H=%d M=%d\n", t.std.wMonth, t.std.wDayOfWeek, t.std.wNth, +// t.std.wHour, t.std.wMinute ); +//printf( "tD m=%d dw=%d n=%d H=%d M=%d\n", t.dst.wMonth, t.dst.wDayOfWeek, t.dst.wNth, +// t.dst.wHour, t.dst.wMinute ); +//printf( "t bs=%d bd=%d\n", t.bias, t.biasDST ); +// +//printf( "ok=%d y=%d dyn='%s'\n", ok, y, t.dynYear.c_str() ); + + return ok; } // GetTZ @@ -511,20 +543,25 @@ static bool Same_tChange( const tChange &tCh1, const tChange &tCh2 ) /*! Compare time zone information */ -static bool tzcmp( const tz_entry &t, const tz_entry &tzi ) +static bool tzcmp( const tz_entry &t, const tz_entry &tzi, bool olsonSupport ) { bool sameName= !tzi.name.empty() && strucmp( tzi.name.c_str(), t.name.c_str() )==0; bool sameLoc = false; // by default, no olson support here - #ifdef ANDROID +//#ifdef ANDROID -- + if (olsonSupport) { // allow olson names as well here sameLoc= !tzi.location.empty() && strucmp( tzi.location.c_str(), t.name.c_str() )==0; //__android_log_print( ANDROID_LOG_DEBUG, "TZ CMP", "'%s' == '%s' / '%s'\n", // t.name.c_str(), tzi.name.c_str(), tzi.location.c_str() ); - #endif + } +//#endif -- + +//printf( "name='%s' loc='%s' ident='%s' sameName=%d sameLoc=%d\n", +// t.name.c_str(), t.ident.c_str(), tzi.location.c_str(), sameName, sameLoc ); if (!t.name.empty() && !sameName && !sameLoc) return false; @@ -639,7 +676,8 @@ static bool YearFit( const tz_entry &t, const tz_entry &tzi, GZones* g ) bool FoundTZ( const tz_entry &tc, string &aName, timecontext_t &aContext, GZones* g, bool createIt, - timecontext_t searchOffset ) + timecontext_t searchOffset, + bool olsonSupport ) { aName = ""; aContext = TCTX_UNKNOWN; @@ -655,13 +693,16 @@ bool FoundTZ( const tz_entry &tc, int i; // search hard coded elements first for (i= offs+1; i<(int)tctx_numtimezones; i++) { const tz_entry &tzi = tz[ i ]; - if (tzcmp ( t, tzi ) && + if (tzcmp ( t, tzi, olsonSupport ) && YearFit( t, tzi, g )) { aName= tzi.name; + //printf( "name='%s' i=%d\n", aName.c_str(), i ); ok = true; break; } // if } // for +//printf( "ok=%d name='%s' i=%d olson=%d\n", ok, aName.c_str(), i, olsonSupport ); + // don't go thru the mutex, if not really needed if (!ok && g!=NULL) { // ------------------------------------------- @@ -679,7 +720,7 @@ bool FoundTZ( const tz_entry &tc, pos!=g->tzP.end(); pos++) { if (j<0 && !(pos->ident=="-") && // element must not be removed - tzcmp( t, *pos )) { + tzcmp( t, *pos, olsonSupport )) { aName= pos->name; ok = true; break; } // if @@ -697,7 +738,7 @@ bool FoundTZ( const tz_entry &tc, pos!=g->tzP.end(); pos++) { if (j<0 && pos->ident == "-" && // removed element ? - tzcmp( t, *pos )) { + tzcmp( t, *pos, olsonSupport )) { pos->ident= t.ident; // reactivate the identifier aName = pos->name; // should be the same ok = true; break; @@ -742,11 +783,16 @@ bool RemoveTZ( const tz_entry &t, GZones* g ) lockMutex( g->muP ); #endif + bool olsonSupport= false; + #ifdef ANDROID + olsonSupport= true; + #endif + TZList::iterator pos; for (pos= g->tzP.begin(); pos!=g->tzP.end(); pos++) { if (!(pos->ident=="-") && // element must not be removed - tzcmp( t, *pos )) { + tzcmp( t, *pos, olsonSupport )) { pos->ident = "-"; //gz()->tzP.erase( pos ); // do not remove it, keep it persistent ok= true; break; @@ -763,7 +809,7 @@ bool RemoveTZ( const tz_entry &t, GZones* g ) -bool TimeZoneNameToContext( cAppCharP aName, timecontext_t &aContext, GZones* g ) +bool TimeZoneNameToContext( cAppCharP aName, timecontext_t &aContext, GZones* g, bool olsonSupport ) { // check some special cases if (strucmp(aName,"DATE")==0) { @@ -791,7 +837,7 @@ bool TimeZoneNameToContext( cAppCharP aName, timecontext_t &aContext, GZones* g t.dynYear= ""; // luz: must be initialized! string tName; - if (FoundTZ( t, tName, aContext, g )) return true; + if (FoundTZ( t, tName, aContext, g, olsonSupport )) return true; /* int i; aContext= TCTX_UNKNOWN; @@ -844,10 +890,10 @@ bool TimeZoneContextToName( timecontext_t aContext, string &aName, GZones* g, aName= "UNKNOWN"; // setting it via param does not work currently for some reasons, switch it on permanently for Android - #ifdef ANDROID +//#ifdef ANDROID -- aPrefIdent= "o"; //__android_log_print( ANDROID_LOG_DEBUG, "ContextToName", "pref='%s' / aContext=%d\n", aPrefIdent, aContext ); - #endif +//#endif -- // if aPrefIndent contains "o", this means we'd like to see olson name, if possible // %%% for now, we can return olson for the built-ins only @@ -1078,18 +1124,6 @@ timecontext_t SelectTZ( TDaylightSavingZone zone, int bias, int biasDST, lineart -// Get int value as string -static string IntStr( sInt32 i ) -{ - const int FLen= 15; /* max length of (internal) item name */ - char f[ FLen ]; - // cheating: this printf format assumes that sInt32 == int - sprintf ( f, "%d", int(i) ); - string s= f; - return s; -} // IntStr - - /* get system's time zone */ static bool MyContext( timecontext_t &aContext, GZones* g ) { diff --git a/src/sysync/timezones.h b/src/sysync/timezones.h index 52b21d5..8550083 100755 --- a/src/sysync/timezones.h +++ b/src/sysync/timezones.h @@ -315,7 +315,7 @@ timecontext_t SelectTZ( TDaylightSavingZone zone, int bias, int biasDST, lineart // visible for platform_timezones.cpp/.mm bool ContextForEntry( timecontext_t &aContext, tz_entry &t, bool chkNameFirst, GZones* g ); -void Get_tChange( lineartime_t tim, tChange &v, bool asDate= false ); +void Get_tChange( lineartime_t tim, tChange &v, sInt16 &y, bool asDate= false ); @@ -349,7 +349,8 @@ bool FoundTZ( const tz_entry &t, timecontext_t &aContext, GZones* g, bool createIt = false, - timecontext_t searchOffset= tctx_tz_unknown ); + timecontext_t searchOffset= tctx_tz_unknown, + bool olsonSupport= false ); /*! Remove an existing entry @@ -373,7 +374,7 @@ lineartime_t DST_Switch( const tz_entry &t, int bias, sInt16 aYear, bool toDST ) * @param[in] g : global list of additional time zones * */ -bool TimeZoneNameToContext( cAppCharP aName, timecontext_t &aContext, GZones* g ); +bool TimeZoneNameToContext( cAppCharP aName, timecontext_t &aContext, GZones* g, bool olsonSupport= false ); /*! Convert context into time zone name, a preferred name can be given * @param[in] aContext : time context to resolve diff --git a/src/sysync/vtimezone.cpp b/src/sysync/vtimezone.cpp index 8d8207d..8e695df 100644 --- a/src/sysync/vtimezone.cpp +++ b/src/sysync/vtimezone.cpp @@ -761,6 +761,8 @@ bool TzDaylightToContext( const char* aText, ///< DAYLIGHT property value timecontext_t cc, ccFirst, ccSlash; aContext= aStdOffs; // as default, convert it into a enum TZ + string sSv; + bool dbg= false; // currently no debugging do { if (s=="FALSE" ) { s= ""; break; } // no DST @@ -801,12 +803,15 @@ bool TzDaylightToContext( const char* aText, ///< DAYLIGHT property value StringSubst( s, ";", "/" ); TimeZoneNameToContext( s.c_str(), aContext, g ); + sSv= s; // make a copy for Olson name test later // if it perfectly fits to a named zone, take it if (GetTZ( std,dst, mins,minsDST, t, g )) { ccFirst= TCTX_UNKNOWN; // start with these defaults ccSlash= TCTX_UNKNOWN; cc = TCTX_SYSTEM; + if (dbg) printf( "lv1 s='%s' %d pref=%d\n", s.c_str(), aContext, aPreferredCtx ); while (FoundTZ( t, rslt, cc, g, false, cc )) { + if (dbg) printf( "lv1 rslt='%s'\n", rslt.c_str() ); if (s==rslt) { aContext= cc; break; } if (s.empty()) { if (ccFirst==TCTX_UNKNOWN) ccFirst= cc; @@ -828,9 +833,12 @@ bool TzDaylightToContext( const char* aText, ///< DAYLIGHT property value ccFirst= TCTX_UNKNOWN; // start with these defaults cc = TCTX_SYSTEM; + tCopy= t; // make the copy before if (GetTZ( aContext, t, g )) { + if (dbg) printf( "lv2 s='%s' %d\n", s.c_str(), aContext ); if (FoundTZ( t, s, cc, g, false, cc )) { // search by correct name first + if (dbg) printf( "lv2 s='%s' / loc='%s'\n", s.c_str(), t.location.c_str() ); if (!(cc!=aPreferredCtx && cc==t_Greenwich)) { // take UTC for this case if (pUnk || cc==aPreferredCtx) { aContext= cc; return true; } if (ccFirst==TCTX_UNKNOWN) ccFirst= cc; // keep it, just in case @@ -842,20 +850,33 @@ bool TzDaylightToContext( const char* aText, ///< DAYLIGHT property value t.biasDST= 0; // no DST offset t.dynYear= ""; ClrDST( t ); + tCopy= t; } // if - tCopy= t; + //tCopy= t; tCopy.name = ""; // make more generic comparison tCopy.ident= ""; cc= TCTX_SYSTEM; while (FoundTZ( tCopy, s, cc, g, false, cc )) { + if (dbg) printf( "lv3 s='%s' loc='%s' cc=%d\n", s.c_str(), tCopy.location.c_str(), cc ); if (!(cc!=aPreferredCtx && cc==t_Greenwich)) { // take UTC for this case - if (pUnk || cc==aPreferredCtx) { aContext= cc; return true; } + if (pUnk || cc==aPreferredCtx) { ccFirst= cc; break; } if (ccFirst==TCTX_UNKNOWN) ccFirst= cc; // keep it, just in case } // if } // while aContext= ccFirst; - return true; + bool ok= !TCTX_IS_UNKNOWN( aContext ); + if (dbg) printf( "aContext=%d ok=%d\n", aContext, ok ); + + // if not ok, try to use Olson names + if (!ok) { s= sSv; + TimeZoneNameToContext( s.c_str(), aContext, g, true ); + if (dbg) printf( "lv4 s='%s' aContext=%d\n", s.c_str(), aContext ); + ok= !TCTX_IS_UNKNOWN( aContext ); + } + + if (dbg) printf( "aContext=%d ok=%d\n", aContext, ok ); + return ok; } // TzDaylightToContext -- cgit v1.2.3 From 61066c74ee1533d654bbf162352349cb63ba41a1 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 10 Aug 2012 22:51:03 +0200 Subject: timezones: previous commit (DAYLIGHT recognition...) broke persisting TZ names in DBs, as TimeZoneContextToName() ignored the aPrefIdent argument and also its default value by hard-coding it to "o". This made databases store time zones as Olson names, rather than internal time zone names. However, the reverse function, TimeZoneNameToContext(), understands Olson names only when passed a new parameter olsonSupport as true. But as the default value is false, existing uses of TimeZoneNameToContext() could no longer convert names created by TimeZoneContextToName() back to a valid time zone, which essentially meant loosing time zone information in sync round trips. This patch reverts the override to aPrefIdent="o" in TimeZoneContextToName(), and additionally explicitly sets the new olsonSupport parameter where TimeZoneNameToContext() is used. --- src/DB_interfaces/api_db/pluginapids.cpp | 2 +- src/DB_interfaces/odbc_db/odbcapiagent.cpp | 4 ++-- src/sysync/configelement.cpp | 4 ++-- src/sysync/customimplds.cpp | 4 ++-- src/sysync/itemfield.cpp | 8 ++++---- src/sysync/mimedirprofile.cpp | 6 +++--- src/sysync/scriptcontext.cpp | 4 ++-- src/sysync/timezones.cpp | 6 ++++-- src/sysync_SDK/Sources/lineartime.cpp | 6 +++--- 9 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/DB_interfaces/api_db/pluginapids.cpp b/src/DB_interfaces/api_db/pluginapids.cpp index 2300dc9..0d3101b 100755 --- a/src/DB_interfaces/api_db/pluginapids.cpp +++ b/src/DB_interfaces/api_db/pluginapids.cpp @@ -401,7 +401,7 @@ bool TPluginApiDS::storeField( if (paramScan(aParams,"TZNAME",s)) { // convert to time zone context timecontext_t tctx; - TimeZoneNameToContext(s.c_str(), tctx, tsfP->getGZones()); + TimeZoneNameToContext(s.c_str(), tctx, tsfP->getGZones(), true); tsfP->moveToContext(tctx, true); // move to new context, bind floating (and float fixed, if TZNAME=FLOATING) } } diff --git a/src/DB_interfaces/odbc_db/odbcapiagent.cpp b/src/DB_interfaces/odbc_db/odbcapiagent.cpp index d665058..1df9fd4 100644 --- a/src/DB_interfaces/odbc_db/odbcapiagent.cpp +++ b/src/DB_interfaces/odbc_db/odbcapiagent.cpp @@ -2554,7 +2554,7 @@ bool TODBCApiAgent::getColumnValueAsField( notnull=getColumnValueAsString(aStatement,aColIndex,val,aDataCharSet, lem_cstr); if (!notnull) goto assignzone; // assign TCTX_UNKNOWN // convert to context - TimeZoneNameToContext(val.c_str(), tctx, getSessionZones()); + TimeZoneNameToContext(val.c_str(), tctx, getSessionZones(), true); goto assignzone; assignoffs: tctx = TCTX_MINOFFSET(moffs); @@ -3139,7 +3139,7 @@ bool TODBCApiAgent::getSQLiteColValueAsField( // get zone name as string appendStringAsUTF8((const char *)sqlite3_column_text(aStatement,aColIndex), val, aDataCharSet, lem_cstr); // convert to context - TimeZoneNameToContext(val.c_str(), tctx, getSessionZones()); + TimeZoneNameToContext(val.c_str(), tctx, getSessionZones(), true); goto assignzone; assignoffs: tctx = TCTX_MINOFFSET(moffs); diff --git a/src/sysync/configelement.cpp b/src/sysync/configelement.cpp index 76aa29c..214efd0 100755 --- a/src/sysync/configelement.cpp +++ b/src/sysync/configelement.cpp @@ -804,8 +804,8 @@ bool TConfigElement::endElement(const char *aElementName, bool aIsDelegated) TzConvertTimestamp(*(fResultPtr.fTimestampP),tctx,TCTX_UTC,getSyncAppBase()->getAppZones()); break; case pamo_timezone: - // time zone by name - if (!TimeZoneNameToContext(fTempString.c_str(), *(fResultPtr.fTimeContextP), getSyncAppBase()->getAppZones())) + // time zone by name (internal or olson) + if (!TimeZoneNameToContext(fTempString.c_str(), *(fResultPtr.fTimeContextP), getSyncAppBase()->getAppZones(), true)) ReportError(false,"invalid/unknown timezone name"); break; case pamo_vtimezone: diff --git a/src/sysync/customimplds.cpp b/src/sysync/customimplds.cpp index 094d2bb..a41365e 100755 --- a/src/sysync/customimplds.cpp +++ b/src/sysync/customimplds.cpp @@ -3679,8 +3679,8 @@ bool TCustomImplDS::storeField( timecontext_t tctx = TCTX_UNKNOWN; // modify time zone if params contain a TZNAME if (paramScan(aParams,"TZNAME",s)) { - // convert to time zone context - TimeZoneNameToContext(s.c_str(), tctx, tsfP->getGZones()); + // convert to time zone context (olson allowed) + TimeZoneNameToContext(s.c_str(), tctx, tsfP->getGZones(), true); } // now parse text string into field tsfP->setAsISO8601(aValue, tctx, false); diff --git a/src/sysync/itemfield.cpp b/src/sysync/itemfield.cpp index ccb39a6..dd2cbb2 100644 --- a/src/sysync/itemfield.cpp +++ b/src/sysync/itemfield.cpp @@ -1550,8 +1550,8 @@ bool TTimestampField::setAsRFC822date(cAppCharP aRFC822String, timecontext_t aDe } } else if (isalpha(*p)) { - // could be time zone name (if not, ignore zone spec) - aIgnoreZone = !TimeZoneNameToContext(p,fTimecontext,fGZonesP); + // could be time zone name, internal or olson name (if not, ignore zone spec) + aIgnoreZone = !TimeZoneNameToContext(p,fTimecontext,fGZonesP,true); } } // if no valid zone, use default @@ -2136,8 +2136,8 @@ TSyError TItemFieldKey::SetValueInternal( sval.assign((cAppCharP)aBuffer,aValSize); tctx = TCTX_UNKNOWN; if (!sval.empty()) { - // convert - if (!TimeZoneNameToContext(sval.c_str(), tctx, tsFldP->getGZones())) + // convert (internal or olson names allowed) + if (!TimeZoneNameToContext(sval.c_str(), tctx, tsFldP->getGZones(), true)) return LOCERR_BADPARAM; // bad timezone name } // set context diff --git a/src/sysync/mimedirprofile.cpp b/src/sysync/mimedirprofile.cpp index cb123e7..5b0ecc7 100644 --- a/src/sysync/mimedirprofile.cpp +++ b/src/sysync/mimedirprofile.cpp @@ -1533,9 +1533,9 @@ bool TMimeDirProfileHandler::fieldToMIMEString( tctx = TCTX_MINOFFSET(fldP->getAsInteger()); } else if (!fldP->isEmpty()) { - // string field can be timezone name or numeric minute offset + // string field can be timezone name (internal or olson) or numeric minute offset fldP->getAsString(s); - if (!TimeZoneNameToContext(s.c_str(),tctx,getSessionZones())) { + if (!TimeZoneNameToContext(s.c_str(),tctx,getSessionZones(), true)) { // if not recognized as time zone name, use integer value tctx = TCTX_MINOFFSET(fldP->getAsInteger()); } @@ -3486,7 +3486,7 @@ bool TMimeDirProfileHandler::MIMEStringToField( fPropTZIDtctx = tctx; goto timecontext; } - else if (TimeZoneNameToContext(aText, tctx, getSessionZones())) { + else if (TimeZoneNameToContext(aText, tctx, getSessionZones(), true)) { // found valid TZID property, save it so we can use it for all values of this property that don't specify their own TZ PDEBUGPRINTFX(DBG_ERROR,("Warning: TZID %s could be resolved against internal name, but appropriate VTIMEZONE is missing",aText)); fPropTZIDtctx=tctx; diff --git a/src/sysync/scriptcontext.cpp b/src/sysync/scriptcontext.cpp index b9ec512..f82c365 100755 --- a/src/sysync/scriptcontext.cpp +++ b/src/sysync/scriptcontext.cpp @@ -341,8 +341,8 @@ public: tctx = TCTX_UNKNOWN; } else { - // search for timezone by name - if (!TimeZoneNameToContext(str.c_str(), tctx, aFuncContextP->getSession()->getSessionZones())) { + // search for timezone by name (internal or olson) + if (!TimeZoneNameToContext(str.c_str(), tctx, aFuncContextP->getSession()->getSessionZones(), true)) { // last attempt is parsing it as a ISO8601 offset spec ISO8601StrToContext(str.c_str(), tctx); } diff --git a/src/sysync/timezones.cpp b/src/sysync/timezones.cpp index 3f8796b..a2a2ecf 100755 --- a/src/sysync/timezones.cpp +++ b/src/sysync/timezones.cpp @@ -889,11 +889,13 @@ bool TimeZoneContextToName( timecontext_t aContext, string &aName, GZones* g, tz_entry t; aName= "UNKNOWN"; + #ifdef ANDROID // setting it via param does not work currently for some reasons, switch it on permanently for Android -//#ifdef ANDROID -- + // BUT NOT GENERALLY - IT CHANGES SEMANTICS OF THE WHOLE TIME ZONE NAMING SYSTEM! + #warning "%%% overriding passed aPrefIdent argument with constant, forcing olson name output" aPrefIdent= "o"; //__android_log_print( ANDROID_LOG_DEBUG, "ContextToName", "pref='%s' / aContext=%d\n", aPrefIdent, aContext ); -//#endif -- + #endif // if aPrefIndent contains "o", this means we'd like to see olson name, if possible // %%% for now, we can return olson for the built-ins only diff --git a/src/sysync_SDK/Sources/lineartime.cpp b/src/sysync_SDK/Sources/lineartime.cpp index 6c8b6f5..f402b24 100755 --- a/src/sysync_SDK/Sources/lineartime.cpp +++ b/src/sysync_SDK/Sources/lineartime.cpp @@ -61,8 +61,8 @@ int timeConv(int argc, const char *argv[]) intime = getSystemNowAs(incontext,&zones); } else { - // get input context from name - if (!TimeZoneNameToContext(inzone.c_str(),incontext,&zones)) + // get input context from name (internal or olson) + if (!TimeZoneNameToContext(inzone.c_str(),incontext,&zones, true)) incontext=TCTX_UNKNOWN; // input time from 3rd argument if (argc!=3) { @@ -95,7 +95,7 @@ int timeConv(int argc, const char *argv[]) s.erase(); ContextToISO8601StrAppend(s, stdoffs, true); CONSOLEPRINTF(("Input time zone represented as TZ/DAYLIGHT :\n\nTZ:%s\nDAYLIGHT:%s\n",s.c_str(),z.c_str())); } - else if (!TimeZoneNameToContext(outzone.c_str(),outcontext,&zones)) + else if (!TimeZoneNameToContext(outzone.c_str(),outcontext,&zones, true)) outcontext=TCTX_UNKNOWN; // now show CONSOLEPRINTF(("")); -- cgit v1.2.3 From 411525256b22eed606a8c96a999fe5568507ecec Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 6 Mar 2012 13:58:35 +0000 Subject: scripting: file URI decoding Utility method for extracting the file path from an URI. Needed for inlining PHOTO data in scripts before sending to a peer. Uses the existing splitURL() method and enhances it, because the previous parser was a bit too simplistic. For example, in file:///tmp/file it skipped all the leading slashes and interpreted "tmp" as host name, instead of returning an empty host name. Query and port number are split from the host and doc part directly by splitURL() now because that seems more consistent. The in-tree code using syncURL() concatenates the separate parts to keep its own semantic unchanged. URL %xx decoding must be handled by the caller of syncURL(), using the new urlDecode() utility code. This is not done in syncURL() to simplify concatenating parts (would have to url encode reserved characters otherwise). The new code always returns an empty doc part. There's no longer a special case for inserting a slash when only the query part is set, because the caller is already expected to treat the doc name as starting in the root of the document hierarchy. --- src/sysync/itemfield.cpp | 2 +- src/sysync/scriptcontext.cpp | 21 ++++ src/sysync/syncagent.cpp | 20 +++- src/sysync/syncclientbase.cpp | 18 ++- src/sysync_SDK/Sources/sysync_utils.cpp | 199 ++++++++++++++++++++++++++++---- src/sysync_SDK/Sources/sysync_utils.h | 9 +- 6 files changed, 235 insertions(+), 34 deletions(-) diff --git a/src/sysync/itemfield.cpp b/src/sysync/itemfield.cpp index dd2cbb2..ada6a71 100644 --- a/src/sysync/itemfield.cpp +++ b/src/sysync/itemfield.cpp @@ -1031,7 +1031,7 @@ void TURLField::stringWasAssigned(void) string proto; if (!fString.empty()) { // make sure we have a URL with protocol - splitURL(fString.c_str() ,&proto, NULL, NULL, NULL, NULL); + splitURL(fString.c_str() ,&proto, NULL, NULL, NULL, NULL, NULL, NULL); if (proto.empty()) { // no protocol set, but string not empty --> assume http fString.insert(0, "http://"); diff --git a/src/sysync/scriptcontext.cpp b/src/sysync/scriptcontext.cpp index f82c365..87cc4d5 100755 --- a/src/sysync/scriptcontext.cpp +++ b/src/sysync/scriptcontext.cpp @@ -920,6 +920,26 @@ public: } } // func_Read + // string URIToPath(string uri) + // extracts the file path in a file:// uri; handles uri decoding + // Returns UNASSIGNED if not a file:// uri + static void func_URIToPath(TItemField *&aTermP, TScriptContext *aFuncContextP) + { + // get params + string uri; + aFuncContextP->getLocalVar(0)->getAsString(uri); + + string protocol, doc; + splitURL(uri.c_str(), &protocol, NULL, &doc, NULL, NULL, NULL, NULL); + if (protocol == "file") { + string path; + path.reserve(doc.size() + 1); + path += "/"; // leading slash is never included by splitURL() + path += doc; + urlDecode(&path); + aTermP->setAsString(path); + } + } // func_URIToPath // string REMOTERULENAME() // returns name of the LAST matched remote rule (or subrule), empty if none @@ -2308,6 +2328,7 @@ const TBuiltInFuncDef BuiltInFuncDefs[] = { { "REQUESTMINTIME", TBuiltinStdFuncs::func_RequestMinTime, fty_none, 1, param_oneInteger }, { "SHELLEXECUTE", TBuiltinStdFuncs::func_Shellexecute, fty_integer, 3, param_Shellexecute }, { "READ", TBuiltinStdFuncs::func_Read, fty_string, 1, param_oneString }, + { "URITOPATH", TBuiltinStdFuncs::func_URIToPath, fty_string, 1, param_oneString }, { "SESSIONVAR", TBuiltinStdFuncs::func_SessionVar, fty_none, 1, param_oneString }, { "SETSESSIONVAR", TBuiltinStdFuncs::func_SetSessionVar, fty_none, 2, param_SetSessionVar }, { "ABORTSESSION", TBuiltinStdFuncs::func_AbortSession, fty_none, 1, param_oneInteger }, diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index 3109813..c84b7a6 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -3756,8 +3756,14 @@ static TSyError readConnectHost( ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); - string host; - splitURL(mykeyP->fAgentP->getSendURI(),NULL,&host,NULL,NULL,NULL); + string host, port; + splitURL(mykeyP->fAgentP->getSendURI(),NULL,&host,NULL,NULL,NULL,&port,NULL); + // old semantic of splitURL was to include port in host string, + // continue doing that + if (!port.empty()) { + host += ':'; + host += port; + } return TStructFieldsKey::returnString( host.c_str(), aBuffer,aBufSize,aValSize @@ -3772,8 +3778,14 @@ static TSyError readConnectDoc( ) { TAgentParamsKey *mykeyP = static_cast(aStructFieldsKeyP); - string doc; - splitURL(mykeyP->fAgentP->getSendURI(),NULL,NULL,&doc,NULL,NULL); + string doc, query; + splitURL(mykeyP->fAgentP->getSendURI(),NULL,NULL,&doc,NULL,NULL,NULL,&query); + // old semantic of splitURL was to include query in document string, + // continue doing that + if (!query.empty()) { + doc += '?'; + doc += query; + } return TStructFieldsKey::returnString( doc.c_str(), aBuffer,aBufSize,aValSize diff --git a/src/sysync/syncclientbase.cpp b/src/sysync/syncclientbase.cpp index 50e98de..bb4b882 100644 --- a/src/sysync/syncclientbase.cpp +++ b/src/sysync/syncclientbase.cpp @@ -353,21 +353,33 @@ localstatus TSyncClientBase::processAnswer(void) // - extract hostname from an URI according to transport void TSyncClientBase::extractHostname(const char *aURI, string &aHostName) { - splitURL(aURI,NULL,&aHostName,NULL,NULL,NULL); + string port; + splitURL(aURI,NULL,&aHostName,NULL,NULL,NULL,&port,NULL); + // keep old semantic: port included in aHostName + if (!port.empty()) { + aHostName += ':'; + aHostName += port; + } } // TSyncClientBase::extractHostname // - extract document name from an URI according to transport void TSyncClientBase::extractDocumentInfo(const char *aURI, string &aDocName) { - splitURL(aURI,NULL,NULL,&aDocName,NULL,NULL); + string query; + splitURL(aURI,NULL,NULL,&aDocName,NULL,NULL,NULL,&query); + // keep old semantic: query part of aDocName + if (!query.empty()) { + aDocName += '?'; + aDocName += query; + } } // TSyncClientBase::extractDocumentInfo // - extract protocol name from an URI according to transport void TSyncClientBase::extractProtocolname(const char *aURI, string &aProtocolName) { - splitURL(aURI,&aProtocolName,NULL,NULL,NULL,NULL); + splitURL(aURI,&aProtocolName,NULL,NULL,NULL,NULL,NULL,NULL); } // TSyncClientBase::extractProtocolname diff --git a/src/sysync_SDK/Sources/sysync_utils.cpp b/src/sysync_SDK/Sources/sysync_utils.cpp index e56dd32..980d453 100755 --- a/src/sysync_SDK/Sources/sysync_utils.cpp +++ b/src/sysync_SDK/Sources/sysync_utils.cpp @@ -2241,9 +2241,55 @@ void splitHostname(const char *aHost,string *aAddr,string *aPort) } } // splitHostname +// translate %XX into corresponding character in-place +void urlDecode(string *str) +{ + // nothing todo? + if (!str || + str->find('%') == string::npos) return; + + string replacement; + replacement.reserve(str->size()); + const char *in = str->c_str(); + char c; + while ((c = *in++) != 0) { + if (c == '%') { + c = tolower(*in++); + unsigned char value = 0; + if (!c) { + break; + } else if (c >= '0' && c <= '9') { + value = c - '0'; + } else if (c >= 'a' && c <= 'f') { + value = c - 'a' + 10; + } else { + // silently skip invalid character + } + value *= 16; + c = tolower(*in++); + if (!c) { + break; + } else if (c >= '0' && c <= '9') { + value += c - '0'; + replacement.append((char *)&value, 1); + } else if (c >= 'a' && c <= 'f') { + value += c - 'a' + 10; + replacement.append((char *)&value, 1); + } else { + // silently skip invalid character + } + } else { + replacement.append(&c, 1); + } + } + *str = replacement; +} -// split URL into protocol, hostname, document name and auth-info (user, password) -void splitURL(const char *aURI,string *aProtocol,string *aHost,string *aDoc,string *aUser, string *aPasswd) +// split URL into protocol, hostname, document name and auth-info (user, password); +// the optional query and port are not url-decoded, everything else is +void splitURL(const char *aURI,string *aProtocol,string *aHost, + string *aDoc, string *aUser, string *aPasswd, + string *aPort, string *aQuery) { const char *p,*q,*r; @@ -2254,21 +2300,37 @@ void splitURL(const char *aURI,string *aProtocol,string *aHost,string *aDoc,stri // protocol found if (aProtocol) aProtocol->assign(p,q-p); p=q+1; // past colon - while (*p=='/') p++; // past trailing slashes + int count = 0; + while (*p=='/' && count < 2) { + p++; // past trailing slashes (two expected, ignore if less are given) + count++; + } + // now identify end of host part + string host; + q=strchr(p, '/'); + if (!q) { + // no slash, skip forward to end of string + q = p + strlen(p); + } + host.assign(p, q - p); + // if protocol specified, check for auth info - q=strchr(p,'@'); - r=strchr(p,':'); + const char *h = host.c_str(); + q=strchr(h,'@'); + r=strchr(h,':'); if (q && r && q>r) { // auth exists - if (aUser) aUser->assign(p,r-p); + if (aUser) aUser->assign(h,r-h); if (aPasswd) aPasswd->assign(r+1,q-r-1); - p=q+1; // past "@" + // skip auth in full string + p += q + 1 - h; } else { // no auth found if (aUser) aUser->erase(); if (aPasswd) aPasswd->erase(); } + // p now points to host part, as expected below } else { // no protocol found @@ -2278,35 +2340,124 @@ void splitURL(const char *aURI,string *aProtocol,string *aHost,string *aDoc,stri if (aPasswd) aPasswd->erase(); } // separate hostname and document - // - assume path + std::string host; + // - check for path q=strchr(p,'/'); // - if no path, check if there is a CGI param directly after the host name if (!q) { + // doc part left empty in this case + if (aDoc) aDoc->erase(); q=strchr(p,'?'); - // in case of no docpath, but CGI, put '?' into docname - r=q; + if (q) { + // query directly follows host + host.assign(p, q - p); + if (aQuery) aQuery->assign(q + 1); + } else { + // entire string is considered the host + host.assign(p); + if (aQuery) aQuery->erase(); + } } else { + // host part stops at slash + host.assign(p, q - p); // in case of '/', do not put slash into docname - // except if docname would be empty otherwise - r=q+1; // exclude slash - if (*r==0) r=q; // nothing follows, include the slash - } - if (q) { - // document exists - if (aDoc) { - aDoc->erase(); - if (*q=='?') (*aDoc)+='/'; // if doc starts with CGI, we are at root - aDoc->append(r); // till end of string + // even if it would be empty (caller expected to add + // slash as needed) + p = q + 1; // exclude slash + // now check for query + q=strchr(p,'?'); + if (q) { + // split at question mark + if (aDoc) aDoc->assign(p, q - p); + if (aQuery) aQuery->assign(q + 1); + } else { + // whole string is document name + if (aDoc) aDoc->assign(p); + if (aQuery) aQuery->erase(); } - if (aHost) aHost->assign(p,q-p); // assign host (all up to / or ?) } - else { - if (aDoc) aDoc->erase(); // empty document name - if (aHost) aHost->assign(p); // entire string is host + + // remove optional port from host part before url-decoding, because + // that might introduce new : characters into the host name + size_t colon = host.find(':'); + if (colon != host.npos) { + if (aHost) aHost->assign(host.substr(0, colon)); + if (aPort) aPort->assign(host.substr(colon + 1)); + } else { + if (aHost) aHost->assign(host); + if (aPort) aPort->erase(); } } // splitURL +#ifdef SPLIT_URL_MAIN + +#include +#include + +static void test(const std::string &in, const std::string &expected) +{ + string protocol, host, doc, user, password, port, query; + char buffer[1024]; + + splitURL(in.c_str(), &protocol, &host, &doc, &user, &password, &port, &query); + + // URL-decode each part + urlDecode(&protocol); + urlDecode(&host); + urlDecode(&doc); + urlDecode(&user); + urlDecode(&password); + + sprintf(buffer, + "prot '%s' user '%s' passwd '%s' host '%s' port '%s' doc '%s' query '%s'", + protocol.c_str(), + user.c_str(), + password.c_str(), + host.c_str(), + port.c_str(), + doc.c_str(), + query.c_str()); + printf("%s -> %s\n", in.c_str(), buffer); + assert(expected == buffer); +} + +int main(int argc, char **argv) +{ + test("http://user:passwd@host/patha/pathb?query", + "prot 'http' user 'user' passwd 'passwd' host 'host' port '' doc 'patha/pathb' query 'query'"); + test("http://user:passwd@host:port/patha/pathb?query", + "prot 'http' user 'user' passwd 'passwd' host 'host' port 'port' doc 'patha/pathb' query 'query'"); + test("file:///foo/bar", + "prot 'file' user '' passwd '' host '' port '' doc 'foo/bar' query ''"); + test("http://host%3a:port?param=value", + "prot 'http' user '' passwd '' host 'host:' port 'port' doc '' query 'param=value'"); + test("http://host%3a?param=value", + "prot 'http' user '' passwd '' host 'host:' port '' doc '' query 'param=value'"); + test("foo%24", + "prot '' user '' passwd '' host 'foo$' port '' doc '' query ''"); + test("foo%2f", + "prot '' user '' passwd '' host 'foo/' port '' doc '' query ''"); + test("foo%2A", + "prot '' user '' passwd '' host 'foo*' port '' doc '' query ''"); + test("foo%24bar", + "prot '' user '' passwd '' host 'foo$bar' port '' doc '' query ''"); + test("%24bar", + "prot '' user '' passwd '' host '$bar' port '' doc '' query ''"); + test("foo%2", + "prot '' user '' passwd '' host 'foo' port '' doc '' query ''"); + test("foo%", + "prot '' user '' passwd '' host 'foo' port '' doc '' query ''"); + test("foo%g", + "prot '' user '' passwd '' host 'foo' port '' doc '' query ''"); + test("foo%gh", + "prot '' user '' passwd '' host 'foo' port '' doc '' query ''"); + test("%ghbar", + "prot '' user '' passwd '' host 'bar' port '' doc '' query ''"); + return 0; +} +#endif // SPLIT_URL_MAIN + #endif //SYSYNC_ENGINE diff --git a/src/sysync_SDK/Sources/sysync_utils.h b/src/sysync_SDK/Sources/sysync_utils.h index ba267eb..5422117 100755 --- a/src/sysync_SDK/Sources/sysync_utils.h +++ b/src/sysync_SDK/Sources/sysync_utils.h @@ -427,8 +427,13 @@ const char *smlFirstItemDataToCharP(const SmlItemListPtr_t aItemListP); // split Hostname into address and port parts void splitHostname(const char *aHost,string *aAddr,string *aPort); -// split URL into protocol, hostname, document name and auth-info (user, password) -void splitURL(const char *aURI,string *aProtocol,string *aHost,string *aDoc,string *aUser, string *aPasswd); +// split URL into protocol, hostname, document name and auth-info (user, password); +// none of the strings are url-decoded, do that as needed +void splitURL(const char *aURI,string *aProtocol,string *aHost,string *aDoc,string *aUser, string *aPasswd, + string *aPort, string *aQuery); + +// in-place decoding of %XX, NULL pointer allowed +void urlDecode(string *str); // returns error code made ready for SyncML sending (that is, remove offset // of 10000 if present, and make generic error 500 for non-SyncML errors, -- cgit v1.2.3 From f7e179f0ad30dec95fbc2de68b94d3edce685b5d Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 8 Mar 2012 16:16:21 +0100 Subject: binfile client: better random LUID generation Avoid integer overflows in the NUMERIC_LOCALIDS by binary-and with 0x7FFFFFFF. Collisions cannot be detected easily because other new fake LUIDs are not in memory. Make collisions less likely by adding a running counter in the string case (starts at 1 each time libsynthesis is loaded). --- src/sysync/binfileimplds.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sysync/binfileimplds.cpp b/src/sysync/binfileimplds.cpp index 70a2196..77844fd 100755 --- a/src/sysync/binfileimplds.cpp +++ b/src/sysync/binfileimplds.cpp @@ -2163,12 +2163,16 @@ bool TBinfileImplDS::implProcessItem( #ifdef NUMERIC_LOCALIDS // assumes that valid local IDs are positive // and can hold values in the range of -1 to -RAND_MAX-1 - localid_t fakelocalid = -rand() - 1; + localid_t fakelocalid = -(rand() & 0x7FFFFFFF) - 1; #else - // TODO: check for collisions with other fake IDs char fakelocalid[STRING_LOCALID_MAXLEN]; - sprintf(fakelocalid, "fake-%d", rand()); + static unsigned int fakeidcounter; + sprintf(fakelocalid, "fake-%u-%d", ++fakeidcounter, rand()); #endif + // Checking for collisions with other fake IDs would be nice, + // but isn't easy because new entries in fChangeLog are not + // in memory. We have to trust the random number generator + // and the sequence counter (for string IDs). memset(&newentry, 0, sizeof(newentry)); ASSIGN_LOCALID_TO_FLD(newentry.dbrecordid,fakelocalid); ASSIGN_LOCALID_TO_ITEM(*aItemP,fakelocalid); -- cgit v1.2.3 From 14b549d4be82ed19ea9ea6ef2f281d4138950a84 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 23 Mar 2012 22:47:49 +0100 Subject: Debug log cosmetics: clarified MaxMsgSize reception and RemoteSyncTypes checking in devInf --- src/sysync/remotedatastore.cpp | 6 +++--- src/sysync/synccommand.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sysync/remotedatastore.cpp b/src/sysync/remotedatastore.cpp index e69e528..0cd8754 100755 --- a/src/sysync/remotedatastore.cpp +++ b/src/sysync/remotedatastore.cpp @@ -251,12 +251,12 @@ bool TRemoteDataStore::setDatastoreDevInf( PDEBUGPRINTFX(DBG_REMOTEINFO,("DevInf provides DSMem: MaxMem=" PRINTF_LLD ", MaxID=" PRINTF_LLD,PRINTF_LLD_ARG(fFreeMemory),PRINTF_LLD_ARG(fMaxID))); } // - SyncCap - standard types currently ignored, only - // out own extensions are relevant. - // Corresponding code in TLocalEngineDS::newDevInfSyncCap() + // our own extensions are relevant. + // Corresponding code in TLocalEngineDS::newDevInfSyncCap() if (aDataStoreDevInfP->synccap) { SmlPcdataListPtr_t stlP = aDataStoreDevInfP->synccap->synctype; // loop through list - PDEBUGBLOCKDESC("RemoteSyncTypes", "Analyzing remote types listed in datastore level SyncCap"); + PDEBUGBLOCKDESC("RemoteSyncTypes", "Analyzing remote sync types listed in datastore level SyncCap"); while (stlP) { if (stlP->data) { const char *type = smlPCDataToCharP(stlP->data); diff --git a/src/sysync/synccommand.cpp b/src/sysync/synccommand.cpp index e56b5d7..65d9e8f 100755 --- a/src/sysync/synccommand.cpp +++ b/src/sysync/synccommand.cpp @@ -615,7 +615,7 @@ bool TSyncHeader::execute(void) // max (outgoing) message size if (metaP->maxmsgsize) { smlPCDataToLong(metaP->maxmsgsize,fSessionP->fMaxOutgoingMsgSize); - PDEBUGPRINTFX(DBG_REMOTEINFO,("MaxMsgSize for outgoing msgs set to %ld",(long)fSessionP->fMaxOutgoingMsgSize)); + PDEBUGPRINTFX(DBG_REMOTEINFO,("MaxMsgSize found in SyncHdr: %ld -> set for outgoing msgs",(long)fSessionP->fMaxOutgoingMsgSize)); } // max (outgoing) object size (SyncML 1.1 only) if (metaP->maxobjsize) { -- cgit v1.2.3 From 7d9678498f1c78303723a508189e5ebf907b7b20 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Sat, 24 Mar 2012 18:24:37 +0100 Subject: show RemoteSyncTypes block in log collapsed by default (stretches devInf log part too much for most cases) --- src/sysync/remotedatastore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysync/remotedatastore.cpp b/src/sysync/remotedatastore.cpp index 0cd8754..c449afa 100755 --- a/src/sysync/remotedatastore.cpp +++ b/src/sysync/remotedatastore.cpp @@ -256,7 +256,7 @@ bool TRemoteDataStore::setDatastoreDevInf( if (aDataStoreDevInfP->synccap) { SmlPcdataListPtr_t stlP = aDataStoreDevInfP->synccap->synctype; // loop through list - PDEBUGBLOCKDESC("RemoteSyncTypes", "Analyzing remote sync types listed in datastore level SyncCap"); + PDEBUGBLOCKDESCCOLL("RemoteSyncTypes", "Analyzing remote sync types listed in datastore level SyncCap"); while (stlP) { if (stlP->data) { const char *type = smlPCDataToCharP(stlP->data); -- cgit v1.2.3 From bcf008437ee93f7e18a8432769d515d1aef3d151 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Sun, 25 Mar 2012 00:14:48 +0100 Subject: XCode/iOS/LLVM/CLANG: Very weird THUMB calculation errors in release builds (but not in debug). Note: This was caused by a compiler bug (see rdar://11116189 and http://openradar.appspot.com/radar?id=1614412) which has been resolved as of XCode 4.4 DP5 ARM builds running on iPad3 and iPhone4 did the calculation from lineartime to unix-timestamp[ms] wrong, result was too low by 4294968!! These devices are both ARMv7, which is said to have well working THUMB support. (ARMv6+THUMB is known to have problems with LLVM.) The problem happened reproducibly using lineartimeToDbInt() with with the data below (but seems to apply to all these conversions. Time result is only 5 seconds off, so I haven't noticed it in daily use, until it hit some allday events which shifted to an earlier day because start time was no longer 00:00:00 but 23:59:55 the day before) Date entered 2012-03-31 00:00:00 lineartime_t 212199955200000 On i386 Debug + ARM Debug (correct): unixtime_ms 1333152000000000 On Arm Release (VERY WRONG, 4294968 off!!): unixtime_ms 1333151995705032 --- src/DB_interfaces/odbc_db/odbcapiagent.cpp | 6 ++++++ src/sysync_ios.xcodeproj/project.pbxproj | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/DB_interfaces/odbc_db/odbcapiagent.cpp b/src/DB_interfaces/odbc_db/odbcapiagent.cpp index 1df9fd4..c2983b8 100644 --- a/src/DB_interfaces/odbc_db/odbcapiagent.cpp +++ b/src/DB_interfaces/odbc_db/odbcapiagent.cpp @@ -1354,6 +1354,12 @@ bool TODBCApiAgent::appendFieldValueLiteral( if (intts) { // Timestamp represented as integer in the DB // - add as integer timestamp +// sInt64 ii = lineartimeToDbInt(ts,aDBFieldType); +// PDEBUGPRINTFX(DBG_DBAPI+DBG_EXOTIC,( +// "ts=%lld -> dbInteger=%lld/0x%llX (UnixToLineartimeOffset=%lld, secondToLinearTimeFactor=%lld)", +// ts,ii,ii,UnixToLineartimeOffset,secondToLinearTimeFactor +// )); +// StringObjAppendPrintf(aSQL,PRINTF_LLD,PRINTF_LLD_ARG(ii)); StringObjAppendPrintf(aSQL,PRINTF_LLD,PRINTF_LLD_ARG(lineartimeToDbInt(ts,aDBFieldType))); } else { diff --git a/src/sysync_ios.xcodeproj/project.pbxproj b/src/sysync_ios.xcodeproj/project.pbxproj index 5215b22..4152bd3 100644 --- a/src/sysync_ios.xcodeproj/project.pbxproj +++ b/src/sysync_ios.xcodeproj/project.pbxproj @@ -2117,6 +2117,7 @@ GENERATE_MASTER_OBJECT_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 3.0; ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = "-mno-thumb"; SDKROOT = iphoneos; }; name = Debug; @@ -2128,9 +2129,11 @@ armv6, armv7, ); + GCC_C_LANGUAGE_STANDARD = c99; GCC_THUMB_SUPPORT = NO; GENERATE_MASTER_OBJECT_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 3.0; + OTHER_CFLAGS = "-mno-thumb"; SDKROOT = iphoneos; }; name = Release; @@ -2192,7 +2195,6 @@ CONFIGURATION_BUILD_DIR = "$(PROJECT_DIR)/sysync_SDK/bin/iOS/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = c99; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_PREFIX_HEADER = prefix_file.h; GCC_THUMB_SUPPORT = NO; -- cgit v1.2.3 From 2d5b114b1627a2cc218b32cd5f70de91ac885447 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 16 Mar 2012 13:25:31 +0100 Subject: iOS: added readme and fixed SYNCMMLIB build setting which was pointing to the wrong library (sample uses the client lib, not the combi lib) --- .../READ-ME-iOS_syncclient_sample.txt | 101 +++++++++++++++++++++ .../project.pbxproj | 4 +- 2 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 src/sysync_SDK/ios_syncclient_app_sample/READ-ME-iOS_syncclient_sample.txt diff --git a/src/sysync_SDK/ios_syncclient_app_sample/READ-ME-iOS_syncclient_sample.txt b/src/sysync_SDK/ios_syncclient_app_sample/READ-ME-iOS_syncclient_sample.txt new file mode 100644 index 0000000..883f2bb --- /dev/null +++ b/src/sysync_SDK/ios_syncclient_app_sample/READ-ME-iOS_syncclient_sample.txt @@ -0,0 +1,101 @@ +Sample code for libsynthesis on iOS +=================================== + +This directory contains a fully functional Cocoa touch based iPhone +application using libsynthesis for iPhone to +implement two-way SyncML synchronisation of contacts and calendar +stored in TAB separated text files (using the built-in textdb plugin). + +Note that plan44.ch licenses commercial plugins for iOS native +contacts and calendars, which are not part of libsynthesis. + +Direct mapping to SQLite3 databases however is part of libsynthesis +and can be used to create sync apps without writing DB plugins. +See doc/SySync_config_reference.pdf for information about configuration +of libsynthesis. + +Due to the requirement of static linking in iOS, libsynthesis +cannot be linked into a iOS app project without also linking another +*.a library containing 4 custom plugins (named iphone_dbplugin1..4). + +The SDK provides a sample project with those 4 (dummy) plugins, which builds such a +library (src/dbplugin_cocoa_sample/dbplugin_cocoa_sample_ios.xcodeproj). +By adding functionality into the sample_dbplugin1..4.mm/.h files, up to 4 +different custom DB backends can be implemented. + +Please contact plan44.ch for licensing conditions for the native contacts and +calendar plugins. + + +SyncML Client sample application +-------------------------------- + +The sample application shows how to interface with the libsynthesis +to configure the settings and how to run a sync session. +The UI is minimal but fully functional. + +The "SyncMLClientSample" application is based on the "Utility Application" XCode +template which is part of the iPhone OS SDK. It includes libsynthesis and +the 4 dummy DB plugins as subprojects. + +A few modifications were made to the basic template generated project as +follows (you don't need to understand these to give the SDK a try and +compile the sample as is, but it's probably useful information if you +want to use the library in your own app): + +1) We added a user-defined build setting on the target level called + SYNCMLLIB which defines the library to use. In the sample project, this + is set to "sysync_client_ios" which means using the client-only library. + This settings is the place to edit when you want to use another build of + libsynthesis (such as the combined client+server library "sysync_combi_ios"). + Note that you also need to change subproject target dependencies accordingly. + +4) The Debug target build configuration has a GCC_PREPROCESSOR_DEFINITIONS + setting DEBUG=1. The DEBUG symbol is used to switch on some extra debug + information to the console. Make sure you DONT have + DEBUG defined in release builds! + + +To build and run the SyncMLClientSample iPhone application, take the following +steps: + +1) Open the "ios_syncclient_app_sample.xcodeproj" XCode project using + a recent XCode version with a iOS SDK >4.2 + +2) Choose the "ios_syncclient_app_sample" target. + +3) Set the desired target SDK/Platform. You can build for the simulator or the + device. For the device, you need to configure the codesign certificates + in the target settings. + +4) Build the project to create the "ios_syncclient_app_sample.app" application. + +5) Run the app (in the simulator or on a connected debug iOS device. + Press the small "i" button to open the settings screen where you + can put in a SyncML server URL, login, server path and sync mode. Press + "Start Synchronisation" to start a sync session synchronizing the + contents of TAB separated text files in the /Documents directory of + the app's sandbox to a SyncML server. + + +The Cocoa wrapper classes for the Sync Engine, Settings Keys, and DB Plugins +---------------------------------------------------------------------------- + +libsynthesis includes three Objective C wrapper files in the +"src/sysync_SDK/sdk_sources_cocoa" which wrap the quite complex libsynthesis +C API into easy to use Cocoa objects. + +The sample project uses the wrapper for the sync +engine itself (SyncEngine object, used within the SyncMLClient object), +for a sync session (SyncSession object) and for the settings key (SettingsKey object). +The SettingsKey object has basic KVC capability so it can be used with UI components +which use KVC to address their content (this feature is however not used +in the sample app, instead direct accessor methods modelled after the C +API of the library are used). + +The file "dbplugin_cocoa" contains wrapper classes for creating custom +database adaptor plugins. +The dbplugin_cocoa_sample_ios.xcodeproj project contains a set of +4 dummy DB plugins that can be extended to create real DB plugins. + +(c) 2011-2012 by plan44.ch \ No newline at end of file diff --git a/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/project.pbxproj b/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/project.pbxproj index fcc4f61..9f38ac6 100644 --- a/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/project.pbxproj +++ b/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/project.pbxproj @@ -487,7 +487,7 @@ "$(SYNCMLPLUGINS)", ); PRODUCT_NAME = "$(TARGET_NAME)"; - SYNCMLLIB = sysync_combi_ios; + SYNCMLLIB = sysync_client_ios; SYNCMLPLUGINS = dbplugin_cocoa_sample_ios; WRAPPER_EXTENSION = app; }; @@ -506,7 +506,7 @@ "$(SYNCMLPLUGINS)", ); PRODUCT_NAME = "$(TARGET_NAME)"; - SYNCMLLIB = sysync_combi_ios; + SYNCMLLIB = sysync_client_ios; SYNCMLPLUGINS = dbplugin_cocoa_sample_ios; WRAPPER_EXTENSION = app; }; -- cgit v1.2.3 From a3ca3715d5983bfbe75685fd9f182eea1cd6c769 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 16 Mar 2012 13:37:30 +0100 Subject: iOS sample app: fixed inconsistencies with dbtypeid, now using SYNCML_TARGET_DBID definition everywhere. Before, the main view did use dbtype 1001 hard-coded, which is not correct. Now, use the #define SYNCML_TARGET_DBID in AppDelegate.h to select the dbtypeid. The default is 2001, which are textfile based contacts, which match the sample XML config. --- .../ios_syncclient_app_sample/FlipsideViewController.m | 14 ++++++-------- .../ios_syncclient_app_sample/MainViewController.m | 9 +++++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/sysync_SDK/ios_syncclient_app_sample/FlipsideViewController.m b/src/sysync_SDK/ios_syncclient_app_sample/FlipsideViewController.m index 1b440c2..5b908d6 100644 --- a/src/sysync_SDK/ios_syncclient_app_sample/FlipsideViewController.m +++ b/src/sysync_SDK/ios_syncclient_app_sample/FlipsideViewController.m @@ -174,11 +174,10 @@ account:@"SyncMLPassword" error:NULL ]; - // Now get the per-datastore settings. In this sample, we only have one single datastore (using the - // built-in datastore adaptor for the iPhone address book). + // Now get the per-datastore settings. In this sample, we only have one single datastore. // The datastores are identified by their , which is defined in the the - // iphone_client_sample.xml config file. The addressbook datastore is named "contacts" and has - // a dbtypeid of 1001 + // ios_syncclient_app_sample.xml config file. + // For this app, the actual dbtypeid numeric value is defined as SYNCML_TARGET_DBID in AppDelegate.h // - first open the targets container (a "target" represents settings for one datastore) SettingsKey *targetsKey = [profileKey newOpenKeyByPath:"/targets" withMode:0 err:&sta]; if (targetsKey) { @@ -247,11 +246,10 @@ account:@"SyncMLPassword" error:NULL ]; - // Now save the per-datastore settings. In this sample, we only have one single datastore (using the - // built-in datastore adaptor for the iPhone address book). + // Now save the per-datastore settings. In this sample, we only have one single datastore. // The datastores are identified by their , which is defined in the the - // iphone_client_sample.xml config file. The addressbook datastore is named "contacts" and has - // a dbtypeid of 1001 + // ios_syncclient_app_sample.xml config file. + // For this app, the actual dbtypeid numeric value is defined as SYNCML_TARGET_DBID in AppDelegate.h // - first open the targets container (a "target" represents settings for one datastore) SettingsKey *targetsKey = [profileKey newOpenKeyByPath:"/targets" withMode:0 err:&sta]; if (sta==LOCERR_OK) { diff --git a/src/sysync_SDK/ios_syncclient_app_sample/MainViewController.m b/src/sysync_SDK/ios_syncclient_app_sample/MainViewController.m index 9172edd..2b6fb23 100644 --- a/src/sysync_SDK/ios_syncclient_app_sample/MainViewController.m +++ b/src/sysync_SDK/ios_syncclient_app_sample/MainViewController.m @@ -8,6 +8,7 @@ #import "MainViewController.h" +#include "AppDelegate.h" @interface MainViewController () // private methods declaration @@ -292,9 +293,9 @@ // use global status label for "Last Sync" title self.globalStatusLabel.text = @"Last Synchronisation:"; - // show the time of last sync of the datastore with ID 1001 - // we need to open the current profile, and find the target with ID 1001 (this is the of - // the datastore as configured in the xml config file) + // show the time of last sync of the datastore with ID SYNCML_TARGET_DBID + // we need to open the current profile, and find the target with ID SYNCML_TARGET_DBID + // (this is the of the datastore as configured in the xml config file) SettingsKey *profileKey = [self.syncmlClient newProfileKeyForID:PROFILEID_FIRST]; if (profileKey) { @@ -305,7 +306,7 @@ // successfully opened targets container // - now open the target we are interested in, which is that for the address book // by its ID 1001 - SettingsKey *targetKey = [targetsKey newOpenSubKeyByID:1001 withMode:0 err:&sta]; + SettingsKey *targetKey = [targetsKey newOpenSubKeyByID:SYNCML_TARGET_DBID withMode:0 err:&sta]; if (sta==LOCERR_OK) { // get the date of last sync NSDate *lastSync = [targetKey dateValueByName:"lastSync"]; -- cgit v1.2.3 From c6bbe8cb2ce1f6467da85434975b2037ef318171 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Sun, 25 Mar 2012 01:18:01 +0100 Subject: iOS sample app: changed linking of libsynthesis and plugins to standard way to do it. Also improved default logging. Before, we used a custom -l linker flag and custom settings to select the library. Now we use the standard way, which is adding the static libraries to link in the "link binary with libraries" section of "Build Phases". Logging improvements: - config variable "conferrpath" is set to "console", XML configuration error messages are sent to stdout (= console) and can be viewed in XCode debugger or organizer. Note that "conferrpath" can also be set to a file path where to log configuration error messages instead. - the syncml engine can output detailed HTML logs. In the sample app, the XML configuration is set up to store these logs in tmp/sysynclogs, however loggings is automatically disabled if that subdirectory does not exist. Therefore, the sample app now auto-creates this directory at startup for DEBUG builds (for release builds, logging should not be enabled by default, as synclogs can get very large (several 10s of megabytes)). --- .../project.pbxproj | 2 +- .../xcschemes/dbplugin_cocoa_sample_ios.xcscheme | 4 +- .../ios_syncclient_app_sample/MainViewController.m | 20 +++++++--- .../READ-ME-iOS_syncclient_sample.txt | 45 +++++++++++++--------- .../project.pbxproj | 22 ++--------- .../xcschemes/ios_syncclient_app_sample.xcscheme | 6 ++- .../ios_syncclient_app_sample.xml | 4 ++ 7 files changed, 58 insertions(+), 45 deletions(-) diff --git a/src/sysync_SDK/dbplugin_cocoa_sample/dbplugin_cocoa_sample_ios.xcodeproj/project.pbxproj b/src/sysync_SDK/dbplugin_cocoa_sample/dbplugin_cocoa_sample_ios.xcodeproj/project.pbxproj index 2db6a8f..38792c1 100644 --- a/src/sysync_SDK/dbplugin_cocoa_sample/dbplugin_cocoa_sample_ios.xcodeproj/project.pbxproj +++ b/src/sysync_SDK/dbplugin_cocoa_sample/dbplugin_cocoa_sample_ios.xcodeproj/project.pbxproj @@ -202,7 +202,7 @@ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0420; + LastUpgradeCheck = 0430; }; buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "dbplugin_cocoa_sample_ios" */; compatibilityVersion = "Xcode 3.2"; diff --git a/src/sysync_SDK/dbplugin_cocoa_sample/dbplugin_cocoa_sample_ios.xcodeproj/xcshareddata/xcschemes/dbplugin_cocoa_sample_ios.xcscheme b/src/sysync_SDK/dbplugin_cocoa_sample/dbplugin_cocoa_sample_ios.xcodeproj/xcshareddata/xcschemes/dbplugin_cocoa_sample_ios.xcscheme index 3c85223..31ecf52 100644 --- a/src/sysync_SDK/dbplugin_cocoa_sample/dbplugin_cocoa_sample_ios.xcodeproj/xcshareddata/xcschemes/dbplugin_cocoa_sample_ios.xcscheme +++ b/src/sysync_SDK/dbplugin_cocoa_sample/dbplugin_cocoa_sample_ios.xcodeproj/xcshareddata/xcschemes/dbplugin_cocoa_sample_ios.xcscheme @@ -1,5 +1,6 @@ diff --git a/src/sysync_SDK/ios_syncclient_app_sample/MainViewController.m b/src/sysync_SDK/ios_syncclient_app_sample/MainViewController.m index 2b6fb23..d4910f7 100644 --- a/src/sysync_SDK/ios_syncclient_app_sample/MainViewController.m +++ b/src/sysync_SDK/ios_syncclient_app_sample/MainViewController.m @@ -235,19 +235,29 @@ { // add setting of SyncML engine configuration variables here. // These will be set before the XML config is loaded - // pre-set some config vars - /* TSyError sta; SettingsKey *configVarsKey = [aSyncEngine newOpenKeyByPath:"/configvars" withMode:0 err:&sta]; if (sta==LOCERR_OK) { + #if DEBUG + // In debug builds, direct XML configuration errors to console (stdout) + [configVarsKey setStringValueByName:"conferrpath" toValue:@"console"]; + #endif + /* %%% enable these if you want a custom model or hardcoded URL // custom devInf model string [configVarsKey setStringValueByName:"custmodel" toValue:@"" CUSTOM_DEVINF_MODEL]; // custom predefined (fixed) URL - [configVarsKey setStringValueByName:"serverurl" toValue:@"" CUSTOM_SERVER_URL]; + [configVarsKey setStringValueByName:"serverurl" toValue:@"" CUSTOM_SERVER_URL]; + */ } // done with config vars - [configVarsKey release]; - */ + [configVarsKey release]; + // if debug build, create the log subdirectory in the sandbox' tmp/, such that + // the syncml engine will write detailed HTML logs. The engine checks the presence of + // the log directory and disables logging if it is not present. + // Note: this has to be in sync with the definition of "logpath" in the XML config + #if DEBUG + [[NSFileManager defaultManager] createDirectoryAtPath:[NSHomeDirectory() stringByAppendingPathComponent:@"tmp/sysynclogs"] withIntermediateDirectories:NO attributes:nil error:NULL]; + #endif } diff --git a/src/sysync_SDK/ios_syncclient_app_sample/READ-ME-iOS_syncclient_sample.txt b/src/sysync_SDK/ios_syncclient_app_sample/READ-ME-iOS_syncclient_sample.txt index 883f2bb..88b81a5 100644 --- a/src/sysync_SDK/ios_syncclient_app_sample/READ-ME-iOS_syncclient_sample.txt +++ b/src/sysync_SDK/ios_syncclient_app_sample/READ-ME-iOS_syncclient_sample.txt @@ -38,24 +38,6 @@ The "SyncMLClientSample" application is based on the "Utility Application" XCode template which is part of the iPhone OS SDK. It includes libsynthesis and the 4 dummy DB plugins as subprojects. -A few modifications were made to the basic template generated project as -follows (you don't need to understand these to give the SDK a try and -compile the sample as is, but it's probably useful information if you -want to use the library in your own app): - -1) We added a user-defined build setting on the target level called - SYNCMLLIB which defines the library to use. In the sample project, this - is set to "sysync_client_ios" which means using the client-only library. - This settings is the place to edit when you want to use another build of - libsynthesis (such as the combined client+server library "sysync_combi_ios"). - Note that you also need to change subproject target dependencies accordingly. - -4) The Debug target build configuration has a GCC_PREPROCESSOR_DEFINITIONS - setting DEBUG=1. The DEBUG symbol is used to switch on some extra debug - information to the console. Make sure you DONT have - DEBUG defined in release builds! - - To build and run the SyncMLClientSample iPhone application, take the following steps: @@ -78,6 +60,33 @@ steps: the app's sandbox to a SyncML server. +Linking information +------------------- + +libsynthesis and the plugins are linked as static libraries (iOS does not +allow dylibs). + +For that, the library projects are included as subprojects into the sample app +project. +Two settings need to be made to have XCode build and link everything (these +are already set up in the sample project, but are important to understand in case +you want to use another libsynthesis target or another plugin library) + +1) In the app's target settings under "build phases", "target dependencies" + need to be defined for the libsynthesis target you want to use and for + the plugin library target. + +2) Also in the app's target settings, under "build phases", "link binary + with libraries" the actual library builds (.a files) need to be + selected. When you press "+", the first folder presented is called + "workspace" and lists the libraries available. + +Note: The Debug target build configuration has a GCC_PREPROCESSOR_DEFINITIONS + setting DEBUG=1. The DEBUG symbol is used to switch on some extra debug + information to the console. Make sure you DONT have + DEBUG defined in release builds! + + The Cocoa wrapper classes for the Sync Engine, Settings Keys, and DB Plugins ---------------------------------------------------------------------------- diff --git a/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/project.pbxproj b/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/project.pbxproj index 9f38ac6..09375d8 100644 --- a/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/project.pbxproj +++ b/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/project.pbxproj @@ -28,6 +28,8 @@ ED46AB9914238A160057DC96 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = ED46AB9814238A160057DC96 /* libsqlite3.dylib */; }; EDAF2DCB14A344510054960A /* ios_syncclient_app_sample.xml in Resources */ = {isa = PBXBuildFile; fileRef = EDAF2DCA14A344510054960A /* ios_syncclient_app_sample.xml */; }; EDC0F81E14A343530006710A /* SettingsKey.m in Sources */ = {isa = PBXBuildFile; fileRef = ED46AB67142348C10057DC96 /* SettingsKey.m */; }; + EDE01B23151E975200352246 /* libdbplugin_cocoa_sample_ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EDAF2DD414A346400054960A /* libdbplugin_cocoa_sample_ios.a */; }; + EDE01B24151E975200352246 /* libsysync_client_ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EDAF2DDE14A3464D0054960A /* libsysync_client_ios.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -119,6 +121,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + EDE01B23151E975200352246 /* libdbplugin_cocoa_sample_ios.a in Frameworks */, + EDE01B24151E975200352246 /* libsysync_client_ios.a in Frameworks */, ED46AB9914238A160057DC96 /* libsqlite3.dylib in Frameworks */, ED46AB95142389E40057DC96 /* libstdc++.6.dylib in Frameworks */, ED46AB91142381A50057DC96 /* Security.framework in Frameworks */, @@ -447,7 +451,6 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 4.0; - LIBRARY_SEARCH_PATHS = "$(SRCROOT)/../bin/iOS/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -466,7 +469,6 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 4.0; - LIBRARY_SEARCH_PATHS = "$(SRCROOT)/../bin/iOS/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -480,15 +482,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ios_syncclient_app_sample-Prefix.pch"; INFOPLIST_FILE = "ios_syncclient_app_sample-Info.plist"; - OTHER_LDFLAGS = ( - "-l", - "$(SYNCMLLIB)", - "-l", - "$(SYNCMLPLUGINS)", - ); PRODUCT_NAME = "$(TARGET_NAME)"; - SYNCMLLIB = sysync_client_ios; - SYNCMLPLUGINS = dbplugin_cocoa_sample_ios; WRAPPER_EXTENSION = app; }; name = Debug; @@ -499,15 +493,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "ios_syncclient_app_sample-Prefix.pch"; INFOPLIST_FILE = "ios_syncclient_app_sample-Info.plist"; - OTHER_LDFLAGS = ( - "-l", - "$(SYNCMLLIB)", - "-l", - "$(SYNCMLPLUGINS)", - ); PRODUCT_NAME = "$(TARGET_NAME)"; - SYNCMLLIB = sysync_client_ios; - SYNCMLPLUGINS = dbplugin_cocoa_sample_ios; WRAPPER_EXTENSION = app; }; name = Release; diff --git a/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/xcshareddata/xcschemes/ios_syncclient_app_sample.xcscheme b/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/xcshareddata/xcschemes/ios_syncclient_app_sample.xcscheme index 790f778..975c07d 100644 --- a/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/xcshareddata/xcschemes/ios_syncclient_app_sample.xcscheme +++ b/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xcodeproj/xcshareddata/xcschemes/ios_syncclient_app_sample.xcscheme @@ -1,5 +1,6 @@ diff --git a/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xml b/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xml index 7098ee4..870b46c 100644 --- a/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xml +++ b/src/sysync_SDK/ios_syncclient_app_sample/ios_syncclient_app_sample.xml @@ -815,6 +815,10 @@ no + $(textfilespath) -- cgit v1.2.3 From 67b23c3e394f21a6c20e2ac33882c54fd807921f Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Wed, 23 May 2012 15:13:48 +0200 Subject: SyncML TK: Fixed stoneage (2003) bug in encoder which caused XML generation of empty tags to fail, such as in SE LT26i devInf. For the WBXML part, someone named "Tomy" already fixed this in 2003, and also intended to fix it for XML, but apparently a cut&paste accident left a "return SML_ERR_XLT_INVAL_PCDATA_TYPE" statement in place, which he probably intended to replace with his new code. So, 9 years later, this fix is now complete :-) --- src/syncml_tk/src/sml/xlt/all/xltenc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/syncml_tk/src/sml/xlt/all/xltenc.c b/src/syncml_tk/src/sml/xlt/all/xltenc.c index 700874b..46ff6fd 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltenc.c +++ b/src/syncml_tk/src/sml/xlt/all/xltenc.c @@ -1224,7 +1224,7 @@ Ret_t xltEncPcdata(XltTagID_t tagId, XltRO_t reqOptFlag, const VoidPtr_t pConten } #endif default: { - return SML_ERR_XLT_INVAL_PCDATA_TYPE; + // 2003-11-24: Tomy to deal with pcdata empty extensions (for example which is valid) // refer to xltdec.c to see that empty extensions result in SmlPcdataPtr_t with all fields (data) set to 0 if (((SmlPcdataPtr_t)pContent)->contentType != SML_PCDATA_UNDEFINED || ((SmlPcdataPtr_t)pContent)->extension != SML_EXT_UNDEFINED || @@ -1428,7 +1428,7 @@ Ret_t xltBuildExtention(SmlPcdataExtension_t extId, XltRO_t reqOptFlag, VoidPtr_ /* in the latter case, we need a special treatment of this sub-dtd, as we have */ /* to put it into a SML_PCDATA_OPAQUE field ... */ if (enc == SML_XML) - return devinfEncBlock(TN_DEVINF_DEVINF,reqOptFlag,pContent,enc,pBufMgr,SML_EXT_DEVINF); + return devinfEncBlock(TN_DEVINF_DEVINF,reqOptFlag,pContent,enc,pBufMgr,SML_EXT_DEVINF); else return subdtdEncWBXML(TN_DEVINF_DEVINF,reqOptFlag,pContent,SML_WBXML,pBufMgr,SML_EXT_DEVINF); break; -- cgit v1.2.3 From 229c28aaa580a3a1716a13b4103f2f3ff120addc Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Wed, 23 May 2012 15:16:56 +0200 Subject: sysytool/sysytest: added error message output when wbxml2xml conversion is aborted due to an internal ENcoder error. So far, wbxml2xml just seemed to complete ok, but XML output was incomplete for no apparent reason and without any error message. --- src/global_options.h | 6 +++--- src/sysync/syncappbase.cpp | 44 +++++++++++++++++++++++--------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/global_options.h b/src/global_options.h index 39be1bb..5013de1 100644 --- a/src/global_options.h +++ b/src/global_options.h @@ -32,13 +32,13 @@ #if defined(EXPIRY_YEAR) && defined(SYSER_REGISTRATION) #error "it seems that this target still defines it's own private expiry date - please update target_options.h" #endif -#if defined(EXPIRY_YEAR) && EXPIRY_YEAR<2010 +#if defined(EXPIRY_YEAR) && EXPIRY_YEAR<2014 #warning "Target has a dangerously early expiry year - please check if it is correct" #endif // global expiry date (usually applies for unregistered demos or regular products in trial mode) #ifndef EXPIRY_YEAR - #define EXPIRY_DATE_STRING "2013-03-31" - #define EXPIRY_YEAR 2013 + #define EXPIRY_DATE_STRING "2014-03-31" + #define EXPIRY_YEAR 2014 #define EXPIRY_MONTH 3 #define EXPIRY_DAY 31 #endif diff --git a/src/sysync/syncappbase.cpp b/src/sysync/syncappbase.cpp index 890da37..81d9f77 100755 --- a/src/sysync/syncappbase.cpp +++ b/src/sysync/syncappbase.cpp @@ -3408,6 +3408,8 @@ bool TRootConfig::parseAgentConfig(const char **aAttributes, sInt32 aLine) #define GET_XMLOUTINSTANCE(u) ((InstanceID_t)GET_USERDATA(u)) +#define ERRCHK(CMDNAME,GENFUNC) { Ret_t r = GENFUNC; if (r!=SML_ERR_OK) CONSOLEPRINTF(("Error converting %s to XML -> XML output truncated!",CMDNAME)); } + // SyncML toolkit callback implementations for sysytool debug decoder // userData must be instance_id of XML instance to generate XML message into @@ -3423,19 +3425,19 @@ static Ret_t sysytoolStartMessageCallback(InstanceID_t id, VoidPtr_t userData, S static Ret_t sysytoolEndMessageCallback(InstanceID_t id, VoidPtr_t userData, Boolean_t final) { - smlEndMessage(GET_XMLOUTINSTANCE(userData),final); + ERRCHK("End Of Message",smlEndMessage(GET_XMLOUTINSTANCE(userData),final)); return SML_ERR_OK; } static Ret_t sysytoolStartSyncCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncPtr_t pContent) { - smlStartSync(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlStartSync(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } static Ret_t sysytoolEndSyncCallback(InstanceID_t id, VoidPtr_t userData) { - smlEndSync(GET_XMLOUTINSTANCE(userData)); + ERRCHK("",smlEndSync(GET_XMLOUTINSTANCE(userData))); return SML_ERR_OK; } @@ -3444,13 +3446,13 @@ static Ret_t sysytoolEndSyncCallback(InstanceID_t id, VoidPtr_t userData) static Ret_t sysytoolStartAtomicCallback(InstanceID_t id, VoidPtr_t userData, SmlAtomicPtr_t pContent) { - smlStartAtomic(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlStartAtomic(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } static Ret_t sysytoolEndAtomicCallback(InstanceID_t id, VoidPtr_t userData) { - smlEndAtomic(GET_XMLOUTINSTANCE(userData)); + ERRCHK("",smlEndAtomic(GET_XMLOUTINSTANCE(userData))); return SML_ERR_OK; } @@ -3460,13 +3462,13 @@ static Ret_t sysytoolEndAtomicCallback(InstanceID_t id, VoidPtr_t userData) static Ret_t sysytoolStartSequenceCallback(InstanceID_t id, VoidPtr_t userData, SmlSequencePtr_t pContent) { - smlStartSequence(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlStartSequence(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } static Ret_t sysytoolEndSequenceCallback(InstanceID_t id, VoidPtr_t userData) { - smlEndSequence(GET_XMLOUTINSTANCE(userData)); + ERRCHK("",smlEndSequence(GET_XMLOUTINSTANCE(userData))); return SML_ERR_OK; } @@ -3474,39 +3476,39 @@ static Ret_t sysytoolEndSequenceCallback(InstanceID_t id, VoidPtr_t userData) static Ret_t sysytoolAddCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAddPtr_t pContent) { - smlAddCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlAddCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } static Ret_t sysytoolAlertCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent) { - smlAlertCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlAlertCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } static Ret_t sysytoolDeleteCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlDeletePtr_t pContent) { - smlDeleteCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlDeleteCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } static Ret_t sysytoolGetCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlGetPtr_t pContent) { - smlGetCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlGetCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } static Ret_t sysytoolPutCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlPutPtr_t pContent) { - smlPutCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlPutCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } #ifdef MAP_RECEIVE static Ret_t sysytoolMapCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMapPtr_t pContent) { - smlMapCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlMapCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } #endif @@ -3514,41 +3516,41 @@ static Ret_t sysytoolMapCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMapP #ifdef RESULT_RECEIVE static Ret_t sysytoolResultsCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlResultsPtr_t pContent) { - smlResultsCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlResultsCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } #endif static Ret_t sysytoolStatusCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlStatusPtr_t pContent) { - smlStatusCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlStatusCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } static Ret_t sysytoolReplaceCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlReplacePtr_t pContent) { - smlReplaceCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlReplaceCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } #ifdef COPY_RECEIVE static Ret_t sysytoolCopyCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlCopyPtr_t pContent) { - smlCopyCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlCopyCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } #endif static Ret_t sysytoolMoveCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMovePtr_t pContent) { - smlMoveCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlMoveCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } #ifdef EXEC_RECEIVE static Ret_t sysytoolExecCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlExecPtr_t pContent) { - smlExecCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlExecCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } #endif @@ -3557,7 +3559,7 @@ static Ret_t sysytoolExecCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlExe #ifdef SEARCH_RECEIVE static Ret_t sysytoolSearchCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlSearchPtr_t pContent) { - smlSearchCmd(GET_XMLOUTINSTANCE(userData),pContent); + ERRCHK("",smlSearchCmd(GET_XMLOUTINSTANCE(userData),pContent)); return SML_ERR_OK; } #endif @@ -3574,6 +3576,7 @@ static Ret_t sysytoolTransmitChunkCallback(InstanceID_t id, VoidPtr_t userData) } + static const SmlCallbacks_t sysyncToolCallbacks = { /* message callbacks */ sysytoolStartMessageCallback, @@ -3620,7 +3623,6 @@ static const SmlCallbacks_t sysyncToolCallbacks = { }; /* sml_callbacks struct */ - // WBXML to XML conversion int wbxmlConv(int argc, const char *argv[]) { -- cgit v1.2.3 From 68e47b66a32d3d29be68473fc3cf92a3bd049b4d Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 25 May 2012 14:42:34 +0200 Subject: SyncML TK: Added missing parsing error debug output for parsing --- src/syncml_tk/src/sml/xlt/all/xltmetinf.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/syncml_tk/src/sml/xlt/all/xltmetinf.c b/src/syncml_tk/src/sml/xlt/all/xltmetinf.c index f37a470..e171a45 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltmetinf.c +++ b/src/syncml_tk/src/sml/xlt/all/xltmetinf.c @@ -74,7 +74,7 @@ Ret_t buildMetInfAnchorCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { pScanner = pDecoder->scanner; if (*ppElem != NULL) - return SML_ERR_XLT_INVAL_SYNCML_DOC; + return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMetInfAnchorCmd"); if ((pAnchor = (SmlMetInfAnchorPtr_t)smlLibMalloc(sizeof(SmlMetInfAnchor_t))) == NULL) return SML_ERR_NOT_ENOUGH_SPACE; @@ -101,7 +101,7 @@ Ret_t buildMetInfAnchorCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { rc = buildPCData(pDecoder, (VoidPtr_t)&pAnchor->next); break; default: - rc = SML_ERR_XLT_INVAL_SYNCML_DOC; + rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC,pScanner,"buildMetInfAnchorCmd_2"); } if (rc != SML_ERR_OK) { smlFreeMetinfAnchor(pAnchor); @@ -127,7 +127,7 @@ Ret_t buildMetInfMemCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { pScanner = pDecoder->scanner; if (*ppElem != NULL) - return SML_ERR_XLT_INVAL_SYNCML_DOC; + return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC, pScanner, "buildMetInfMemCmd"); if ((pMem = (SmlMetInfMemPtr_t)smlLibMalloc(sizeof(SmlMetInfMem_t))) == NULL) return SML_ERR_NOT_ENOUGH_SPACE; @@ -166,7 +166,7 @@ Ret_t buildMetInfMemCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { break; default: - rc = SML_ERR_XLT_INVAL_SYNCML_DOC; + rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC, pScanner, "buildMetInfMemCmd_2"); } if (rc != SML_ERR_OK) { smlFreeMetinfMem(pMem); @@ -193,7 +193,7 @@ Ret_t buildMetInfMetInfCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { pScanner = pDecoder->scanner; if (*ppElem != NULL) - return SML_ERR_XLT_INVAL_SYNCML_DOC; + return SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC, pScanner, "buildMetInfMetInfCmd"); if ((pMeta = (SmlMetInfMetInfPtr_t)smlLibMalloc(sizeof(SmlMetInfMetInf_t))) == NULL) return SML_ERR_NOT_ENOUGH_SPACE; @@ -257,7 +257,7 @@ Ret_t buildMetInfMetInfCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { break; default: - rc = SML_ERR_XLT_INVAL_SYNCML_DOC; + rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC, pScanner, "buildMetInfMetInfCmd_2"); } if (rc != SML_ERR_OK) { smlFreeMetinfMetinf(pMeta); -- cgit v1.2.3 From ca121b9e50abef4b9a2dbb62a6d44b3f07f0d081 Mon Sep 17 00:00:00 2001 From: Andris Pavenis Date: Fri, 25 May 2012 12:42:58 +0300 Subject: Fix configure option --enable-debug-logs help string --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 50e0903..390fada 100644 --- a/configure.in +++ b/configure.in @@ -28,7 +28,7 @@ PIC_CXXFLAGS="$lt_prog_compiler_pic_CXX" AC_SUBST(PIC_CXXFLAGS) AC_ARG_ENABLE(debug-logs, - AS_HELP_STRING([--debug-logs], + AS_HELP_STRING([--enable-debug-logs], [For developers: add links to call location to HTML log files. Depends on Doxygen (for HTML version of source) and g++ (for __PRETTY_FUNCTION__).]), enable_debug_logs="$enableval", enable_debug_logs="no") if test "$enable_debug_logs" = "yes"; then -- cgit v1.2.3 From 7227b2a8a8c339e4d8cf38c6591b968261893314 Mon Sep 17 00:00:00 2001 From: Andris Pavenis Date: Tue, 29 May 2012 12:21:58 +0300 Subject: Workaround problem with Sony Xperia 2012 (S and perhaps also P and U) phones Data element in Alert command contains Next (syncml:metinf) directly instead of inside Anchor element. Handle this case. One could easily add also handle element Last in the same way, but it is not implemented yet. --- src/syncml_tk/src/sml/xlt/all/xltmetinf.c | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/syncml_tk/src/sml/xlt/all/xltmetinf.c b/src/syncml_tk/src/sml/xlt/all/xltmetinf.c index e171a45..8d88c44 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltmetinf.c +++ b/src/syncml_tk/src/sml/xlt/all/xltmetinf.c @@ -119,6 +119,37 @@ Ret_t buildMetInfAnchorCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { return SML_ERR_OK; } +Ret_t buildMetinfNextCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { + XltDecScannerPtr_t pScanner; + SmlMetInfAnchorPtr_t pAnchor; + Ret_t rc; + + pScanner = pDecoder->scanner; + + if (*ppElem == NULL) { + if ((pAnchor = (SmlMetInfAnchorPtr_t)smlLibMalloc(sizeof(SmlMetInfAnchor_t))) == NULL) + return SML_ERR_NOT_ENOUGH_SPACE; + smlLibMemset(pAnchor, 0, sizeof(SmlMetInfAnchor_t)); + } else { + pAnchor = (SmlMetInfAnchorPtr_t)*ppElem; + if (pAnchor->next != NULL) + return SML_ERR_XLT_INVAL_SYNCML_DOC; + } + + rc = buildPCData(pDecoder, (VoidPtr_t)&pAnchor->next); + + if (rc != SML_ERR_OK && ppElem == NULL) { + smlFreeMetinfAnchor(pAnchor); + return rc; + } + + if (ppElem == NULL) { + *ppElem = pAnchor; + } + + return SML_ERR_OK; +} + Ret_t buildMetInfMemCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { XltDecScannerPtr_t pScanner; SmlMetInfMemPtr_t pMem; @@ -256,6 +287,11 @@ Ret_t buildMetInfMetInfCmd(XltDecoderPtr_t pDecoder, VoidPtr_t *ppElem) { rc = buildEmptyTag(pDecoder); // allow for instead of break; + /* Next without Anchor, Andris Pavenis 2012-05-29 */ + case TN_METINF_NEXT: + rc = buildMetinfNextCmd(pDecoder, (VoidPtr_t)&pMeta->anchor); + break; + default: rc = SML_DECODEERROR(SML_ERR_XLT_INVAL_SYNCML_DOC, pScanner, "buildMetInfMetInfCmd_2"); } -- cgit v1.2.3 From d66a06998d1f3aebe913ef5cd3a1bcc1278de897 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 29 Mar 2012 12:00:33 +0200 Subject: SyncCap: X-SYNTHESIS-RESTART -> 390001 (for Funambol) It turned out the 500 internal server error in the Funambol server was caused by the non-integer X-SYNTHESIS-RESTART string in the SyncCap. The server expects integers and throws a parser exception when sent something else. The easiest solution is to use a random number that (hopefully) no-one else uses. The "390" prefix was generated with a random number generator, "001" is the first extension. --- src/sysync/localengineds.cpp | 7 ++++++- src/sysync/remotedatastore.cpp | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index 31a30b0..ef07771 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -3772,9 +3772,14 @@ SmlDevInfSyncCapPtr_t TLocalEngineDS::newDevInfSyncCap(uInt32 aSyncCapMask) // Now add non-standard synccaps. // From the spec: "Other values can also be specified." // Values are PCDATA, so we can use plain strings. + // But the Funambol server expects integer numbers and + // throws a parser error when sent a string. So better + // stick to a semi-random number (hopefully no-one else + // is using it). + // // Corresponding code in TRemoteDataStore::setDatastoreDevInf(). if (canRestart()) { - synctypeP=newPCDataString("X-SYNTHESIS-RESTART"); + synctypeP=newPCDataString("390001"); addPCDataToList(synctypeP,&(synccapP->synctype)); } // return it diff --git a/src/sysync/remotedatastore.cpp b/src/sysync/remotedatastore.cpp index c449afa..7093719 100755 --- a/src/sysync/remotedatastore.cpp +++ b/src/sysync/remotedatastore.cpp @@ -261,7 +261,7 @@ bool TRemoteDataStore::setDatastoreDevInf( if (stlP->data) { const char *type = smlPCDataToCharP(stlP->data); PDEBUGPRINTFX(DBG_REMOTEINFO,("SyncType='%s'", type)); - if (!strcmp(type, "X-SYNTHESIS-RESTART")) { + if (!strcmp(type, "390001")) { fCanRestart = true; } } -- cgit v1.2.3 From e7d0394e5e1a59fd04f6b52a18920a3b34779c7d Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Wed, 2 May 2012 14:30:12 +0200 Subject: super datastore: implemented canRestart() A super datastore can restart if all of its sub-datastores can. --- src/sysync/superdatastore.cpp | 10 ++++++++++ src/sysync/superdatastore.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/sysync/superdatastore.cpp b/src/sysync/superdatastore.cpp index ab6706f..7e44070 100755 --- a/src/sysync/superdatastore.cpp +++ b/src/sysync/superdatastore.cpp @@ -250,6 +250,16 @@ TSuperDataStore::~TSuperDataStore() // Methods overriding TLocalEngineDS // ---------------------------------- +bool TSuperDataStore::canRestart() +{ + TSubDSLinkList::iterator pos; + for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) { + if (!pos->fDatastoreLinkP->canRestart()) { + return false; + } + } + return true; +} // obtain Sync Cap mask, must be lowest common mask of all subdatastores uInt32 TSuperDataStore::getSyncCapMask(void) diff --git a/src/sysync/superdatastore.h b/src/sysync/superdatastore.h index 8549a37..b89292a 100755 --- a/src/sysync/superdatastore.h +++ b/src/sysync/superdatastore.h @@ -166,6 +166,8 @@ public: // Abstracts of TLocalEngineDS protected: + // check if all subdatastores can restart + bool canRestart(); // obtain Sync Cap mask, must be lowest common mask of all subdatastores virtual uInt32 getSyncCapMask(void); // Internal events during sync for derived classes -- cgit v1.2.3 From c8b7d22ed24602f6ad3c669db566a6f0e6b650b9 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Wed, 2 May 2012 14:40:28 +0200 Subject: datastore + CTCap: added support for flags This extends the idea already used for "can restart" to arbitrary flags: additional sync modes in CTCap can be used to convey additional information about a datastore to the peer. This only works when using SyncML 1.2. The XML "syncmode" element of a sets a single flag. It can occur zero or more times. Each entry is treated as an opaque string by the engine. For compatibility with SyncML peers which expect sync modes to be integers, the string should be a valid integer. All sync modes received from a peer are stored in the TSyncDatastore when receiving a CTCap. They can be checked with the new SYNCMODESUPPORTED() script method. This method only works inside scripts which work with items, like a compare script. --- src/sysync/localengineds.cpp | 26 +++++++++++++++++++++++++- src/sysync/localengineds.h | 4 ++++ src/sysync/multifielditemtype.cpp | 24 ++++++++++++++++++++++++ src/sysync/remotedatastore.cpp | 1 + src/sysync/superdatastore.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/sysync/superdatastore.h | 3 +++ src/sysync/syncdatastore.h | 4 ++++ 7 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index ef07771..7f740d1 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -939,7 +939,13 @@ bool TLocalDSConfig::localStartElement(const char *aElementName, const char **aA expectBool(fReadOnly); else if (strucmp(aElementName,"canrestart")==0) expectBool(fCanRestart); - else if (strucmp(aElementName,"reportupdates")==0) + else if (strucmp(aElementName,"syncmode")==0) { + if (!fSyncModeBuffer.empty()) { + fSyncModes.insert(fSyncModeBuffer); + fSyncModeBuffer.clear(); + } + expectString(fSyncModeBuffer); + } else if (strucmp(aElementName,"reportupdates")==0) expectBool(fReportUpdates); else if (strucmp(aElementName,"deletewins")==0) expectBool(fDeleteWins); @@ -1012,6 +1018,11 @@ bool TLocalDSConfig::localStartElement(const char *aElementName, const char **aA // resolve void TLocalDSConfig::localResolve(bool aLastPass) { + if (!fSyncModeBuffer.empty()) { + fSyncModes.insert(fSyncModeBuffer); + fSyncModeBuffer.clear(); + } + if (aLastPass) { #ifdef SCRIPT_SUPPORT TScriptContext *sccP = NULL; @@ -1162,6 +1173,7 @@ void TLocalEngineDS::InternalResetDataStore(void) fReadOnly=false; fReportUpdates=fDSConfigP->fReportUpdates; // update reporting according to what is configured fCanRestart=fDSConfigP->fCanRestart; + fSyncModes=fDSConfigP->fSyncModes; fServerAlerted=false; fResuming=false; #ifdef SUPERDATASTORES @@ -3782,6 +3794,18 @@ SmlDevInfSyncCapPtr_t TLocalEngineDS::newDevInfSyncCap(uInt32 aSyncCapMask) synctypeP=newPCDataString("390001"); addPCDataToList(synctypeP,&(synccapP->synctype)); } + + // Finally add non-standard synccaps that are outside of the + // engine's control. + set modes; + getSyncModes(modes); + for (set::const_iterator it = modes.begin(); + it != modes.end(); + ++it) { + synctypeP=newPCDataString(*it); + addPCDataToList(synctypeP,&(synccapP->synctype)); + } + // return it return synccapP; } // TLocalEngineDS::newDevInfSyncCap diff --git a/src/sysync/localengineds.h b/src/sysync/localengineds.h index dd0826b..8a2b748 100755 --- a/src/sysync/localengineds.h +++ b/src/sysync/localengineds.h @@ -140,6 +140,10 @@ public: // - options bool fReadOnly; // if set, datastore will not write any data (only maps) to local DB (but fake successful status to remote) bool fCanRestart; // if set, then the datastore is able to participate in multiple sync sessions; in other words after a successful read/write cycle it is possible to restart at the reading phase + set fSyncModes; // all additional, non-standard sync modes +#ifndef HARDCODED_CONFIG + string fSyncModeBuffer; // only used during XML config parsing +#endif bool fReportUpdates; // if set(normal case), updates of server items will be sent to client (can be set to false for example for emails) bool fResendFailing; // if set, items that receive a failure status from the remote will be resent in the next session (if DS 1.2 suspend marks supported by the DB) bool fDeleteWins; // if set, in a replace/delete conflict the delete wins (also see DELETEWINS()) diff --git a/src/sysync/multifielditemtype.cpp b/src/sysync/multifielditemtype.cpp index aa7cbb1..02d629f 100755 --- a/src/sysync/multifielditemtype.cpp +++ b/src/sysync/multifielditemtype.cpp @@ -69,6 +69,29 @@ void TMultiFieldTypeConfig::clear(void) class TMFTypeFuncs { public: + // integer SYNCMODESUPPORTED(string mode) + // + // True if the remote datastore is known to support the specific mode + // (usually because it sent SyncCap including the mode), false if unknown + // or not supported. + // + // Only works inside scripts which work on remote items. + static void func_SyncModeSupported(TItemField *&aTermP, TScriptContext *aFuncContextP) + { + bool supported = false; + TMultiFieldItemType *mfitP = static_cast(aFuncContextP->getCallerContext()); + if (mfitP->fFirstItemP && + mfitP->fFirstItemP->getRemoteItemType()) { + TSyncDataStore *related = mfitP->fFirstItemP->getRemoteItemType()->getRelatedDatastore(); + if (related) { + std::string mode; + aFuncContextP->getLocalVar(0)->getAsString(mode); + supported = related->syncModeSupported(mode); + } + } + aTermP->setAsInteger(supported); + }; // func_SyncModeSupported + // void SETFILTERALL(integer all) // sets if all records in the syncset need to checked against filter or only those that are new or changed static void func_SetFilterAll(TItemField *&aTermP, TScriptContext *aFuncContextP) @@ -321,6 +344,7 @@ const uInt8 param_IntArg[] = { VAL(fty_integer) }; // builtin functions for datastore-context table const TBuiltInFuncDef DataTypeFuncDefs[] = { + { "SYNCMODESUPPORTED", TMFTypeFuncs::func_SyncModeSupported, fty_integer, 1, param_StrArg }, { "SETFILTERALL", TMFTypeFuncs::func_SetFilterAll, fty_none, 1, param_IntArg }, #ifdef SYSYNC_TARGET_OPTIONS { "SIZELIMIT", TMFTypeFuncs::func_Limit, fty_integer, 0, NULL }, diff --git a/src/sysync/remotedatastore.cpp b/src/sysync/remotedatastore.cpp index 7093719..e7c4b08 100755 --- a/src/sysync/remotedatastore.cpp +++ b/src/sysync/remotedatastore.cpp @@ -261,6 +261,7 @@ bool TRemoteDataStore::setDatastoreDevInf( if (stlP->data) { const char *type = smlPCDataToCharP(stlP->data); PDEBUGPRINTFX(DBG_REMOTEINFO,("SyncType='%s'", type)); + fSyncModes.insert(type); if (!strcmp(type, "390001")) { fCanRestart = true; } diff --git a/src/sysync/superdatastore.cpp b/src/sysync/superdatastore.cpp index 7e44070..79035c9 100755 --- a/src/sysync/superdatastore.cpp +++ b/src/sysync/superdatastore.cpp @@ -273,6 +273,43 @@ uInt32 TSuperDataStore::getSyncCapMask(void) return capmask; } // TSuperDataStore::getSyncCapMask +bool TSuperDataStore::syncModeSupported(const std::string &mode) +{ + TSubDSLinkList::iterator pos; + for (pos=fSubDSLinks.begin();pos!=fSubDSLinks.end();pos++) { + if (!pos->fDatastoreLinkP->syncModeSupported(mode)) { + return false; + } + } + return true; +} + +void TSuperDataStore::getSyncModes(set &modes) +{ + // Initialize with content from first subdatastore. + TSubDSLinkList::iterator pos = fSubDSLinks.begin(); + if (pos!=fSubDSLinks.end()) { + pos->fDatastoreLinkP->getSyncModes(modes); + ++pos; + } + // Remove any mode not found in any of the other subdatastores. + while (pos!=fSubDSLinks.end() && !modes.empty()) { + set b; + pos->fDatastoreLinkP->getSyncModes(b); + set::iterator it = modes.begin(); + while (it != modes.end()) { + if (b.find(*it) != b.end()) { + ++it; + } else { + set::iterator del = it; + ++it; + modes.erase(del); + } + } + pos++; + } +} + // process Sync alert from remote party: check if alert code is supported, // check if slow sync is needed due to anchor mismatch diff --git a/src/sysync/superdatastore.h b/src/sysync/superdatastore.h index b89292a..62a8789 100755 --- a/src/sysync/superdatastore.h +++ b/src/sysync/superdatastore.h @@ -170,6 +170,9 @@ protected: bool canRestart(); // obtain Sync Cap mask, must be lowest common mask of all subdatastores virtual uInt32 getSyncCapMask(void); + // intersection of all sync mode sets in the subdatastores + virtual bool syncModeSupported(const std::string &mode); + virtual void getSyncModes(set &modes); // Internal events during sync for derived classes // Note: local DB authorisation must be established already before calling these // - prepares for Sync with this datastore diff --git a/src/sysync/syncdatastore.h b/src/sysync/syncdatastore.h index d3336ad..cb6a0fa 100755 --- a/src/sysync/syncdatastore.h +++ b/src/sysync/syncdatastore.h @@ -16,6 +16,7 @@ #define SyncDataStore_H #include "syncitemtype.h" +#include namespace sysync { @@ -78,6 +79,8 @@ public: sInt64 getFreeID(void) { return fFreeID; }; sInt64 getMaxID(void) { return fMaxID; }; bool canRestart() { return fCanRestart; } + virtual bool syncModeSupported(const std::string &mode) { return fSyncModes.find(mode) != fSyncModes.end(); } + virtual void getSyncModes(set &modes) { modes = fSyncModes; } // - session TSyncSession *getSession(void) { return fSessionP; }; // description structure of datastore (NULL if not available) @@ -118,6 +121,7 @@ protected: sInt64 fMaxID; // maximum number of ID sInt64 fFreeID; // free IDs bool fCanRestart; // if set, then the datastore is able to participate in multiple sync sessions; in other words after a successful read/write cycle it is possible to restart at the reading phase + set fSyncModes; // all supported sync modes that we know about (empty unless SyncCap was parsed) public: // Type of items in this datastore (read-only, can be used by multiple Datastores simultaneously) // - receiving types (also used as default item type) -- cgit v1.2.3 From 818ab26496dd97b4ba1e38b79f329b4ef232168e Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Wed, 2 May 2012 14:49:17 +0200 Subject: autotools: bumped minor version Bumped because of the "syncmode" + SYNCMODESUPPORTED() extension. --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 390fada..fb6b575 100644 --- a/configure.in +++ b/configure.in @@ -4,7 +4,7 @@ AC_INIT(src/sysync/engineinterface.cpp) AC_CONFIG_MACRO_DIR([m4]) # four digit upstream version, one additional digit for # Linux/SyncEvolution specific extensions: -AM_INIT_AUTOMAKE(synthesis, 3.4.0.16.6) +AM_INIT_AUTOMAKE(synthesis, 3.4.0.16.7) AM_CONFIG_HEADER(config.h) AC_LIBTOOL_DLOPEN -- cgit v1.2.3 From 7074e330378cb30968d63ea1fcefec73a68d995d Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 3 May 2012 09:38:56 +0200 Subject: script context: fixed out-of-bounds memory access TScriptContext::initParse() gets called for all scripts, even if they are empty. In that case the code read past the end of the string. Now empty scripts are treated as special case where TScriptContext member variables are cleared without reading from the script buffer. This bug was found in valgrind on x86, but not amd64. Didn't cause problems in practice. Not sure why it didn't show up earlier - perhaps something in the allocation of std::string changed during a recent libstdc++ update. --- src/sysync/scriptcontext.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sysync/scriptcontext.cpp b/src/sysync/scriptcontext.cpp index 87cc4d5..88b29ea 100755 --- a/src/sysync/scriptcontext.cpp +++ b/src/sysync/scriptcontext.cpp @@ -3007,9 +3007,14 @@ void TScriptContext::initParse(const string &aTScript, bool aExecuting) ep=bp+aTScript.size(); // end of script p=bp; // cursor, start at beginning np=NULL; // no next token yet + line=0; // no line yet linesource=NULL; // no source yet scriptname=NULL; // no name yet inComment=false; // not in comment yet + if (p == ep) + // empty script + return; + // try to get start line if (ep>bp && *p!=TK_SOURCELINE) SYSYNC_THROW(TScriptErrorException(DEBUGTEXT("Script does not start with TK_SOURCELINE","scri2"),line)); -- cgit v1.2.3 From eca00add21db86cc0c816759effe2fcdb5a1a4e1 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 22 May 2012 08:36:37 +0000 Subject: engine: fixed memory leaks The merged item during local conflict handling wasn't freed. When reading an item fails, the caller gets an error *and* an item, then ignores the item pointer. Always free the already allocated item before returning an error. --- src/sysync/binfileimplds.cpp | 1 + src/sysync/customimplds.cpp | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sysync/binfileimplds.cpp b/src/sysync/binfileimplds.cpp index 77844fd..321b7cb 100755 --- a/src/sysync/binfileimplds.cpp +++ b/src/sysync/binfileimplds.cpp @@ -2223,6 +2223,7 @@ error: PDEBUGPRINTFX(DBG_ERROR,("Database Error --> SyncML status %ld%s",(long)statuscode,lastDBErrorText().c_str())); //aStatusCommand.addItemString(lastDBErrorDbgText().c_str()); done: + delete augmentedItemP; aStatusCommand.setStatusCode(statuscode); return ok; } // TBinfileImplDS::implProcessItem diff --git a/src/sysync/customimplds.cpp b/src/sysync/customimplds.cpp index a41365e..4bd7197 100755 --- a/src/sysync/customimplds.cpp +++ b/src/sysync/customimplds.cpp @@ -3311,8 +3311,11 @@ localstatus TCustomImplDS::getItemFromSyncSetItem(TSyncSetItem *aSyncSetItemP, T aItemP->setSyncOp(sop_replace); // Now fetch item (read phase) localstatus sta = apiFetchItem(*((TMultiFieldItem *)aItemP),true,aSyncSetItemP); - if (sta!=LOCERR_OK) - return sta; // error + if (sta!=LOCERR_OK) { + delete aItemP; + aItemP = NULL; + } + return sta; } // ok return LOCERR_OK; @@ -3476,7 +3479,12 @@ localstatus TCustomImplDS::getItemByID(localid_t aLocalID, TSyncItem *&aItemP) // - set default operation aItemP->setSyncOp(sop_replace); // - now fetch directly from DB - return apiFetchItem(*((TMultiFieldItem *)aItemP),true,NULL); + localstatus sta = apiFetchItem(*((TMultiFieldItem *)aItemP),true,NULL); + if (sta!=LOCERR_OK) { + delete aItemP; + aItemP = NULL; + } + return sta; } else { // return sync item from syncset item (fetches data now if not fetched before) -- cgit v1.2.3 From a22ad8f9a7af0f544e4e894340a2cfe9de8b02ed Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 22 May 2012 08:38:12 +0000 Subject: debug logger: avoid deadlock when opening log file fails The mutex was locked before trying to open the file and not freed before returning after a failure to open the file. The next attempt to log then got stuck in the mutex lock, because the mutex is not recursive. Probably only relevant in "close to flush" mode. --- src/sysync/debuglogger.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sysync/debuglogger.cpp b/src/sysync/debuglogger.cpp index c0cdecf..413a345 100755 --- a/src/sysync/debuglogger.cpp +++ b/src/sysync/debuglogger.cpp @@ -368,6 +368,8 @@ void TStdFileDbgOut::putLine(cAppCharP aLine, bool aForceFlush) // we need to open the file for append first lockMutex(mutex); fFile=fopen(fFileName.c_str(),"a"); + if (!fFile) + unlockMutex(mutex); } if (fFile) { // now output -- cgit v1.2.3 From 902e577c47b7f539da65fb89b2fcffae26f1ca8d Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 22 May 2012 08:42:46 +0000 Subject: SyncML server: avoid Replace command without IDs during resumed sync The code which removes the local ID triggered during a resumed sync in which a Replace (as substitude for Add) was pending for resend. In that case the local ID must not be removed, because otherwise the client gets an item without any ID, which it cannot process. --- src/sysync/localengineds.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index 7f740d1..09ad433 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -4677,8 +4677,27 @@ TSyncOpCommand *TLocalEngineDS::newSyncOpCommand( if (syncop==sop_add || syncop==sop_wants_add) aSyncItemP->clearRemoteID(); // no remote ID else { - if (!fDSConfigP->fAlwaysSendLocalID) { - // only if localID may not be included in all syncops + if (!fDSConfigP->fAlwaysSendLocalID && + aSyncItemP->hasRemoteID()) { + // only if localID may not be included in all syncops, + // and not if the item has no remote ID yet + // + // The second case had to be added to solve an issue + // during suspended syncs: + // - server tries to add a new item and uses the Replace op for it + // - pending Replace is added to map + // - next sync resends the Replace, but with empty IDs and thus + // cannot be processed by client + // + // Log from such a failed sync: + // Item localID='328' already has map entry: remoteid='', mapflags=0x1, changed=0, deleted=0, added=0, markforresume=0, savedmark=1 + // Resuming and found marked-for-resume -> send replace + // ... + // Command 'Replace': is 1-th counted cmd, cmdsize(+tags needed to end msg)=371, available=130664 (maxfree=299132, freeaftersend=298761, notUsableBufferBytes()=168468) + // Item remoteID='', localID='', datasize=334 + // Replace: issued as (outgoing MsgID=2, CmdID=4), now queueing for status + // ... + // Status 404: Replace target not found on client -> silently ignore but remove map in server (item will be added in next session), aSyncItemP->clearLocalID(); // no local ID } } -- cgit v1.2.3 From c7fd8d8b0d0a4bf92a2c0f54fb1fc4876bde3603 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 22 May 2012 14:00:02 +0200 Subject: logging: avoid hard-coding log settings when is set Global settings were hard-coded and always used open/close (explicitly set for server, implied by dbgsubthread_linemix). For SyncEvolution this is overkill (never shares a log file between session, by setting a unique log path and not reusing engine instances). To let SyncEvolution use the desired settings, the overwriting of values is now conditional on : if and only if set, then session log settings as configured are used, without changes. If not set the behavior is as before. This is a slight semantic change. Apps which use now have to set the non-default openclose, mix and yes explicitly to get the traditional behavior. --- src/sysync/syncappbase.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sysync/syncappbase.cpp b/src/sysync/syncappbase.cpp index 81d9f77..e4cf283 100755 --- a/src/sysync/syncappbase.cpp +++ b/src/sysync/syncappbase.cpp @@ -729,7 +729,11 @@ void TDebugConfig::localResolve(bool aLastPass) // XML config - user options settings are parsed into fSessionDbgLoggerOptions // - by default, global logging has same options as configured for session... fGlobalDbgLoggerOptions = fSessionDbgLoggerOptions; - // ...but we have a few hard-coded things for global logging: + if (fLogSessionsToGlobal) { + // ... and these settings are used as they are, if session and global + // logging are identical... + } else { + // ...but when they are different, we have a few hard-coded things for global logging: #ifdef MULTITHREAD_PIPESERVER // - for pipe server, global logs should be per-thread fGlobalDbgLoggerOptions.fFlushMode=dbgflush_flush; // flush every log line @@ -745,6 +749,7 @@ void TDebugConfig::localResolve(bool aLastPass) fGlobalDbgLoggerOptions.fThreadIDForAll=true; // thread ID for each message #endif #endif + } #endif // initialize global debug logging options getSyncAppBase()->fAppLogger.setMask(fDebug); // set initial debug mask from config -- cgit v1.2.3 From 6f7da40dd30fd5eeb650bc9e102998e75a1ed833 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Tue, 29 May 2012 21:34:53 +0200 Subject: libsynthesis 3.4.0.42 --- src/global_options.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/global_options.h b/src/global_options.h index 5013de1..fb978eb 100644 --- a/src/global_options.h +++ b/src/global_options.h @@ -76,8 +76,8 @@ #endif #ifndef SYSYNC_BUILDNUMBER -#define SYSYNC_BUILDNUMBER 41 -#define SYSYNC_BUILDNUMBER_TXT "41" +#define SYSYNC_BUILDNUMBER 42 +#define SYSYNC_BUILDNUMBER_TXT "42" #endif -- cgit v1.2.3 From 1f1dfcf3c9bbacdbab1818fa644c967569d7b82b Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Thu, 7 Jun 2012 12:32:45 +0200 Subject: SyncML TK (and other global) debug output: added LF when using CONSOLEINFO_LIBC mode (introduced in 13ff1e4149 (logging + Linux: enable console output)) Unified with identification in logs ("sysync"). --- src/sysync/debuglogger.cpp | 4 ++-- src/sysync/sysync_debug.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sysync/debuglogger.cpp b/src/sysync/debuglogger.cpp index 413a345..f50f8ea 100755 --- a/src/sysync/debuglogger.cpp +++ b/src/sysync/debuglogger.cpp @@ -83,7 +83,7 @@ cAppCharP const DbgOutDefaultPrefixes[numDbgOutFormats] = { "\n" "", "\n" - "Synthesis SyncML Engine " SYSYNC_FULL_VERSION_STRING " Log\n" + "SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING " Log\n" "\n" "\n" - "

      Start of log - Synthesis SyncML Engine " SYSYNC_FULL_VERSION_STRING "

      \n
        \n" + "

        Start of log - SySync SyncML Engine " SYSYNC_FULL_VERSION_STRING "

        \n
          \n" }; cAppCharP const DbgOutDefaultSuffixes[numDbgOutFormats] = { diff --git a/src/sysync/sysync_debug.h b/src/sysync/sysync_debug.h index 61d4cdd..68dce5e 100755 --- a/src/sysync/sysync_debug.h +++ b/src/sysync/sysync_debug.h @@ -110,7 +110,7 @@ TDebugLogger *getDbgLogger(void); // Because a lot of libs log to stderr, include a unique prefix. // Assumes that all printf format strings are plain strings. #define CONSOLEPUTS(m) CONSOLE_PRINTF_VARARGS("%s", (m)) -#define CONSOLE_PRINTF_VARARGS(_m, _args...) fprintf(stderr, "SYSYNC " _m, ##_args) +#define CONSOLE_PRINTF_VARARGS(_m, _args...) fprintf(stderr, "SYSYNC " _m "\n", ##_args) #define CONSOLEPRINTF(m) CONSOLE_PRINTF_VARARGS m # else // CONSOLEINFO_LIBC #define CONSOLEPUTS(m) ConsolePuts(m) -- cgit v1.2.3 From e2f68c19f55732870582e9b0f05964f323b6f67c Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Fri, 8 Jun 2012 15:21:11 +0200 Subject: console printing: added line break to CONSOLEINFO_LIBC version A line break must be added by the implementation of the console printf. printf() wasn't doing that. Now a "\n" is appended to the format string. --- src/sysync/sysync_debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysync/sysync_debug.h b/src/sysync/sysync_debug.h index 68dce5e..3f5f278 100755 --- a/src/sysync/sysync_debug.h +++ b/src/sysync/sysync_debug.h @@ -110,7 +110,7 @@ TDebugLogger *getDbgLogger(void); // Because a lot of libs log to stderr, include a unique prefix. // Assumes that all printf format strings are plain strings. #define CONSOLEPUTS(m) CONSOLE_PRINTF_VARARGS("%s", (m)) -#define CONSOLE_PRINTF_VARARGS(_m, _args...) fprintf(stderr, "SYSYNC " _m "\n", ##_args) + #define CONSOLE_PRINTF_VARARGS(_m, _args...) SySync_ConsolePrintf(stderr, "SYSYNC " _m "\n", ##_args) #define CONSOLEPRINTF(m) CONSOLE_PRINTF_VARARGS m # else // CONSOLEINFO_LIBC #define CONSOLEPUTS(m) ConsolePuts(m) -- cgit v1.2.3 From dd565ef13870b2f2971e2d21843d9ce54c75082f Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Fri, 8 Jun 2012 15:24:54 +0200 Subject: console printing: allow app to override printf() If compiled with CONSOLEINFO_LIBC (as on Linux), printf() is used by default for console output. This output can be pretty verbose (for example, some SyncML servers trigger harmless error messages in the SyncML TK decoding routines). This commit allows the app to choose the actual implementation at runtime by setting a function pointer. An empty dummy function can be used to suppress the output, or some other function can use the app's own logging infrastructure. --- src/synthesis-linker.map | 1 + src/sysync/debuglogger.cpp | 6 +++++- src/sysync/sysync_debug.h | 11 +++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/synthesis-linker.map b/src/synthesis-linker.map index 2435aca..224d8b2 100644 --- a/src/synthesis-linker.map +++ b/src/synthesis-linker.map @@ -3,6 +3,7 @@ VER_1.0 { SySync_*ConnectEngine; SySync_*ConnectEngineS; SySync_*DisconnectEngine; + SySync_*ConsolePrintf; extern "C++" { sysync::DataConversion*; sysync::SySyncDebugPuts*; diff --git a/src/sysync/debuglogger.cpp b/src/sysync/debuglogger.cpp index f50f8ea..834d350 100755 --- a/src/sysync/debuglogger.cpp +++ b/src/sysync/debuglogger.cpp @@ -527,7 +527,11 @@ void TDebugLoggerBase::outputVia(TDebugLoggerBase *aDebugLoggerP) fOutputLoggerP = aDebugLoggerP; } // TDebugLoggerBase::outputVia - +#if defined(CONSOLEINFO) && defined(CONSOLEINFO_LIBC) +extern "C" { + int (*SySync_ConsolePrintf)(FILE *stream, const char *format, ...) = fprintf; +} +#endif // output formatted text void TDebugLoggerBase::DebugVPrintf(TDBG_LOCATION_PROTO uInt32 aDbgMask, cAppCharP aFormat, va_list aArgs) diff --git a/src/sysync/sysync_debug.h b/src/sysync/sysync_debug.h index 3f5f278..1f4b9b3 100755 --- a/src/sysync/sysync_debug.h +++ b/src/sysync/sysync_debug.h @@ -112,6 +112,17 @@ TDebugLogger *getDbgLogger(void); #define CONSOLEPUTS(m) CONSOLE_PRINTF_VARARGS("%s", (m)) #define CONSOLE_PRINTF_VARARGS(_m, _args...) SySync_ConsolePrintf(stderr, "SYSYNC " _m "\n", ##_args) #define CONSOLEPRINTF(m) CONSOLE_PRINTF_VARARGS m + + // default implementation invokes fprintf, can be set by app + // @param stream stderr, useful for invoking fprintf directly + // @param format guaranteed to start with "SYSYNC " (see above) +#ifdef __cplusplus + extern "C" { +#endif + extern int (*SySync_ConsolePrintf)(FILE *stream, const char *format, ...); +#ifdef __cplusplus + } +#endif # else // CONSOLEINFO_LIBC #define CONSOLEPUTS(m) ConsolePuts(m) #define CONSOLEPRINTF(m) ConsolePrintf m -- cgit v1.2.3 From af28f38de235cb5511c69e395afdc36073b4ed2e Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Tue, 19 Jun 2012 23:20:30 +0200 Subject: XCode upgrade version changed --- .../xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme index 4e2e45c..b1c96ea 100644 --- a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme +++ b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme @@ -1,5 +1,6 @@ Date: Fri, 27 Jul 2012 20:53:36 +0200 Subject: Delayed termination of sync session added - experimental for now --- src/sysync/syncagent.cpp | 15 +++++++++++---- src/sysync/syncagent.h | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index c84b7a6..5012355 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -3161,6 +3161,15 @@ TSyError TSyncAgent::ServerSessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aI // handle pre-processed step command according to current engine state switch (fServerEngineState) { + // Almost done state + case ses_almostdone: + // everything done, except for termination of session + // - do it now + TerminateSession(); + // - now done + fServerEngineState = ses_done; + // fall through to done state + // Done state case ses_done : // session done, nothing happens any more @@ -3337,12 +3346,10 @@ TSyError TSyncAgent::ServerGeneratingStep(uInt16 &aStepCmd, TEngineProgressInfo // no more data to send aStepCmd = STEPCMD_OK; // need one more step to finish } - // in any case, if done, all susequent steps will return STEPCMD_DONE + // in any case, if done, subsequent steps will terminate the session and return STEPCMD_DONE if (done) { - // Session is done - TerminateSession(); // subsequent steps will all return STEPCMD_DONE - fServerEngineState = ses_done; + fServerEngineState = ses_almostdone; } // request reset fRequestSize = 0; diff --git a/src/sysync/syncagent.h b/src/sysync/syncagent.h index 6a2b990..5403c1e 100755 --- a/src/sysync/syncagent.h +++ b/src/sysync/syncagent.h @@ -61,6 +61,7 @@ typedef enum { ses_processing, ///< ready to perform next STEPCMD_STEP to process SyncML messages ses_generating, ///< ready to perform next STEPCMD_STEP to generate SyncML messages ses_dataready, ///< data is ready to be sent, waiting for STEPCMD_SENTDATA + ses_almostdone, ///< session done, but termination delayed until next step ses_done, ///< session done numServerEngineStates } TServerEngineState; -- cgit v1.2.3 From 7a63217d7994190a7056bb6f1e5fd6012b0a4e60 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 10 Aug 2012 22:53:36 +0200 Subject: Engine Version 3.4.0.44 --- src/global_options.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/global_options.h b/src/global_options.h index fb978eb..80a1a99 100644 --- a/src/global_options.h +++ b/src/global_options.h @@ -76,8 +76,8 @@ #endif #ifndef SYSYNC_BUILDNUMBER -#define SYSYNC_BUILDNUMBER 42 -#define SYSYNC_BUILDNUMBER_TXT "42" +#define SYSYNC_BUILDNUMBER 44 +#define SYSYNC_BUILDNUMBER_TXT "44" #endif -- cgit v1.2.3 From c4e8d6dd5fe3d99d84966ec52a6379fa7f0f2ff1 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Thu, 23 Aug 2012 18:17:21 +0200 Subject: Cocoa wrapper: added conditionals to allow using in ARC code, silenced some LLVM analyzer warnings --- src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m b/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m index 7fcd54f..88a0f36 100644 --- a/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m +++ b/src/sysync_SDK/sdk_sources_cocoa/SyncEngine.m @@ -9,7 +9,7 @@ - (id)initWithCI:(UI_Call_In)aCI andSessionHandle:(SessionH)aSessionH; { - if ([super init]!=nil) { + if ((self = [super init])) { fCI = aCI; fSessionH = aSessionH; // also prepare tunnel wrapper @@ -32,7 +32,9 @@ } } // done + #if !__has_feature(objc_arc) [super dealloc]; + #endif } // dealloc @@ -87,7 +89,9 @@ SettingsKey *sessionKey = [self newOpenSessionKeyWithMode:0 err:&sta]; if (sta==LOCERR_OK) { [sessionKey setStringValueByName:"debugMsg" toValue:aMessage]; + #if !__has_feature(objc_arc) [sessionKey release]; + #endif } } @@ -98,7 +102,9 @@ SettingsKey *sessionKey = [self newOpenSessionKeyWithMode:0 err:&sta]; if (sta==LOCERR_OK) { [sessionKey setStringValueByName:"errorMsg" toValue:aMessage]; + #if !__has_feature(objc_arc) [sessionKey release]; + #endif } } @@ -295,7 +301,7 @@ // empty prefix if none specified if (aEntryPointPrefix==nil) aEntryPointPrefix = @""; // init superclass and DLL - if (self == [super init]) { + if ((self = [super init])) { ConnectEngine_Func ConnectEngine_Var = NULL; #ifdef SYSYNC_ENGINE_STATIC // static: just directly use the entry point. @@ -345,7 +351,9 @@ DBGNSLOG(@"SySync engine platform = '%@'\n", [engineInfo stringValueByName:"platform"]); DBGNSLOG(@"library product name = '%@'\n", [engineInfo stringValueByName:"name"]); DBGNSLOG(@"version comment = '%@'\n", [engineInfo stringValueByName:"comment"]); + #if !__has_feature(objc_arc) [engineInfo release]; + #endif } } } @@ -353,7 +361,9 @@ sta=LOCERR_OUTOFMEM; // probably... // kill or go if (sta!=LOCERR_OK) { + #if !__has_feature(objc_arc) [self dealloc]; + #endif return nil; } // ok @@ -395,7 +405,9 @@ fCI = NULL; DisconnectEngine_Var = NULL; // done + #if !__has_feature(objc_arc) [super dealloc]; + #endif } -- cgit v1.2.3 From 708a9e441b50e109923fafe0fe0defc99fc5206d Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Wed, 29 Aug 2012 10:51:20 +0200 Subject: engine: item counting corrected (before, items that could not be sent in the current message and were queued for the next message were not counted in the statistics and also not reported via pev_itemsent progress event) --- src/sysync/stdlogicds.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/sysync/stdlogicds.cpp b/src/sysync/stdlogicds.cpp index 1020e54..6a8d75d 100644 --- a/src/sysync/stdlogicds.cpp +++ b/src/sysync/stdlogicds.cpp @@ -880,15 +880,16 @@ bool TStdLogicDS::logicGenerateSyncCommandsAsServer( syncopcmdP=NULL; // possibly, we have a NULL command here (e.g. in case it could not be generated due to MaxObjSize restrictions) if (cmdP) { + // We pass the command to the issue mechanism - last chance to count is here. + // Note: the command might be queued and actually sent in a subsequent message. + fItemsSent++; // overall counter for statistics + itemcount++; // per message counter + DB_PROGRESS_EVENT(this,pev_itemsent,fItemsSent,getNumberOfChanges(),0); // send event (but no check for abort) + // now issue if (!fSessionP->issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) { alldone=false; // issue failed (no room in message), not finished so far - break; + break; // stop trying to issue more commands } - // count item sent - fItemsSent++; // overall counter for statistics - itemcount++; // per message counter - // send event (but no check for abort) - DB_PROGRESS_EVENT(this,pev_itemsent,fItemsSent,getNumberOfChanges(),0); } }; // while not aborted and not message full // we are not done until all aNextMessageCommands are also out @@ -1039,19 +1040,23 @@ bool TStdLogicDS::logicGenerateSyncCommandsAsClient( syncopcmdP=NULL; // possibly, we have a NULL command here (e.g. in case it could not be generated due to MaxObjSize restrictions) if (cmdP) { - if (!fSessionP->issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) { - alldone=false; // issue failed (no room in message), not finished so far - break; - } - // count item sent + // We pass the command to the issue mechanism - last chance to count is here. + // Note: the command might be queued and actually sent in a subsequent message. fItemsSent++; - // send event and check for abort #ifdef PROGRESS_EVENTS + // send progress event and check for abort if (!DB_PROGRESS_EVENT(this,pev_itemsent,fItemsSent,getNumberOfChanges(),0)) { implEndDataRead(); // terminate reading fSessionP->AbortSession(500,true,LOCERR_USERABORT); return false; // error } + #endif + // issue now + if (!fSessionP->issuePtr(cmdP,aNextMessageCommands,aInterruptedCommandP)) { + alldone=false; // issue failed (no room in message), not finished so far + break; + } + #ifdef PROGRESS_EVENTS // check for "soft" suspension if (!SESSION_PROGRESS_EVENT(fSessionP,pev_suspendcheck,NULL,0,0,0)) { fSessionP->SuspendSession(LOCERR_USERSUSPEND); -- cgit v1.2.3 From fe040854a0f23c4a118f7bda1c72c6ed58961ff3 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Wed, 6 Jun 2012 08:56:31 +0200 Subject: SyncCap: compatibility enhancement for Nokia phones It turns out that Nokia phones don't like SyncModes that they don't recognize. They abort a sync over Bluetooth/OBEX with a generic "Forbidden" error at the OBEX layer. This only occurs when the sync app requests the inclusion of non-standard SyncCaps ("can restart", custom flags). As a workaround, the extended SyncModes are sent less often now: - a client always sends them, based on the observation that servers handle that okay(ish - see Funambol workaround) - a server only sends them if the client has already sent some extensions itself This works as long as the Put with the client's DevInf is processed before the server's DevInf is generated in response to a Get; for libsynthesis<->libsynthesis syncs that is the case. A new dummy 390002 SyncMode is always sent, to trigger this behavior in the server also when the client has no other extensions. Perhaps this behavior should be made configurable and off by default, to avoid regressions in combinations which haven't been tested yet. --- src/sysync/localengineds.cpp | 43 +++++++++++++++++++++++++++++++------------ src/sysync/syncsession.cpp | 35 +++++++++++++++++++++++++++++++++++ src/sysync/syncsession.h | 2 ++ 3 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index 09ad433..ce3d034 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -3784,26 +3784,45 @@ SmlDevInfSyncCapPtr_t TLocalEngineDS::newDevInfSyncCap(uInt32 aSyncCapMask) // Now add non-standard synccaps. // From the spec: "Other values can also be specified." // Values are PCDATA, so we can use plain strings. + // // But the Funambol server expects integer numbers and // throws a parser error when sent a string. So better // stick to a semi-random number (hopefully no-one else // is using it). // + // Worse, Nokia phones cancel direct sync sessions with an + // OBEX error ("Forbidden") when non-standard sync modes + // are included in the SyncCap. As a workaround for that + // we use the following logic: + // - libsynthesis in a SyncML client will always send + // all the extended sync modes; with the Funambol + // workaround in place that works + // - libsynthesis in a SyncML server will only send the + // extended sync modes if the client has sent any + // extended sync modes itself; the 390002 mode is + // sent unconditionally for that purpose + // // Corresponding code in TRemoteDataStore::setDatastoreDevInf(). - if (canRestart()) { - synctypeP=newPCDataString("390001"); + // + if (!IS_SERVER || + fSessionP->receivedSyncModeExtensions()) { + if (canRestart()) { + synctypeP=newPCDataString("390001"); + addPCDataToList(synctypeP,&(synccapP->synctype)); + } + synctypeP=newPCDataString("390002"); addPCDataToList(synctypeP,&(synccapP->synctype)); - } - // Finally add non-standard synccaps that are outside of the - // engine's control. - set modes; - getSyncModes(modes); - for (set::const_iterator it = modes.begin(); - it != modes.end(); - ++it) { - synctypeP=newPCDataString(*it); - addPCDataToList(synctypeP,&(synccapP->synctype)); + // Finally add non-standard synccaps that are outside of the + // engine's control. + set modes; + getSyncModes(modes); + for (set::const_iterator it = modes.begin(); + it != modes.end(); + ++it) { + synctypeP=newPCDataString(*it); + addPCDataToList(synctypeP,&(synccapP->synctype)); + } } // return it diff --git a/src/sysync/syncsession.cpp b/src/sysync/syncsession.cpp index 074abce..f71a2a0 100644 --- a/src/sysync/syncsession.cpp +++ b/src/sysync/syncsession.cpp @@ -5890,6 +5890,41 @@ TLocalDSConfig *TSessionConfig::newDatastoreConfig(const char *aName, const char #endif // HARDCODED_CONFIG +bool TSyncSession::receivedSyncModeExtensions() +{ + TRemoteDataStorePContainer::iterator pos; + for (pos=fRemoteDataStores.begin(); pos!=fRemoteDataStores.end(); ++pos) { + set modes; + (*pos)->getSyncModes(modes); + set::const_iterator it; + for (it=modes.begin(); it!=modes.end(); ++it) { + const char *nptr = it->c_str(); + char *endptr; + if (!*nptr) { + // ignore empty mode + continue; + } + long mode = strtol(nptr, &endptr, 10); + // ignore trailing spaces + while (isspace(*endptr)) { + endptr++; + } + if (*endptr) { + // non-standard character => found extensions + return true; + } + + if (mode > 32) { + // Non-standed integer code! + // Choosing 32 is somewhat random, not all of those + // are really defined in the standard. + return true; + } + } + } + return false; +} + } // namespace sysync #endif // not SYNCSESSION_PART1_EXCLUDE diff --git a/src/sysync/syncsession.h b/src/sysync/syncsession.h index 6fa780b..fe882a3 100755 --- a/src/sysync/syncsession.h +++ b/src/sysync/syncsession.h @@ -728,6 +728,8 @@ protected: TLocalDataStorePContainer fLocalDataStores; // - list of remote (client-side) datastores TRemoteDataStorePContainer fRemoteDataStores; + bool receivedSyncModeExtensions(); // any of the remote datastores in fRemoteDataStores + // had custom sync modes // - list of local content types TSyncItemTypePContainer fLocalItemTypes; // - list of remote item types -- cgit v1.2.3 From f73780508571bc41d32fda2de698a68be8aabcf1 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Fri, 8 Jun 2012 16:05:22 +0200 Subject: sync client: made sync mode choice configurable libsynthesis has traditionally implemented "refresh-from-server" as "delete local data" plus "slow" sync. This is more compatible, because some servers did not support "refresh-from-server". But it has the downside that the server cannot know that the client won't send any data, and Funambol now only allows one slow sync before blocking the next one for a certain period of time. Probably this is done to prevent excessive resource usage by badly behaving clients. This commit introduces a new configure option called "yes/no". It goes into "" and defaults to "yes", the traditional behavior. --- src/sysync/localengineds.cpp | 3 ++- src/sysync/syncagent.cpp | 3 +++ src/sysync/syncagent.h | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index ce3d034..15a8d48 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -3480,7 +3480,8 @@ localstatus TLocalEngineDS::engGenerateClientSyncAlert( fNextLocalAnchor.c_str() )); // create appropriate initial alert command - uInt16 alertCode = getSyncStateAlertCode(fServerAlerted,true); + TAgentConfig *configP = static_cast(static_cast(getSession())->getRootConfig()->fAgentConfigP); + uInt16 alertCode = getSyncStateAlertCode(fServerAlerted, configP->fPreferSlowSync); // check for resume if (fResuming) { // check if what we resume is same as what we wanted to do diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index 5012355..63842af 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -400,6 +400,7 @@ void TAgentConfig::clear(void) fAssumedServerAuth=auth_none; // start with no auth fAssumedServerAuthEnc=fmt_chr; // start with char encoding fAssumedNonce.erase(); // start with no nonce + fPreferSlowSync=true; // auth retry options (mainly for stupid servers like SCTS) #ifdef SCTS_COMPATIBILITY_HACKS fNewSessionForAuthRetry=false; @@ -474,6 +475,8 @@ bool TAgentConfig::localStartElement(const char *aElementName, const char **aAtt expectEnum(sizeof(fAssumedServerAuthEnc),&fAssumedServerAuthEnc,encodingFmtSyncMLNames,numFmtTypes); else if (strucmp(aElementName,"defaultauthnonce")==0) expectString(fAssumedNonce); + else if (strucmp(aElementName,"preferslowsync")==0) + expectBool(fPreferSlowSync); else if (strucmp(aElementName,"newsessionforretry")==0) expectBool(fNewSessionForAuthRetry); else if (strucmp(aElementName,"originaluriforretry")==0) diff --git a/src/sysync/syncagent.h b/src/sysync/syncagent.h index 5403c1e..898b4e7 100755 --- a/src/sysync/syncagent.h +++ b/src/sysync/syncagent.h @@ -154,6 +154,12 @@ public: TAuthTypes fAssumedServerAuth; TFmtTypes fAssumedServerAuthEnc; // start with char encoding string fAssumedNonce; // start with no nonce + bool fPreferSlowSync; // Ask server for slow sync instead of refresh-from-server: + // both have the same effect, but have different pros and cons. + // Defaults to true, the traditional behavior of libsynthesis. + // Some old servers only support slow sync. Some new ones + // work better with refresh, because they throttle clients + // which do slow syncs excessively. // auth retry options (mainly for stupid servers like SCTS) bool fNewSessionForAuthRetry; // restart session for auth retries bool fNoRespURIForAuthRetry; // send retry to original URI for auth retries -- cgit v1.2.3 From 4d458ac4ee4e5705c04e2edd2c58d842bdcc8213 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Tue, 19 Jun 2012 11:35:29 +0200 Subject: CtCap: Funambol workaround Reduce number of non-standard sync modes in SyncCap by one if possible, to stay below what looks like a hard-coded limit for the number of SyncCap entries in the Funambol OneMedia server. --- src/sysync/localengineds.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index 15a8d48..095b327 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -3807,13 +3807,12 @@ SmlDevInfSyncCapPtr_t TLocalEngineDS::newDevInfSyncCap(uInt32 aSyncCapMask) // if (!IS_SERVER || fSessionP->receivedSyncModeExtensions()) { + bool extended=false; if (canRestart()) { synctypeP=newPCDataString("390001"); addPCDataToList(synctypeP,&(synccapP->synctype)); + extended=true; } - synctypeP=newPCDataString("390002"); - addPCDataToList(synctypeP,&(synccapP->synctype)); - // Finally add non-standard synccaps that are outside of the // engine's control. set modes; @@ -3823,6 +3822,22 @@ SmlDevInfSyncCapPtr_t TLocalEngineDS::newDevInfSyncCap(uInt32 aSyncCapMask) ++it) { synctypeP=newPCDataString(*it); addPCDataToList(synctypeP,&(synccapP->synctype)); + extended=true; + } + + // Add fake mode to signal peer that we support extensions. + // Otherwise a server won't send them, to avoid breaking + // client's (like Nokia phones) which don't. + // + // Don't send the fake mode unnecessarily (= when some other + // non-standard modes where already added), because Funambol seems + // to have a hard-coded limit of 9 entries in the and + // complains with a 513 internal server error (when using WBXML) + // or a 'Expected "CTCap" end tag, found "CTType" end tag' (when + // using XML). + if (!extended) { + synctypeP=newPCDataString("390002"); + addPCDataToList(synctypeP,&(synccapP->synctype)); } } -- cgit v1.2.3 From d04818f5979d9359b4b5b3037c4a70fdacfc867f Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 12 Jul 2012 15:47:18 +0000 Subject: autotools: must link against libpthreads Some libpthreads functions are called directly, and therefore libsynthesis should be linked to it directly. Otherwise it depends on other libs or executables loading that library, which can fail. --- src/Makefile.am.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am.in b/src/Makefile.am.in index faa3872..df2f34a 100644 --- a/src/Makefile.am.in +++ b/src/Makefile.am.in @@ -78,7 +78,7 @@ libsynthesis_la_CFLAGS = \ $(LIBICAL_CFLAGS) \ $(LIBECAL_CFLAGS) libsynthesis_la_CXXFLAGS = $(libsynthesis_la_CFLAGS) -libsynthesis_la_LIBADD = $(PCRE_LIBS) $(SQLITE3_LIBS) $(XMLPARSE_LIBS) $(LIBICAL_LIBS) $(LIBECAL_LIBS) libsmltk.la -lz -ldl +libsynthesis_la_LIBADD = $(PCRE_LIBS) $(SQLITE3_LIBS) $(XMLPARSE_LIBS) $(LIBICAL_LIBS) $(LIBECAL_LIBS) libsmltk.la -lz -ldl -lpthread libsynthesis_la_LDFLAGS = -version-info $(ENGINE_CURRENT):$(ENGINE_REVISION):$(ENGINE_AGE) \ -Wl,--version-script=$(srcdir)/synthesis-linker.map libsynthesis_la_DEPENDENCIES = $(srcdir)/synthesis-linker.map libsmltk.la -- cgit v1.2.3 From b79d1f138749007a76e403ba98ba05292b3f54f4 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 16 Aug 2012 18:59:22 +0200 Subject: engine: allow text->VJOURNAL conversion SyncEvolution uses the Synthesis engine to convert a plain text memo to iCalendar 2.0 in the local storage. This is done like this: - define a text datatype which uses the same field list as calendar data: 1 false true SUMMARY 0 false true text/plain 1.0 ... same for text/plain 1.1 - The "calendar" profile was extended to support VJOURNAL. - The incoming script of "journaltext10" sets ISEVENT in the "calendar" field list so that the data is marked as "journal". - define a store which supports iCalendar and plain text: The problem with that is: 1. A text/plain item from the peer is parsed and turned into an item with TTextItemType (corresponds to "journaltext10"). 2. Before writing into the local storage, MAKETEXTWITHPROFILE("vCalendar", 2) is called. 3. For the value of VERSION, that method calls TTTextItemType::getTypeVers() = TSyncItemType::getTypeVers(), which returns the configured 1.0 (from "journaltext10"). 4. The backend fails to parse the generated item because it only supports iCalendar 2.0 and is given something which has VERSION:1.0 (and iCalendar 2.0 encoding rules). The "fix" in this patch is to extend TTextItemType specifically for the case where getTypeVers() is called with a non-zero mode. I checked the source, that should only happen when used in combination with a MIME profile, something which only SyncEvolution does. I had to hard-code the support for iCalendar 2.0 (aMode = 2) and vCalendar 1.0 (aMode = 1), something that IMHO does not really belong into TTextItemType. --- src/sysync/textitemtype.cpp | 17 +++++++++++++++++ src/sysync/textitemtype.h | 1 + 2 files changed, 18 insertions(+) diff --git a/src/sysync/textitemtype.cpp b/src/sysync/textitemtype.cpp index 09aeb69..3ba4fa9 100755 --- a/src/sysync/textitemtype.cpp +++ b/src/sysync/textitemtype.cpp @@ -157,6 +157,23 @@ TTextItemType::~TTextItemType() delete fProfileHandlerP; } // TTextItemType::~TTextItemType +cAppCharP TTextItemType::getTypeVers(sInt32 aMode) +{ + // This function is called by TMimeDirProfile when generating the + // VERSION property. Allow converting a plain text item to + // iCalendar 2.0 (aMode = 2) or vCalendar 1.0 (aMode = 1) by + // overriding the base version that was configured for the + // underlying text item type. + switch (aMode) { + default: + return inherited::getTypeVers(aMode); + case 1: + return "1.0"; + case 2: + return "2.0"; + } +} // TTextItemType::getTypeVers + #ifdef OBJECT_FILTERING diff --git a/src/sysync/textitemtype.h b/src/sysync/textitemtype.h index 4240f4b..5dac68a 100755 --- a/src/sysync/textitemtype.h +++ b/src/sysync/textitemtype.h @@ -61,6 +61,7 @@ public: // destructor virtual ~TTextItemType(); // access to type + virtual cAppCharP getTypeVers(sInt32 aMode=0); virtual uInt16 getTypeID(void) const { return ity_text; }; virtual bool isBasedOn(uInt16 aItemTypeID) const { return aItemTypeID==ity_text ? true : TMultiFieldItemType::isBasedOn(aItemTypeID); }; // differentiation between implemented and just descriptive TSyncTypeItems -- cgit v1.2.3 From 9b5e2531a6fe7a8cbbb29c584daf9acd2515f74e Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Mon, 3 Sep 2012 12:49:00 +0000 Subject: SyncML TK: don't read past end of buffer The code which dumps the buffer after a parsing error had no way of obtaining the buffer size and thus read past the end of the buffer, as seen in SyncEvolution nightly testing under valgrind. Must provide the buffer length together with a pointer. The extra information is optional, so only the places which needed to know the length were updated. --- src/syncml_tk/src/sml/xlt/all/xltdec.c | 19 ++++++++++++------- src/syncml_tk/src/sml/xlt/all/xltdeccom.h | 7 ++++++- src/syncml_tk/src/sml/xlt/all/xltdecwbxml.c | 8 +++++--- src/syncml_tk/src/sml/xlt/all/xltdecxml.c | 8 +++++--- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/syncml_tk/src/sml/xlt/all/xltdec.c b/src/syncml_tk/src/sml/xlt/all/xltdec.c index a07f24c..d9aa295 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltdec.c +++ b/src/syncml_tk/src/sml/xlt/all/xltdec.c @@ -246,10 +246,15 @@ Ret_t show_decode_error(Ret_t aRc, XltDecScannerPtr_t aScanner, char *aRoutineNa SMLERRPRINTFX(DBG_ERROR,("%s: smlErr 0x%hX (%s) while parsing",aRoutineName,aRc,smlErrorText(aRc))); // Show details if (aScanner && aScanner->curtok) { + Long_t currentBytesShow = numBytesShow; + Long_t remaining; + // show what token we are parsing show_token(aScanner->curtok,DBG_ERROR); - // show hex of 32 bytes after scan position - b = aScanner->getPos(aScanner); + // show hex of at most 32 bytes after scan position + b = aScanner->getPos(aScanner, &remaining); + if (remaining < currentBytesShow) + currentBytesShow = remaining; SMLERRPRINTFX(DBG_ERROR,( "- Tag start at 0x%lX, scanner pos at 0x%lX%s, data:", (unsigned long)aScanner->curtok->start, @@ -258,16 +263,16 @@ Ret_t show_decode_error(Ret_t aRc, XltDecScannerPtr_t aScanner, char *aRoutineNa )); if (b!=NULL) { p=hexshow; - for (i=0; i>4); *p++ = NibbleToHexDigit(*b++); *p++ = ' '; } *p=0; - b-=numBytesShow; // rewind + b-=currentBytesShow; // rewind SMLERRPRINTFX(DBG_ERROR,("%s", hexshow)); p=hexshow; - for (i=0; i=0x20) && (*b<0x7F) ? *b : '_'; b++; } @@ -470,7 +475,7 @@ xltDecInit(const SmlEncoding_t enc, return rc; } - *ppBufPos = pDecoder->scanner->getPos(pDecoder->scanner); + *ppBufPos = pDecoder->scanner->getPos(pDecoder->scanner, NULL); *ppDecoder = (XltDecoderPtr_t)pDecoder; @@ -590,7 +595,7 @@ xltDecNext(XltDecoderPtr_t pDecoder, } } - *ppBufPos = pScanner->getPos(pScanner); + *ppBufPos = pScanner->getPos(pScanner, NULL); return SML_ERR_OK; } diff --git a/src/syncml_tk/src/sml/xlt/all/xltdeccom.h b/src/syncml_tk/src/sml/xlt/all/xltdeccom.h index 33cf526..edf9209 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltdeccom.h +++ b/src/syncml_tk/src/sml/xlt/all/xltdeccom.h @@ -143,8 +143,13 @@ struct XltDecScanner_s /** * Get the current position of the scanner within the working buffer. + * + * @param pScanner (IN/OUT) + * the scanner + * @param remaining (OUT) + * number of valid bytes after current position, optional (NULL acceptable) */ - MemPtr_t (*getPos)(XltDecScannerPtr_t pScanner); + MemPtr_t (*getPos)(XltDecScannerPtr_t pScanner, Long_t *remaining); /* public attributes */ diff --git a/src/syncml_tk/src/sml/xlt/all/xltdecwbxml.c b/src/syncml_tk/src/sml/xlt/all/xltdecwbxml.c index 7826022..43865a0 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltdecwbxml.c +++ b/src/syncml_tk/src/sml/xlt/all/xltdecwbxml.c @@ -124,7 +124,7 @@ struct wbxmlScannerPriv_s Ret_t (*destroy)(XltDecScannerPtr_t); Ret_t (*pushTok)(XltDecScannerPtr_t); void (*setBuf)(XltDecScannerPtr_t pScanner, const MemPtr_t pBufStart, const MemPtr_t pBufEnd); - MemPtr_t (*getPos)(XltDecScannerPtr_t pScanner); + MemPtr_t (*getPos)(XltDecScannerPtr_t pScanner, Long_t *remaining); /* public attributes */ XltDecTokenPtr_t curtok; /**< current token */ @@ -168,7 +168,7 @@ static Ret_t _destroy(XltDecScannerPtr_t); static Ret_t _nextTok(XltDecScannerPtr_t); static Ret_t _pushTok(XltDecScannerPtr_t); static void _setBuf(XltDecScannerPtr_t, const MemPtr_t, const MemPtr_t); -static MemPtr_t _getPos(XltDecScannerPtr_t); +static MemPtr_t _getPos(XltDecScannerPtr_t, Long_t *remaining); /** * Advance the current position pointer after checking whether the end of @@ -398,8 +398,10 @@ _setBuf(XltDecScannerPtr_t pScanner, const MemPtr_t pBufStart, } static MemPtr_t -_getPos(XltDecScannerPtr_t pScanner) +_getPos(XltDecScannerPtr_t pScanner, Long_t *remaining) { + if (remaining) + *remaining = ((wbxmlScannerPrivPtr_t)pScanner)->bufend - ((wbxmlScannerPrivPtr_t)pScanner)->pos; return ((wbxmlScannerPrivPtr_t)pScanner)->pos; } diff --git a/src/syncml_tk/src/sml/xlt/all/xltdecxml.c b/src/syncml_tk/src/sml/xlt/all/xltdecxml.c index 6129c40..f523e27 100755 --- a/src/syncml_tk/src/sml/xlt/all/xltdecxml.c +++ b/src/syncml_tk/src/sml/xlt/all/xltdecxml.c @@ -71,7 +71,7 @@ struct xmlScannerPriv_s Ret_t (*destroy)(XltDecScannerPtr_t); Ret_t (*pushTok)(XltDecScannerPtr_t); void (*setBuf)(XltDecScannerPtr_t pScanner, const MemPtr_t pBufStart, const MemPtr_t pBufEnd); - MemPtr_t (*getPos)(XltDecScannerPtr_t pScanner); + MemPtr_t (*getPos)(XltDecScannerPtr_t pScanner, Long_t *remaining); XltDecTokenPtr_t curtok; /**< current token */ Long_t charset; /**< 0 */ @@ -100,7 +100,7 @@ static Ret_t _destroy(XltDecScannerPtr_t); static Ret_t _nextTok(XltDecScannerPtr_t); static Ret_t _pushTok(XltDecScannerPtr_t); static void _setBuf(XltDecScannerPtr_t, const MemPtr_t, const MemPtr_t); -static MemPtr_t _getPos(XltDecScannerPtr_t); +static MemPtr_t _getPos(XltDecScannerPtr_t, Long_t *remaining); /** * Advance the current position pointer after checking whether the end of @@ -311,8 +311,10 @@ _setBuf(XltDecScannerPtr_t pScanner, const MemPtr_t pBufStart, * Get the current position of the scanner within its working buffer. */ static MemPtr_t -_getPos(XltDecScannerPtr_t pScanner) +_getPos(XltDecScannerPtr_t pScanner, Long_t *remaining) { + if (remaining) + *remaining = ((xmlScannerPrivPtr_t)pScanner)->bufend - ((xmlScannerPrivPtr_t)pScanner)->pos; return ((xmlScannerPrivPtr_t)pScanner)->pos; } -- cgit v1.2.3 From a781b669388d28766809393096057b45b6aa02f6 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Mon, 3 Sep 2012 12:51:24 +0000 Subject: engine: updated logging of sync keys The additonal logging helped to understand that the sync keys provided by a datastore are not getting updated during a one-way sync. --- src/sysync/binfileimplds.cpp | 3 ++- src/sysync/customimplds.cpp | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sysync/binfileimplds.cpp b/src/sysync/binfileimplds.cpp index 321b7cb..8708215 100755 --- a/src/sysync/binfileimplds.cpp +++ b/src/sysync/binfileimplds.cpp @@ -2446,6 +2446,7 @@ localstatus TBinfileImplDS::SaveAdminData(bool aSessionFinished, bool aSuccessfu fTarget.lastChangeCheck=fPreviousToRemoteSyncCmpRef; #if TARGETS_DB_VERSION>=6 // - identifiers (tokens for StartDataRead) + PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("SaveAdminData: saving target sync token %s", fPreviousToRemoteSyncIdentifier.c_str())); AssignCString(fTarget.dummyIdentifier1,fPreviousToRemoteSyncIdentifier.c_str(),remoteAnchorMaxLen); // former lastSyncIdentifier AssignCString(fTarget.dummyIdentifier2,NULL,remoteAnchorMaxLen); // former lastSuspendIdentifier, not needed, make empty // store remote datastore's display name (is empty if we haven't got one from the remote via devInf) @@ -2597,7 +2598,7 @@ localstatus TBinfileImplDS::SaveAdminData(bool aSessionFinished, bool aSuccessfu PDEBUGPRINTFX(DBG_ERROR,("Error writing pending item file, bferr=%hd",bfe)); } } - PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_DETAILS,("SaveAdminData: resumeAlertCode = %hd, lastSuspendModCount = %ld",fResumeAlertCode,(long)fTarget.lastSuspendModCount)); + PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_DETAILS,("SaveAdminData: fTargetIndex %d resumeAlertCode = %hd, lastSuspendModCount = %ld",fTargetIndex,fResumeAlertCode,(long)fTarget.lastSuspendModCount)); // update the target record if (fTargetIndex>=0) { targetsBinFileP->updateRecord(fTargetIndex,&fTarget); diff --git a/src/sysync/customimplds.cpp b/src/sysync/customimplds.cpp index 4bd7197..453dd37 100755 --- a/src/sysync/customimplds.cpp +++ b/src/sysync/customimplds.cpp @@ -3187,6 +3187,9 @@ localstatus TCustomImplDS::implSaveEndOfSession(bool aUpdateAnchors) } // also update opaque reference string possibly needed in DS API implementations fPreviousToRemoteSyncIdentifier = fCurrentSyncIdentifier; + PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("updating sync token (fPreviousToRemoteSyncIdentifier) from %s to current sync token %s",fPreviousToRemoteSyncIdentifier.c_str(),fCurrentSyncIdentifier.c_str())); + } else { + PDEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_EXOTIC,("keeping old sync token (fPreviousToRemoteSyncIdentifier) %s instead of updating to current sync token %s",fPreviousToRemoteSyncIdentifier.c_str(),fCurrentSyncIdentifier.c_str())); } // updating anchor means invalidating last Suspend fPreviousSuspendCmpRef = fPreviousToRemoteSyncCmpRef; // setting to current reference can do less harm than setting it to zero -- cgit v1.2.3 From 9c4f83ce915c239bb90e6dbf1ab00cd85bfb836e Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Thu, 23 Aug 2012 13:50:36 +0200 Subject: engine: initial support for caching of remote data This patch introduces support for true one-way syncing ("caching"): the local datastore is meant to be an exact copy of the data on the remote side. The assumption is that no modifications are ever made locally outside of syncing. This is different from one-way sync modes, which allows local changes and only temporarily disables sending them to the remote side. Another goal of the new mode is to avoid data writes as much as possible. This new mode only works on the server side of a sync, where the engine has enough control over the data flow. It has to be enabled in an with the new SETCACHEDATA() method, similar to the way how local read-only mode gets enabled with SETREADONLY(). A CACHEDATA() query method also gets added. A SyncML client cannot enable caching mode when alerting the server, nor can the server alert a client in that mode. The intended usage is that the caching mode is configured in the layer above the engine and then gets enabled in the engine after initiating a normal slow or incremental sync. Once the mode is enabled, the SyncML server behaves differently as follows: - The local sync set is always initialized, even in refresh-from-client syncs. That's because the data is intended to be reused during refresh-from-client syncs. - A refresh-from-client is treated like a slow sync: the mapping is cleared and remote items are matched. - After receiving and matching all remote items in a slow sync or refresh-from-client sync, any local item that was not matched (= its status is still "to be added remotely") gets deleted because it no longer exists on the remote side. The behavior of matching items in caching mode still needs to be changed to achieve the desired behavior. This is in a separate patch. --- src/DB_interfaces/api_db/pluginapids.cpp | 4 +++- src/sysync/customimplds.cpp | 15 +++++++++------ src/sysync/customimplds.h | 2 ++ src/sysync/localengineds.cpp | 23 ++++++++++++++++++++++- src/sysync/localengineds.h | 5 +++++ src/sysync/stdlogicds.cpp | 29 ++++++++++++++++++++++++++++- src/sysync/stdlogicds.h | 2 ++ 7 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/DB_interfaces/api_db/pluginapids.cpp b/src/DB_interfaces/api_db/pluginapids.cpp index 0d3101b..48212c6 100755 --- a/src/DB_interfaces/api_db/pluginapids.cpp +++ b/src/DB_interfaces/api_db/pluginapids.cpp @@ -1069,7 +1069,7 @@ localstatus TPluginApiDS::apiReadSyncSet(bool aNeedAll) // we don't need to load the syncset if we are only refreshing from remote // but we also must load it if we can't zap without it on slow refresh, or when we can't retrieve items on non-slow refresh // (we won't retrieve anything in case of slow refresh, because after zapping there's nothing left by definition) - if (!fRefreshOnly || (fSlowSync && apiNeedSyncSetToZap()) || (!fSlowSync && implNeedSyncSetToRetrieve())) { + if (!fRefreshOnly || (fRefreshOnly && fCacheData) || (fSlowSync && apiNeedSyncSetToZap()) || (!fSlowSync && implNeedSyncSetToRetrieve())) { SYSYNC_TRY { // true for initial ReadNextItem*() call, false later on bool firstReadNextItem=true; @@ -1202,6 +1202,8 @@ localstatus TPluginApiDS::apiReadSyncSet(bool aNeedAll) SYSYNC_CATCH (...) dberr=LOCERR_EXCEPTION; SYSYNC_ENDCATCH + } else { + PDEBUGPRINTFX(DBG_DATA+DBG_EXOTIC,("skipped reading sync set because of refresh-from-peer sync")); } // if we need the syncset at all endread: // then end read here diff --git a/src/sysync/customimplds.cpp b/src/sysync/customimplds.cpp index 453dd37..9547f87 100755 --- a/src/sysync/customimplds.cpp +++ b/src/sysync/customimplds.cpp @@ -1844,7 +1844,7 @@ void TCustomImplDS::implMarkOnlyUngeneratedForResume(void) } } } - else if (!isRefreshOnly()) { + else if (!isRefreshOnly() || (isRefreshOnly() && isCacheData())) { // not slow sync, and not refresh from remote only - mark those that are actually are involved if (pos!=fMapTable.end()) { // known item, needs a mark only if record is modified (and updates reported at all) @@ -2110,7 +2110,7 @@ localstatus TCustomImplDS::implGetItem( TMultiFieldItem *myitemP=NULL; // short-cut if refreshing only and not slowsync resuming (then we need the items for comparison) - if (isRefreshOnly() && !(isResuming() && isSlowSync())) + if (isRefreshOnly() && !isCacheData() && !(isResuming() && isSlowSync())) return sta; // aEof is set, return nothing TP_DEFIDX(li); @@ -2249,7 +2249,7 @@ localstatus TCustomImplDS::implGetItem( // this item apparently was already slow-sync-matched before the suspend - still show it for reference to avoid re-adding it sop=sop_reference_only; } - else if (!isRefreshOnly()) { + else if (!isRefreshOnly() || (isRefreshOnly() && isCacheData())) { // item is already in map: check if this is an already detected, but unfinished add if (!((*pos).mapflags & mapflag_pendingAddConfirm)) { // is a replace (not an add): changed if mod date newer or resend flagged (AND updates enabled) @@ -2337,7 +2337,10 @@ localstatus TCustomImplDS::implGetItem( // session, so just leave it out for now) // Note: this is first-time add detection. If we get this reported as sop_wants_add below, a map with // mapflag_pendingAddConfirm will be created for it. - if (isRefreshOnly()) { + if (isRefreshOnly() && isCacheData()) { + PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("New item (no map yet) detected during Refresh only -> ignore for now, will be deleted later unless matched against peer item")); + sop=sop_none; + } else if (isRefreshOnly()) { PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("New item (no map yet) detected during Refresh only -> ignore for now, will be added in next two-way sync")); sop=sop_none; } @@ -2532,7 +2535,7 @@ localstatus TCustomImplDS::implStartDataWrite() // - transaction starts implicitly when first INSERT / UPDATE / DELETE occurs // - resumed slow refreshes must NOT zap the sync set again! // - prevent zapping when datastore is in readonly mode! - if (fRefreshOnly && fSlowSync && !isResuming() && !fReadOnly) { + if (fRefreshOnly && !fCacheData && fSlowSync && !isResuming() && !fReadOnly) { // - make sure we have at least one pev_deleting event, in case app tracks it to see if session caused changes to DB DB_PROGRESS_EVENT(this,pev_deleting,0,0,0); // now, we need to zap the DB first @@ -3163,7 +3166,7 @@ localstatus TCustomImplDS::implSaveEndOfSession(bool aUpdateAnchors) PDEBUGBLOCKCOLL("SaveEndOfSession"); // update TCustomImplDS dsSavedAdmin variables (other levels have already updated their variables if (aUpdateAnchors) { - if (!fRefreshOnly || fSlowSync) { + if (!fRefreshOnly || (fRefreshOnly && fCacheData) || fSlowSync) { // This was really a two-way sync or we implicitly know that // we are now in sync with remote (like after one-way-from-remote refresh = reload local) #ifdef BASED_ON_BINFILE_CLIENT diff --git a/src/sysync/customimplds.h b/src/sysync/customimplds.h index de2200f..6f70b0b 100755 --- a/src/sysync/customimplds.h +++ b/src/sysync/customimplds.h @@ -688,6 +688,8 @@ protected: TSyncItem &aItem, // the item TStatusCommand &aStatusCommand ); + virtual localstatus logicDeleteItemByID(TSyncItem &aSyncItem) { return apiDeleteItem((TMultiFieldItem &)aSyncItem); } + /// process item (according to operation: add/delete/replace - and for future: copy/move) virtual bool implProcessItem( TSyncItem *aItemP, // the item diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index 095b327..f6c5be4 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -360,6 +360,22 @@ public: }; // func_SetRefreshOnly + // integer CACHEDATA() + // returns true if sync is refreshing local data in caching mode (without deleting everything beforehand) + static void func_CacheData(TItemField *&aTermP, TScriptContext *aFuncContextP) + { + aTermP->setAsBoolean( + static_cast(aFuncContextP->getCallerContext())->isCacheData() + ); + }; // func_CacheData + static void func_SetCacheData(TItemField *&aTermP, TScriptContext *aFuncContextP) + { + static_cast(aFuncContextP->getCallerContext())->engSetCacheData( + aFuncContextP->getLocalVar(0)->getAsBoolean() + ); + }; // func_SetCacheData + + // integer READONLY() // returns true if sync is read-only (only reading from local datastore) static void func_ReadOnly(TItemField *&aTermP, TScriptContext *aFuncContextP) @@ -564,6 +580,8 @@ const TBuiltInFuncDef DBFuncDefs[] = { { "FORCESLOWSYNC", TLDSfuncs::func_ForceSlowSync, fty_none, 0, NULL }, { "REFRESHONLY", TLDSfuncs::func_RefreshOnly, fty_integer, 0, NULL }, { "SETREFRESHONLY", TLDSfuncs::func_SetRefreshOnly, fty_none, 1, param_IntArg }, + { "CACHEDATA", TLDSfuncs::func_CacheData, fty_integer, 0, NULL }, + { "SETCACHEDATA", TLDSfuncs::func_SetCacheData, fty_none, 1, param_IntArg }, { "READONLY", TLDSfuncs::func_ReadOnly, fty_integer, 0, NULL }, { "SETREADONLY", TLDSfuncs::func_SetReadOnly, fty_none, 1, param_IntArg }, { "FIRSTTIMESYNC", TLDSfuncs::func_FirstTimeSync, fty_integer, 0, NULL }, @@ -1170,6 +1188,7 @@ void TLocalEngineDS::InternalResetDataStore(void) fForceSlowSync=false; fSlowSync=false; fRefreshOnly=false; + fCacheData=false; fReadOnly=false; fReportUpdates=fDSConfigP->fReportUpdates; // update reporting according to what is configured fCanRestart=fDSConfigP->fCanRestart; @@ -2468,6 +2487,7 @@ TAlertCommand *TLocalEngineDS::engProcessSyncAlert( if (IS_SERVER) { // server case: initially we are not in refresh only mode. Alert code or alert script could change this fRefreshOnly=false; + fCacheData=false; } // save it for suspend and reference in scripts @@ -2630,12 +2650,13 @@ TAlertCommand *TLocalEngineDS::engProcessSyncAlert( } // - datastore state is now dss_alerted PDEBUGPRINTFX(DBG_HOT,( - "Alerted (code=%hd) for %s%s %s%s%s ", + "Alerted (code=%hd) for %s%s %s%s%s%s ", aAlertCode, fResuming ? "Resumed " : "", SyncModeDescriptions[fSyncMode], fSlowSync ? (fSyncMode==smo_twoway ? "Slow Sync" : "Refresh") : "Normal Sync", fReadOnly ? " (Readonly)" : "", + fCacheData ? " (Cache)" : "", fRefreshOnly ? " (Refreshonly)" : "" )); CONSOLEPRINTF(( diff --git a/src/sysync/localengineds.h b/src/sysync/localengineds.h index 8a2b748..1a6885d 100755 --- a/src/sysync/localengineds.h +++ b/src/sysync/localengineds.h @@ -335,6 +335,9 @@ protected: bool fForceSlowSync; ///< set if external reason wants to force a slow sync even if it is not needed bool fSlowSync; ///< set if slow sync or refresh bool fRefreshOnly; ///< set if local data is refreshed from remote only, that is, no local changes will be sent to remote (can be set independently of apparent fSyncMode) + bool fCacheData; ///< only relevant if fRefreshOnly is also set: instead of throwing away + /// all data at the start of the sync, apply remote changes and remove + /// all local data that the peer doesn't have bool fReadOnly; ///< if set, datastore will not write any user data (but fake successful status to remote) bool fReportUpdates; ///< if NOT set, datastore will not report updates to client (e.g. for email) bool fServerAlerted; ///< set if sync was server alerted @@ -581,6 +584,7 @@ public: bool isSlowSync(void) { return fSlowSync; }; ///< true if slow sync bool isResuming(void) { return fResuming; }; ///< true if resuming a previous session bool isRefreshOnly(void) { return fRefreshOnly; }; ///< true if only refreshing with data from remote (no send to remote) + bool isCacheData(void) { return fCacheData; }; bool isReadOnly(void) { return fReadOnly; }; ///< true if only reading from local datastore (and ignoring any updates from remote) /// get remote datastore related to this datastore TRemoteDataStore *getRemoteDatastore(void) { return fRemoteDatastoreP; }; @@ -653,6 +657,7 @@ public: void engForceSlowSync(void) { fForceSlowSync=true; }; /// set refresh only mode (do not send to remote) void engSetRefreshOnly(bool b) { fRefreshOnly=b; }; + void engSetCacheData(bool b) { fCacheData=b; }; /// set read only mode (do not receive from remote) void engSetReadOnly(bool b) { fReadOnly=b; }; /// can be called to avoid further ADD commands to be sent to remote (device full case, e.g.) diff --git a/src/sysync/stdlogicds.cpp b/src/sysync/stdlogicds.cpp index 6a8d75d..04eee91 100644 --- a/src/sysync/stdlogicds.cpp +++ b/src/sysync/stdlogicds.cpp @@ -302,7 +302,7 @@ localstatus TStdLogicDS::performStartSync(void) fNumRefOnlyItems=0; if (sta==LOCERR_OK) { // now get data from DB - if (!isRefreshOnly() || (isSlowSync() && isResuming())) { + if (!isRefreshOnly() || (isRefreshOnly() && isCacheData()) || (isSlowSync() && isResuming())) { // not only updating from client, so read all items now // Note: for a resumed slow updating from client only, we need the // currently present syncset as well as we need it to detect @@ -1537,6 +1537,33 @@ localstatus TStdLogicDS::dsBeforeStateChange(TLocalEngineDSState aOldState,TLoca sta = startDataWrite(); } } // client + if (aNewState==dssta_serverseenclientmods) { + // Can only happen in server. Implement removal of unmatched items + // when in caching mode. + if (fCacheData && fSlowSync) { + TSyncItemPContainer::iterator pos=fItems.begin(); + while (pos!=fItems.end()) { + TSyncItem *syncitemP = (*pos); + if (syncitemP->getSyncOp() == sop_wants_add) { + PDEBUGPRINTFX(DBG_DATA,("caching mode: remove unmatched item %s", + syncitemP->getLocalID())); + syncitemP->setSyncOp(sop_delete); + localstatus status = logicDeleteItemByID(*syncitemP); + if (status != LOCERR_OK) { + return status; + } else { + TSyncItemPContainer::iterator next = pos; + ++next; + fItems.erase(pos); + pos = next; + fLocalItemsDeleted++; + } + } else { + ++pos; + } + } + } + } if (aNewState==dssta_completed && !isAborted()) { // finish writing data now anyway endDataWrite(); diff --git a/src/sysync/stdlogicds.h b/src/sysync/stdlogicds.h index a633a98..1470905 100755 --- a/src/sysync/stdlogicds.h +++ b/src/sysync/stdlogicds.h @@ -174,6 +174,8 @@ protected: TStatusCommand &aStatusCommand ///< status, must be set on error or non-200-status ); + /// delete existing item in datastore, returns 211 if not existing any more + virtual localstatus logicDeleteItemByID(TSyncItem &aSyncItem) = 0; /// @} -- cgit v1.2.3 From 840c95e4aed4ef0080c44f0d4946d2e8d5e169d6 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Fri, 24 Aug 2012 15:17:40 +0200 Subject: engine: ignore unassigned entries at end of array The engine treated an array with unassigned entries at the end as larger (and thus different) from an array that ends before those entries, although reading those entries would also have return "unassigned". This caused the engine to treat items as different although there were semantically identical. Found while testing caching mode with SyncEvolution, where it is critical to minimize writing to the cache. The new code checks the larger array to see if all the extra entries are unassigned, in which case it returns "equal" as result instead of the previous "larger" or "smaller". --- src/sysync/itemfield.cpp | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/sysync/itemfield.cpp b/src/sysync/itemfield.cpp index ada6a71..e6f7d71 100644 --- a/src/sysync/itemfield.cpp +++ b/src/sysync/itemfield.cpp @@ -466,8 +466,33 @@ sInt16 TArrayField::compareWith(TItemField &aItemField, bool aCaseInsensitive) } // all compared fields are equal if (mysz==othersz) return 0; // same size : equal - // larger array is greater - return mysz>othersz ? 1 : -1; + // Sizes differ, but that only matters if the extra entries are + // actually assigned. Without that special case, we end up + // with the situation where parsing, encoding and parsing + // again leads to different fields: + // - EMAIL;TYPE=OTHER:foo -> EMAIL_FLAGS 1 entry "unassigned" + // - unassigned -> EMAIL:foo + // - EMAIL:foo -> EMAIL_FLAGS 0 entry + // - EMAIL_FLAGS 1 entry "unassigned" > EMAIL_FLAGS 0 entry + TItemField &largerField = mysz < othersz ? aItemField : *this; + sInt16 minsz, maxsz; + sInt16 res; // larger array is greater + if (mysz < othersz) { + minsz = mysz; + maxsz = othersz; + res = -1; + } else { + minsz = othersz; + maxsz = mysz; + res = 1; + } + for (sInt16 idx=minsz; idxisAssigned()) + // found real difference + return res; + } + // larger array contains only extra unassigned entries, ignore them + return 0; } // TArrayField::compareWith /* end of TArrayField implementation */ -- cgit v1.2.3 From 77c4693ed270a84a8fa7bee2c0b5ed2882c98880 Mon Sep 17 00:00:00 2001 From: Patrick Ohly Date: Fri, 24 Aug 2012 15:33:50 +0200 Subject: engine: override merge options In caching mode, the local data is meant to be an exact copy of the client's data. If the client removes fields in an item and then does a slow sync, no fields of a matching server item are meant to be preserved (= merged). This can be achieved with merge=no in the field list. But the field list is static and shared between data stores, which might not all operate in caching mode. This makes updating the config cumbersome. Therefore this patch adds a flag to the merging functions instead which overrides the configured behavior such that fields are always copied from one side into the other. This is activated when in caching mode and also (optionally) via the MERGEITEMS() function. The comparison is still needed, to determine whether anything changed at all during a slow sync and prevent unnecessary database writes. The loosing side is always the server in caching mode, no need to check (similar to making the local store read-only). This all applies to "slow sync" and "conflict" code paths. In caching mode, a is currently ignored in favor of doing the intended copying directly. Needs to be fixed. --- src/sysync/localengineds.cpp | 20 ++++++++++++++++---- src/sysync/multifielditem.cpp | 26 ++++++++++++++++---------- src/sysync/multifielditem.h | 4 ++-- src/sysync/multifielditemtype.cpp | 7 +++++-- src/sysync/syncitem.h | 7 ++++++- 5 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/sysync/localengineds.cpp b/src/sysync/localengineds.cpp index f6c5be4..c81c412 100644 --- a/src/sysync/localengineds.cpp +++ b/src/sysync/localengineds.cpp @@ -5652,6 +5652,10 @@ bool TLocalEngineDS::engProcessRemoteItemAsServer( PDEBUGPRINTFX(DBG_DATA,("Read-Only or IgnoreUpdate: server always wins")); crstrategy=cr_server_wins; } + else if (fCacheData) { + PDEBUGPRINTFX(DBG_DATA,("Caching data: client always wins")); + crstrategy=cr_client_wins; + } else { // two-way crstrategy = fItemConflictStrategy; // get conflict strategy pre-set for this item @@ -5849,7 +5853,7 @@ bool TLocalEngineDS::engProcessRemoteItemAsServer( aStatusCommand.setStatusCode(208); // client wins fConflictsClientWins++; // - attempt to merge data from loosing item (accumulating fields) - if (!deleteconflict) { + if (!fCacheData && !deleteconflict) { aSyncItemP->mergeWith(*conflictingItemP,changedincoming,changedexisting,this); } if (changedincoming) { @@ -5977,11 +5981,19 @@ bool TLocalEngineDS::engProcessRemoteItemAsServer( } // if adds prevented, we cannot duplicate, let server win if (fPreventAdd && crstrategy==cr_duplicate) crstrategy=cr_server_wins; + else if (fCacheData) crstrategy=cr_client_wins; // now execute chosen strategy if (crstrategy==cr_client_wins) { - // - merge server's data into client item - PDEBUGPRINTFX(DBG_DATA,("Trying to merge some data from loosing server item into winning client item")); - aSyncItemP->mergeWith(*matchingItemP,changedincoming,changedexisting,this); + if (fCacheData) { + // - merge server's data into client item + PDEBUGPRINTFX(DBG_DATA,("Caching: copying winning client item into loosing server item")); + aSyncItemP->mergeWith(*matchingItemP,changedincoming,changedexisting,this, + TSyncItem::MERGE_OPTION_CHANGE_OTHER); + } else { + // - merge server's data into client item + PDEBUGPRINTFX(DBG_DATA,("Trying to merge some data from loosing server item into winning client item")); + aSyncItemP->mergeWith(*matchingItemP,changedincoming,changedexisting,this); + } // only count if server gets updated if (changedexisting) fConflictsClientWins++; // Note: changedexisting will cause needserverupdate to be set below diff --git a/src/sysync/multifielditem.cpp b/src/sysync/multifielditem.cpp index 3436c29..3d00435 100755 --- a/src/sysync/multifielditem.cpp +++ b/src/sysync/multifielditem.cpp @@ -1356,15 +1356,15 @@ bool TMultiFieldItem::checkItem(TLocalEngineDS *aDatastoreP) // - also updates other item to make sure it is equal to the winning after the merge // sets (but does not reset) change status of this and other item. // Note that changes of non-relevant fields are not reported here. -void TMultiFieldItem::mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP) +void TMultiFieldItem::mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP, int mode) { TMultiFieldItem *multifielditemP = castToSameTypeP(&aItem); if (!multifielditemP) return; // do the merge - if (fItemTypeP) + if (fItemTypeP && mode == MERGE_OPTION_FROM_CONFIG) fItemTypeP->mergeItems(*this,*multifielditemP,aChangedThis,aChangedOther,aDatastoreP); else - standardMergeWith(*multifielditemP,aChangedThis,aChangedOther); + standardMergeWith(*multifielditemP,aChangedThis,aChangedOther, mode); // show result OBJDEBUGPRINTFX(getItemType()->getSession(),DBG_DATA+DBG_CONFLICT,( "mergeWith() final status: thisitem: %schanged, otheritem: %schanged (relevant; eqm_none field changes are not indicated)", @@ -1380,7 +1380,8 @@ void TMultiFieldItem::mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aCha // - also updates other item to make sure it is equal to the winning after the merge // returns update status of this and other item. Note that changes of non-relevant fields are // not reported here. -void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther) +void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther, + int mode) { // same type of multifield, try to merge for (sInt16 i=0; inumFields(); i++) { @@ -1401,7 +1402,7 @@ void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedTh bool winning = winningField.isAssigned(); bool loosing = loosingField.isAssigned(); // - now decide what to do - if (sep!=mem_none) { + if (sep!=mem_none && mode == MERGE_OPTION_FROM_CONFIG) { // merge enabled PDEBUGPRINTFX(DBG_DATA+DBG_CONFLICT,( "Field '%s' available and enabled for merging, mode/sep=0x%04hX, %srelevant", @@ -1483,7 +1484,11 @@ void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedTh // assignment just passes the proxy) if (!mergerelevant) { // everything is handled by the field assignment mechanisms - loosingField = winningField; + if (mode == MERGE_OPTION_CHANGE_THIS) { + winningField = loosingField; + } else { + loosingField = winningField; + } } else if (winningField!=loosingField) { // merge relevant fields will get more sophisticated treatment, such @@ -1498,16 +1503,17 @@ void TMultiFieldItem::standardMergeWith(TMultiFieldItem &aItem, bool &aChangedTh FMT_LENGTH_LIMITED(30,wfv.c_str()),FMT_LENGTH_LIMITED(30,lfv.c_str()) )); #endif - // update loosing item, too - if (loosingField.isShortVers(winningField,fItemTypeP->getFieldOptions(i)->maxsize)) { + // update loosing item, too, unless the winning field is shorter or explicitly requested + if (mode == MERGE_OPTION_CHANGE_THIS || + loosingField.isShortVers(winningField,fItemTypeP->getFieldOptions(i)->maxsize)) { // winning field is short version of loosing field -> loosing field is "better", use it winningField=loosingField; - if (mergerelevant) aChangedThis=true; + aChangedThis=true; } else { // standard case, loosing field is replaced by winning field loosingField=winningField; - if (mergerelevant) aChangedOther=true; + aChangedOther=true; } // this is some kind of item-level merge as well #ifdef SYDEBUG diff --git a/src/sysync/multifielditem.h b/src/sysync/multifielditem.h index 0582f66..0cc14bd 100755 --- a/src/sysync/multifielditem.h +++ b/src/sysync/multifielditem.h @@ -325,9 +325,9 @@ public: // - also updates other item to make sure it is equal to the winning after the merge // sets (but does not reset) change status of this and other item. // Note that changes of non-relevant fields are not reported here. - virtual void mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP); + virtual void mergeWith(TSyncItem &aItem, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS *aDatastoreP, int mode = MERGE_OPTION_FROM_CONFIG); // standard merge (subset of mergeWith, used if no merge script is defined) - void standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther); + void standardMergeWith(TMultiFieldItem &aItem, bool &aChangedThis, bool &aChangedOther, int mode = MERGE_OPTION_FROM_CONFIG); // compare: returns 0 if equal, 1 if this > aItem, -1 if this < aItem // SYSYNC_NOT_COMPARABLE if not equal and no ordering known virtual sInt16 compareWith( diff --git a/src/sysync/multifielditemtype.cpp b/src/sysync/multifielditemtype.cpp index 02d629f..6dd32d9 100755 --- a/src/sysync/multifielditemtype.cpp +++ b/src/sysync/multifielditemtype.cpp @@ -189,12 +189,15 @@ public: }; // func_IgnoreUpdate - // void MERGEFIELDS() + // void MERGEFIELDS(mode = 0) + // Optional mode parameter determines the result of the merge. + // 0 = according to config, 1 = loosing item is overwritten, 2 = winning item is overwritten static void func_MergeFields(TItemField *&aTermP, TScriptContext *aFuncContextP) { TMultiFieldItemType *mfitP = static_cast(aFuncContextP->getCallerContext()); if (mfitP->fFirstItemP) - mfitP->fFirstItemP->standardMergeWith(*(mfitP->fSecondItemP),mfitP->fChangedFirst,mfitP->fChangedSecond); + mfitP->fFirstItemP->standardMergeWith(*(mfitP->fSecondItemP),mfitP->fChangedFirst,mfitP->fChangedSecond, + aFuncContextP->getLocalVar(0)->getAsInteger()); }; // func_MergeFields diff --git a/src/sysync/syncitem.h b/src/sysync/syncitem.h index df40c15..ae455c9 100755 --- a/src/sysync/syncitem.h +++ b/src/sysync/syncitem.h @@ -126,13 +126,18 @@ public: virtual bool replaceDataFrom(TSyncItem & /* aItem */, bool /* aAvailableOnly */=false, bool /* aDetectCutoffs */=false, bool /* aAssignedOnly */=false, bool /* aTransferUnassigned */=false) { return true; }; // no data -> nop // check item before processing it virtual bool checkItem(TLocalEngineDS * /* aDatastoreP */) { return true; }; // default is: ok + enum { + MERGE_OPTION_FROM_CONFIG, /**< merge as defined in the field list */ + MERGE_OPTION_CHANGE_OTHER, /**< ensure that the other item is the same as this item */ + MERGE_OPTION_CHANGE_THIS /**< ensure that this items is the same as the other */ + }; // merge this item with specified item. // Notes: // - specified item is treated as loosing item, this item is winning item // - also updates other item to make sure it is equal to the winning after the merge // sets (but does not reset) change status of this and other item. // Note that changes of non-relevant fields are not reported here. - virtual void mergeWith(TSyncItem & /* aItem */, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS * /* aDatastoreP */) { aChangedThis=false; aChangedOther=false; }; // nop by default + virtual void mergeWith(TSyncItem & /* aItem */, bool &aChangedThis, bool &aChangedOther, TLocalEngineDS * /* aDatastoreP */, int mode = MERGE_OPTION_FROM_CONFIG) { aChangedThis=false; aChangedOther=false; }; // nop by default // remote and local ID string fRemoteID; // ID in remote party (if this is a server: LUID, GUID otherwise) string fLocalID; // ID in this party (if this is a server: GUID, LUID otherwise) -- cgit v1.2.3 From 7afaf89ada4ebc0f726e5b101977a1d1f59d41a6 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Wed, 5 Sep 2012 15:22:50 +0200 Subject: Fixed merge artifact (missing comma) --- src/sysync/syncagent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index 63842af..df48cd6 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -3975,7 +3975,7 @@ static const TStructFieldInfo ServerParamFieldInfos[] = { "displayalert", VALTYPE_TEXT, false, 0, 0, &readDisplayAlert, NULL }, #endif #endif - { "restartsync", VALTYPE_INT8, true, 0, 0, &readRestartSync, &writeRestartSync } + { "restartsync", VALTYPE_INT8, true, 0, 0, &readRestartSync, &writeRestartSync }, // write into debug log { "errorMsg", VALTYPE_TEXT, true, 0, 0, NULL, &writeErrorMsg }, { "debugMsg", VALTYPE_TEXT, true, 0, 0, NULL, &writeDebugMsg }, -- cgit v1.2.3 From 9d2a29062ad3adaa7bad5fd8639110dc0f619044 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Thu, 6 Sep 2012 09:13:57 +0200 Subject: XCode 4.4: applied recommended scheme updates (use LLDB, no longer GDB - howvever needed to do it manually as debugger for test action is not automatically upgraded, only setting for run action is). --- src/sysync_ios.xcodeproj/project.pbxproj | 2 +- .../xcschemes/static_clientEngine_opensource_ios.xcscheme | 3 ++- .../xcschemes/static_combiEngine_opensource_ios.xcscheme | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sysync_ios.xcodeproj/project.pbxproj b/src/sysync_ios.xcodeproj/project.pbxproj index 4152bd3..b26594a 100644 --- a/src/sysync_ios.xcodeproj/project.pbxproj +++ b/src/sysync_ios.xcodeproj/project.pbxproj @@ -1770,7 +1770,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0430; + LastUpgradeCheck = 0440; ORGANIZATIONNAME = plan44.ch; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "sysync_ios" */; diff --git a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme index b1c96ea..13316e8 100644 --- a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme +++ b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_clientEngine_opensource_ios.xcscheme @@ -1,6 +1,6 @@ diff --git a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme index 04507cc..1a2bbf7 100644 --- a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme +++ b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme @@ -1,6 +1,6 @@ @@ -38,6 +38,7 @@ buildConfiguration = "Debug" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" + enableOpenGLFrameCaptureMode = "0" allowLocationSimulation = "YES"> -- cgit v1.2.3 From 877ff4da622a71e76e9497a71097aeb152cbc843 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 7 Sep 2012 13:00:03 +0200 Subject: TPluginAPIDS: protected dsFinalizeLocalID() from calling into plugin before DBApi_Data has been initialized (created a DB context). This does not happen under normal circumstances, but when the SyncML protocol is messed up enough, we can get here before the datastore has been initialized and therefore crash. --- src/DB_interfaces/api_db/pluginapids.cpp | 6 +++++- src/sysync/binfileimplds.cpp | 2 +- src/sysync/customimplds.cpp | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/DB_interfaces/api_db/pluginapids.cpp b/src/DB_interfaces/api_db/pluginapids.cpp index 48212c6..0540a28 100755 --- a/src/DB_interfaces/api_db/pluginapids.cpp +++ b/src/DB_interfaces/api_db/pluginapids.cpp @@ -1404,8 +1404,12 @@ bool TPluginApiDS::dsFinalizeLocalID(string &aLocalID) #ifndef SDK_ONLY_SUPPORT // only handle here if we are in charge - otherwise let ancestor handle it if (!fDBApi_Data.Created()) return inherited::dsFinalizeLocalID(aLocalID); + #else + // still check for DBAPi to be ready at this point, because when peer messes up protocol, we + // can get here before the datastore has been initialized at all + if (!fDBApi_Data.Created()) return false; // no dataset loaded -> all localids are final (from last session) #endif - + TDB_Api_Str finalizedID; localstatus sta = fDBApi_Data.FinalizeLocalID(aLocalID.c_str(),finalizedID); if (sta==LOCERR_OK && !finalizedID.empty()) { diff --git a/src/sysync/binfileimplds.cpp b/src/sysync/binfileimplds.cpp index 8708215..4353b95 100755 --- a/src/sysync/binfileimplds.cpp +++ b/src/sysync/binfileimplds.cpp @@ -2507,7 +2507,7 @@ localstatus TBinfileImplDS::SaveAdminData(bool aSessionFinished, bool aSuccessfu fPendingMapHeader.remotepartyID = static_cast(fSessionP)->fRemotepartyID; fPendingMaps.setExtraHeaderDirty(); // - now pending maps (unsent ones) - DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_DETAILS,("SaveAdminData: saving %ld entries from fPendingAddMap to fPendingMaps binfile",(long)fPendingAddMaps.size())); + DEBUGPRINTFX(DBG_ADMIN+DBG_DBAPI+DBG_DETAILS,("SaveAdminData: saving %ld entries from fPendingAddMaps to fPendingMaps binfile",(long)fPendingAddMaps.size())); for (spos=fPendingAddMaps.begin();spos!=fPendingAddMaps.end();spos++) { string locID = (*spos).first; dsFinalizeLocalID(locID); // pending maps might have non-final ID, so give datastore implementation to return finalized version diff --git a/src/sysync/customimplds.cpp b/src/sysync/customimplds.cpp index 9547f87..42f430c 100755 --- a/src/sysync/customimplds.cpp +++ b/src/sysync/customimplds.cpp @@ -3126,7 +3126,7 @@ localstatus TCustomImplDS::SaveAdminData(bool aSessionFinished, bool aSuccessful if (IS_CLIENT) { #ifdef SYSYNC_CLIENT // - now pending maps (unsent ones) - PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("SaveAdminData: adding %ld entries from fPendingAddMap as mapentry_pendingmap",(long)fPendingAddMaps.size())); + PDEBUGPRINTFX(DBG_ADMIN+DBG_EXOTIC,("SaveAdminData: adding %ld entries from fPendingAddMaps as mapentry_pendingmap",(long)fPendingAddMaps.size())); for (spos=fPendingAddMaps.begin();spos!=fPendingAddMaps.end();spos++) { string locID = (*spos).first; dsFinalizeLocalID(locID); // make sure we have the permanent version in case datastore implementation did deliver temp IDs -- cgit v1.2.3 From 543a40b46f118ae10981fafb7aded4f17bdcb284 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Sun, 9 Sep 2012 14:46:48 +0200 Subject: binfile: Fixed bug in deleteProfile() introduced in 2008 - after deleting last profile settings file was not recreated correctly, so subsequent operations (like adding a new profile) did not work until app was restarted. Commit (in 2008, before libsynthesis OS) which introduced the problem: 1594f21f2f (added file cleaning to enhance stability - all files will be deleted when last profile is deleted, and changelogs will be deleted whenever a non-resuming slow sync starts while only ONE profile exists.) --- src/sysync/binfileimplclient.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sysync/binfileimplclient.cpp b/src/sysync/binfileimplclient.cpp index 29c243a..bf803d1 100755 --- a/src/sysync/binfileimplclient.cpp +++ b/src/sysync/binfileimplclient.cpp @@ -1921,7 +1921,8 @@ bool TBinfileClientConfig::deleteProfile(sInt32 aProfileIndex) if (fProfileBinFile.getNumRecords()==0) { // last profile deleted, remove file itself to clean up as much as possible fProfileBinFile.closeAndDelete(); - fProfileBinFile.open(); // re-open = re-create + // re-create + fProfileBinFile.create(sizeof(TBinfileDBSyncProfile),0,NULL,true); } else { // make sure header is up to date (in case we terminate improperly) @@ -2298,7 +2299,7 @@ bool TBinfileClientConfig::deleteTarget( if (fTargetsBinFile.getNumRecords()==0) { // last target deleted, remove file itself to clean up as much as possible fTargetsBinFile.closeAndDelete(); - fTargetsBinFile.open(); // re-open = re-create + fTargetsBinFile.create(sizeof(TBinfileDBSyncTarget),0,NULL,true); } else { // make sure header is up to date (in case we terminate improperly) -- cgit v1.2.3 From e0977af971f4d87c45729e938d279070518b7944 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Mon, 10 Sep 2012 11:11:12 +0200 Subject: iOS: changed to using identifierForVendor instead of deprecated UUID in iOS6 --- src/platform_adapters/macosx/configfiles.mm | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/platform_adapters/macosx/configfiles.mm b/src/platform_adapters/macosx/configfiles.mm index d8f1140..b7c21b2 100644 --- a/src/platform_adapters/macosx/configfiles.mm +++ b/src/platform_adapters/macosx/configfiles.mm @@ -322,12 +322,26 @@ lineartime_t getFileModificationDate(const char *aFileName) // iPhone + + + // get local device URI/ID bool getLocalDeviceID(string &aURI) { UIDevice *theDevice = [UIDevice currentDevice]; - // Obtain unique ID - aURI = [theDevice.uniqueIdentifier UTF8String]; + #ifdef __IPHONE_6_0 + if ([theDevice respondsToSelector:@selector(identifierForVendor)]) { + // iOS 6 style identifier + aURI = [[theDevice identifierForVendor] UUIDString]; + } + else + #endif + { + // Obtain old-style unique ID, in disguise because we should not use it any more + // "uniqueIdentifier" + SEL uiSEL = NSSelectorFromString([NSString stringWithFormat:@"un%c%cu%1xI%2xnti%1xier",'i','q',0x0E,0xDE,0x0F]); + aURI = [[theDevice performSelector:uiSEL] UTF8String]; + } return true; } // getLocalDeviceID -- cgit v1.2.3 From db1aa723526bf53ea242ab5b74698e21bb9be91e Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Mon, 10 Sep 2012 17:24:16 +0200 Subject: configfiles: Fixed iOS6 case, was missing conversion to C string --- src/platform_adapters/macosx/configfiles.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_adapters/macosx/configfiles.mm b/src/platform_adapters/macosx/configfiles.mm index b7c21b2..dde7fee 100644 --- a/src/platform_adapters/macosx/configfiles.mm +++ b/src/platform_adapters/macosx/configfiles.mm @@ -332,7 +332,7 @@ bool getLocalDeviceID(string &aURI) #ifdef __IPHONE_6_0 if ([theDevice respondsToSelector:@selector(identifierForVendor)]) { // iOS 6 style identifier - aURI = [[theDevice identifierForVendor] UUIDString]; + aURI = [[[theDevice identifierForVendor] UUIDString] UTF8String]; } else #endif -- cgit v1.2.3 From 676fa1b9cf0bb6755fc26de109a1628149755f9f Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Fri, 14 Sep 2012 17:57:46 +0200 Subject: iOS6/XCode 4.5: no longer building for armv6, but now for armv7s (in addition to armv7) --- src/sysync_ios.xcodeproj/project.pbxproj | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/sysync_ios.xcodeproj/project.pbxproj b/src/sysync_ios.xcodeproj/project.pbxproj index b26594a..f5a1fb2 100644 --- a/src/sysync_ios.xcodeproj/project.pbxproj +++ b/src/sysync_ios.xcodeproj/project.pbxproj @@ -2106,10 +2106,6 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = ( - armv6, - armv7, - ); GCC_C_LANGUAGE_STANDARD = c99; GCC_THUMB_SUPPORT = NO; GCC_WARN_ABOUT_RETURN_TYPE = YES; @@ -2125,10 +2121,6 @@ C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = ( - armv6, - armv7, - ); GCC_C_LANGUAGE_STANDARD = c99; GCC_THUMB_SUPPORT = NO; GENERATE_MASTER_OBJECT_FILE = YES; -- cgit v1.2.3 From 500aee4cecda5b807b32102a0d9badd825391ea8 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Sat, 15 Sep 2012 16:55:37 +0200 Subject: iOS: release builds no longer have extended debug, just normal. --- .../ReleasedProducts/clientEngine_opensource_ios/target_options.h | 2 +- .../ReleasedProducts/combiEngine_opensource_ios/target_options.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Targets/ReleasedProducts/clientEngine_opensource_ios/target_options.h b/src/Targets/ReleasedProducts/clientEngine_opensource_ios/target_options.h index 694bbfb..3430f61 100644 --- a/src/Targets/ReleasedProducts/clientEngine_opensource_ios/target_options.h +++ b/src/Targets/ReleasedProducts/clientEngine_opensource_ios/target_options.h @@ -17,7 +17,7 @@ #if !DEBUG #define RELEASE_VERSION #endif -#define RELEASE_SYDEBUG 2 // extended DBG included +#define RELEASE_SYDEBUG 1 // no extended DBG included //#define OPTIONAL_SYDEBUG 1 // Eval limit options diff --git a/src/Targets/ReleasedProducts/combiEngine_opensource_ios/target_options.h b/src/Targets/ReleasedProducts/combiEngine_opensource_ios/target_options.h index 214c41b..eebbf4a 100644 --- a/src/Targets/ReleasedProducts/combiEngine_opensource_ios/target_options.h +++ b/src/Targets/ReleasedProducts/combiEngine_opensource_ios/target_options.h @@ -17,7 +17,7 @@ #if !DEBUG #define RELEASE_VERSION #endif -#define RELEASE_SYDEBUG 2 // extended DBG included +#define RELEASE_SYDEBUG 1 // no extended DBG included //#define OPTIONAL_SYDEBUG 1 // Eval limit options -- cgit v1.2.3 From af97a17189648d14df614a4d88350c876a4e0943 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Mon, 17 Sep 2012 11:38:32 +0200 Subject: updated contributor agreement name --- Synthesis_AG_Contributor_Agreement.doc | Bin 41472 -> 0 bytes Synthesis_AG_Contributor_Agreement.pdf | Bin 55375 -> 0 bytes libsynthesis_contributor_agreement.doc | Bin 0 -> 43008 bytes libsynthesis_contributor_agreement.pages | Bin 0 -> 216055 bytes libsynthesis_contributor_agreement.pdf | Bin 0 -> 60889 bytes 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Synthesis_AG_Contributor_Agreement.doc delete mode 100644 Synthesis_AG_Contributor_Agreement.pdf create mode 100644 libsynthesis_contributor_agreement.doc create mode 100644 libsynthesis_contributor_agreement.pages create mode 100644 libsynthesis_contributor_agreement.pdf diff --git a/Synthesis_AG_Contributor_Agreement.doc b/Synthesis_AG_Contributor_Agreement.doc deleted file mode 100644 index 870e644..0000000 Binary files a/Synthesis_AG_Contributor_Agreement.doc and /dev/null differ diff --git a/Synthesis_AG_Contributor_Agreement.pdf b/Synthesis_AG_Contributor_Agreement.pdf deleted file mode 100644 index 1742fc4..0000000 Binary files a/Synthesis_AG_Contributor_Agreement.pdf and /dev/null differ diff --git a/libsynthesis_contributor_agreement.doc b/libsynthesis_contributor_agreement.doc new file mode 100644 index 0000000..d4aef97 Binary files /dev/null and b/libsynthesis_contributor_agreement.doc differ diff --git a/libsynthesis_contributor_agreement.pages b/libsynthesis_contributor_agreement.pages new file mode 100644 index 0000000..e9cd0c8 Binary files /dev/null and b/libsynthesis_contributor_agreement.pages differ diff --git a/libsynthesis_contributor_agreement.pdf b/libsynthesis_contributor_agreement.pdf new file mode 100644 index 0000000..e4a23df Binary files /dev/null and b/libsynthesis_contributor_agreement.pdf differ -- cgit v1.2.3 From 96306358b446d79616b94a78997e27e227acf206 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Sat, 22 Sep 2012 12:04:20 +0200 Subject: XCode 4.5 - cosmetic scheme opening side effect --- .../xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme index 1a2bbf7..348f68c 100644 --- a/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme +++ b/src/sysync_ios.xcodeproj/xcshareddata/xcschemes/static_combiEngine_opensource_ios.xcscheme @@ -38,7 +38,6 @@ buildConfiguration = "Debug" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" - enableOpenGLFrameCaptureMode = "0" allowLocationSimulation = "YES"> -- cgit v1.2.3 From a5bd3a3c386e824b43b31e1a760ddefca2d29b95 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Wed, 26 Sep 2012 20:49:22 +0200 Subject: iOS6 bug workaround - identifierForVendor returning 00000000-0000-0000-0000-000000000000 will now fall back to return UDID instead. Note that this is a bug in iOS6, see: bugReporter/Radar rdar://12377282 OpenRadar: http://openradar.appspot.com/radar?id=2049403 --- src/platform_adapters/macosx/configfiles.mm | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/platform_adapters/macosx/configfiles.mm b/src/platform_adapters/macosx/configfiles.mm index dde7fee..7e25099 100644 --- a/src/platform_adapters/macosx/configfiles.mm +++ b/src/platform_adapters/macosx/configfiles.mm @@ -320,29 +320,40 @@ lineartime_t getFileModificationDate(const char *aFileName) #ifdef MOBOSX -// iPhone - - - +// iOS // get local device URI/ID bool getLocalDeviceID(string &aURI) { UIDevice *theDevice = [UIDevice currentDevice]; + NSString *deviceID = nil; #ifdef __IPHONE_6_0 if ([theDevice respondsToSelector:@selector(identifierForVendor)]) { // iOS 6 style identifier - aURI = [[[theDevice identifierForVendor] UUIDString] UTF8String]; + NSUUID *ifv = [theDevice identifierForVendor]; + if (ifv) { + deviceID = [ifv UUIDString]; + if (deviceID && [deviceID isEqualToString:@"00000000-0000-0000-0000-000000000000"]) { + deviceID = nil; // not valid, forget it! + } + } } - else #endif - { + if (deviceID==nil) { // Obtain old-style unique ID, in disguise because we should not use it any more // "uniqueIdentifier" SEL uiSEL = NSSelectorFromString([NSString stringWithFormat:@"un%c%cu%1xI%2xnti%1xier",'i','q',0x0E,0xDE,0x0F]); - aURI = [[theDevice performSelector:uiSEL] UTF8String]; + deviceID = [theDevice performSelector:uiSEL]; + } + // now return if ok + if (deviceID) { + aURI = [deviceID UTF8String]; + return true; + } + else { + aURI = "iOSDev-without-unique-ID"; + return false; // no unique device ID } - return true; } // getLocalDeviceID #else -- cgit v1.2.3 From b2949cc8a58bb0b8e0f349bef2df8dbaac8ba898 Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Mon, 1 Oct 2012 16:02:49 +0200 Subject: Fixes small ifdef issues which made compiling for server-only fail --- src/sysync/customimplds.cpp | 2 ++ src/sysync/syncagent.cpp | 51 +++++++++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/sysync/customimplds.cpp b/src/sysync/customimplds.cpp index 42f430c..988dd67 100755 --- a/src/sysync/customimplds.cpp +++ b/src/sysync/customimplds.cpp @@ -1115,7 +1115,9 @@ localstatus TCustomImplDS::dsAfterStateChange(TLocalEngineDSState aOldState,TLoc #endif // reset in case that we restart DeleteSyncSet(); + #ifdef BASED_ON_BINFILE_CLIENT fSyncSetLoaded=false; + #endif } // let inherited do its stuff as well return inherited::dsAfterStateChange(aOldState,aNewState); diff --git a/src/sysync/syncagent.cpp b/src/sysync/syncagent.cpp index df48cd6..11ea6ac 100755 --- a/src/sysync/syncagent.cpp +++ b/src/sysync/syncagent.cpp @@ -835,6 +835,31 @@ string TSyncAgent::getDeviceType(void) } // TSyncAgent::getDeviceType + +bool TSyncAgent::checkAllFromClientOnly() +{ + bool allFromClientOnly=false; + // Note: the map phase will not take place, if all datastores are in + // send-to-server-only mode and we are not in non-conformant old + // synthesis-compatible fCompleteFromClientOnly mode. + #ifdef SYSYNC_SERVER + if (!fCompleteFromClientOnly) + #endif + { + // let all local datastores know that message has ended + allFromClientOnly=true; + for (TLocalDataStorePContainer::iterator pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { + // check sync modes + if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) { + allFromClientOnly=false; + break; + } + } + } + return allFromClientOnly; +} + + #ifdef SYSYNC_CLIENT // initialize the client session and link it with the SML toolkit @@ -1539,26 +1564,6 @@ bool TSyncAgent::ClientMessageStarted(SmlSyncHdrPtr_t aContentP, TStatusCommand } // TSyncAgent::ClientMessageStarted -bool TSyncAgent::checkAllFromClientOnly() -{ - bool allFromClientOnly=false; - // Note: the map phase will not take place, if all datastores are in - // send-to-server-only mode and we are not in non-conformant old - // synthesis-compatible fCompleteFromClientOnly mode. - if (!fCompleteFromClientOnly) { - // let all local datastores know that message has ended - allFromClientOnly=true; - for (TLocalDataStorePContainer::iterator pos=fLocalDataStores.begin(); pos!=fLocalDataStores.end(); ++pos) { - // check sync modes - if ((*pos)->isActive() && (*pos)->getSyncMode()!=smo_fromclient) { - allFromClientOnly=false; - break; - } - } - } - return allFromClientOnly; -} - // determines new package states and sets fInProgress void TSyncAgent::ClientMessageEnded(bool aIncomingFinal) { @@ -3172,7 +3177,7 @@ TSyError TSyncAgent::ServerSessionStep(uInt16 &aStepCmd, TEngineProgressInfo *aI // - now done fServerEngineState = ses_done; // fall through to done state - + // Done state case ses_done : // session done, nothing happens any more @@ -3977,8 +3982,8 @@ static const TStructFieldInfo ServerParamFieldInfos[] = #endif { "restartsync", VALTYPE_INT8, true, 0, 0, &readRestartSync, &writeRestartSync }, // write into debug log - { "errorMsg", VALTYPE_TEXT, true, 0, 0, NULL, &writeErrorMsg }, - { "debugMsg", VALTYPE_TEXT, true, 0, 0, NULL, &writeDebugMsg }, + { "errorMsg", VALTYPE_TEXT, true, 0, 0, NULL, &writeErrorMsg }, + { "debugMsg", VALTYPE_TEXT, true, 0, 0, NULL, &writeDebugMsg }, }; // get table describing the fields in the struct -- cgit v1.2.3 From 25687680a8dd8e0aca781d99add42b47f6457fda Mon Sep 17 00:00:00 2001 From: Lukas Zeller Date: Tue, 2 Oct 2012 21:59:18 +0200 Subject: libsynthesis 3.4.0.45 --- src/global_options.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/global_options.h b/src/global_options.h index 80a1a99..a07ec3e 100644 --- a/src/global_options.h +++ b/src/global_options.h @@ -76,8 +76,8 @@ #endif #ifndef SYSYNC_BUILDNUMBER -#define SYSYNC_BUILDNUMBER 44 -#define SYSYNC_BUILDNUMBER_TXT "44" +#define SYSYNC_BUILDNUMBER 45 +#define SYSYNC_BUILDNUMBER_TXT "45" #endif -- cgit v1.2.3