5aa2c0d8f7e3fe8cdce3f3569060ec6f869bcf11
[platform/upstream/syncevolution.git] / src / synthesis / src / sysync / syncappbase.cpp
1 /*
2  *  TSyncAppBase
3  *    Base class for SySync applications, is supposed to exist
4  *    as singular object only, manages "global" things such
5  *    as config reading and session dispatching
6  *
7  *  Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
8  *
9  *  2002-03-06 : luz : Created
10  */
11
12 #include "prefix_file.h"
13
14 #include "sysync.h"
15 #include "syncappbase.h"
16 #include "scriptcontext.h"
17 #include "iso8601.h"
18 #include "syncagent.h"
19 #include "multifielditem.h" // in case we have no scripts...
20
21 #ifdef SYSER_REGISTRATION
22 #include "syserial.h"
23 #endif
24
25 #ifdef DIRECT_APPBASE_GLOBALACCESS
26 #include "sysync_glob_vars.h"
27 #endif
28
29
30
31 #include "global_progress.h" // globally accessible progress event posting
32
33 #ifndef ENGINE_LIBRARY
34
35 // can be called globally to post progress events
36 extern "C" int GlobalNotifyProgressEvent (
37   TProgressEventType aEventType,
38   sInt32 aExtra1,
39   sInt32 aExtra2,
40   sInt32 aExtra3
41 )
42 {
43   #ifdef PROGRESS_EVENTS
44   TSyncAppBase *baseP = getExistingSyncAppBase();
45   if (baseP) {
46     return baseP->NotifyProgressEvent(aEventType,NULL,aExtra1,aExtra2,aExtra3);
47   }
48   #endif
49   return true; // not aborted
50 } // GlobalNotifyProgressEvent
51
52 #endif // not ENGINE_LIBRARY
53
54
55 namespace sysync {
56
57 #ifndef HARDCODED_CONFIG
58
59 // SyncML encoding names for end user config
60 const char * const SyncMLEncodingNames[numSyncMLEncodings] = {
61   "undefined",
62   "wbxml",
63   "xml"
64 };
65
66 #endif
67
68 // SyncML encoding names for MIME type
69 const char * const SyncMLEncodingMIMENames[numSyncMLEncodings] = {
70   "undefined",
71   SYNCML_ENCODING_WBXML,
72   SYNCML_ENCODING_XML
73 };
74
75
76 #if !defined(HARDCODED_CONFIG) || defined(ENGINEINTERFACE_SUPPORT)
77
78 // platform string names for accessing them as config variables
79 const char * const PlatformStringNames[numPlatformStrings] = {
80   "platformvers", // version string of the current platform
81   "globcfg_path", // global system-wide config path (such as C:\Windows or /etc)
82   "loccfg_path", // local config path (such as exedir or user's dir)
83   "defout_path", // default path to writable directory to write logs and other output by default
84   "temp_path", // path where we can write temp files
85   "exedir_path", // path to directory where executable resides
86   "userdir_path", // path to the user's home directory for user-visible documents and files
87   "appdata_path", // path to the user's preference directory for this application
88   "prefs_path", // path to directory where all application prefs reside (not just mine)
89   "device_uri", // URI of the device (as from getDeviceInfo)
90   "device_name", // Name of the device (as from getDeviceInfo)
91   "user_name", // Name of the currently logged in user
92 };
93
94 #endif
95
96
97 #ifdef ENGINEINTERFACE_SUPPORT
98
99   #ifdef DIRECT_APPBASE_GLOBALACCESS
100   // Only for old-style targets that still use a global anchor
101
102   #ifdef ENGINE_LIBRARY
103     #error "Engine Library may NOT access appBase via global anchor any more!"
104   #endif
105
106
107   // With enginemodulebase support, the global anchor is the enginebase module,
108   // not syncappbase itself.
109
110   #define GET_SYNCAPPBASE (((TEngineInterface *)sysync_glob_anchor())->getSyncAppBase())
111
112   // get access to existing Sync app base object, NULL if none (i.e. if no TEngineInterface)
113   TSyncAppBase *getExistingSyncAppBase(void)
114   {
115     TEngineInterface *eng = (TEngineInterface *)sysync_glob_anchor();
116     return eng ? eng->getSyncAppBase() : NULL;
117   } // getExistingSyncAppBase
118
119   // global function, returns pointer to (singular) app base
120   // object. With EngineInterface, this is ALWAYS a member of
121   // TEngineInterface, so we create the global engineinterface if we don't have it
122   // already.
123   TSyncAppBase *getSyncAppBase(void)
124   {
125     TSyncAppBase *appBase = getExistingSyncAppBase();
126     if (appBase) return appBase;
127     // no appBase yet, we must create and anchor the TEngineInterface
128     #ifdef SYSYNC_CLIENT
129     ENGINE_IF_CLASS *engine = sysync::newClientEngine();
130     #else
131     ENGINE_IF_CLASS *engine = sysync::newServerEngine();
132     #endif
133     // we must init the engine to trigger creation of the appbase!
134     if (engine) engine->Init();
135     return GET_SYNCAPPBASE;
136   } // getSyncAppBase
137
138   TEngineInterface *getEngineInterface(void)
139   {
140     return (TEngineInterface *)sysync_glob_anchor();
141   } // getEngineInterface
142
143   // free Sync Session Dispatcher
144   void freeSyncAppBase(void)
145   {
146     TEngineInterface *eng = getEngineInterface();
147     if (eng) {
148       // kills interface, and also kills syncappbase in the process
149       delete eng;
150     }
151   }
152
153   #endif // DIRECT_APPBASE_GLOBALACCESS
154
155
156 #else // ENGINEINTERFACE_SUPPORT
157
158   // Old style without enginemodulebase: the global anchor is syncappbase
159
160   #define GET_SYNCAPPBASE ((TSyncAppBase *)sysync_glob_anchor())
161
162
163   // global function, returns pointer to (singular) app base
164   // object. If not yet existing, a new app base is created
165   TSyncAppBase *getSyncAppBase(void)
166   {
167     if (!sysync_glob_anchor()) {
168       // no dispatcher exists, create new one
169       // (using function which will create derived app
170       // specific dispatcher)
171       sysync_glob_setanchor(newSyncAppBase());
172     }
173     // dispatcher now exists, use it
174     return GET_SYNCAPPBASE;
175   } // getSyncAppBase
176
177
178   // get access to existing Sync app base object, NULL if none
179   TSyncAppBase *getExistingSyncAppBase(void)
180   {
181     return GET_SYNCAPPBASE;
182   } // getExistingSyncAppBase
183
184
185   // free Sync Session Dispatcher
186   void freeSyncAppBase(void)
187   {
188     if (sysync_glob_anchor()) {
189       // object exists, kill it now
190       delete GET_SYNCAPPBASE;
191       sysync_glob_setanchor(NULL);
192     }
193   }
194
195 #endif // not ENGINEINTERFACE_SUPPORT
196
197
198 #ifdef SYDEBUG
199
200 // static routines for accessing appbase logs from UI_Call_In/DB_Callback
201
202 extern "C" void AppBaseLogDebugPuts(void *aCallbackRef, const char *aText)
203 {
204   if (aCallbackRef) {
205     POBJDEBUGPUTSX(static_cast<TSyncAppBase *>(aCallbackRef),DBG_DBAPI+DBG_PLUGIN,aText);
206   }
207 } // AppBaseLogDebugPuts
208
209
210 extern "C" void AppBaseLogDebugExotic(void *aCallbackRef, const char *aText)
211 {
212   if (aCallbackRef) {
213     POBJDEBUGPUTSX(static_cast<TSyncAppBase *>(aCallbackRef),DBG_DBAPI+DBG_PLUGIN+DBG_EXOTIC,aText);
214   }
215 } // AppBaseLogDebugExotic
216
217
218 extern "C" void AppBaseLogDebugBlock(void *aCallbackRef, const char *aTag, const char *aDesc, const char *aAttrText )
219 {
220   if (aCallbackRef) {
221     bool collapsed=false;
222     if (aTag && aTag[0]=='-') { aTag++; collapsed=true; }
223     static_cast<TSyncAppBase *>(aCallbackRef)->getDbgLogger()->DebugOpenBlock(aTag,aDesc,collapsed,"%s",aAttrText);
224   }
225 } // AppBaseLogDebugBlock
226
227
228 extern "C" void AppBaseLogDebugEndBlock(void *aCallbackRef, const char *aTag)
229 {
230   if (aCallbackRef) {
231     if (aTag && aTag[0]=='-') aTag++;
232     static_cast<TSyncAppBase *>(aCallbackRef)->getDbgLogger()->DebugCloseBlock(aTag);
233   }
234 } // AppBaseLogDebugEndBlock
235
236
237 extern "C" void AppBaseLogDebugEndThread(void *aCallbackRef)
238 {
239   if (aCallbackRef) {
240     static_cast<TSyncAppBase *>(aCallbackRef)->getDbgLogger()->DebugThreadOutputDone(true); // remove thread record for global threads
241   }
242 } // AppBaseLogDebugEndThread
243
244 #endif
245
246
247 // root config constructor
248 TRootConfig::TRootConfig(TSyncAppBase *aSyncAppBaseP) :
249   TRootConfigElement(aSyncAppBaseP),
250   fCommConfigP(NULL),
251   fAgentConfigP(NULL),
252   fDatatypesConfigP(NULL)
253   #ifdef SCRIPT_SUPPORT
254   , fScriptConfigP(NULL)
255   #endif
256   #ifdef SYDEBUG
257   , fDebugConfig("debug",this) // init static debug config member
258   #endif
259 {
260   // config date is unknown so far
261   fConfigDate=0;
262   #ifndef HARDCODED_CONFIG
263   // string for identifying config file in logs
264   fConfigIDString="<none>";
265   #endif
266 } // TRootConfig::TRootConfig
267
268
269 TRootConfig::~TRootConfig()
270 {
271   // clear linked
272   if (fAgentConfigP) delete fAgentConfigP;
273   if (fDatatypesConfigP) delete fDatatypesConfigP;
274   if (fCommConfigP) delete fCommConfigP;
275   #ifdef SCRIPT_SUPPORT
276   if (fScriptConfigP) delete fScriptConfigP;
277   #endif
278 } // TRootConfig::~TRootConfig
279
280
281 void TRootConfig::clear(void)
282 {
283   // root config variables
284   // - init PUT suppression
285   fNeverPutDevinf=false;
286   // - init message size
287   fLocalMaxMsgSize=DEFAULT_MAXMSGSIZE;
288   fLocalMaxObjSize=DEFAULT_MAXOBJSIZE;
289   // - system time zone
290   fSystemTimeContext=TCTX_SYSTEM; // default to automatic detection
291   #ifdef ENGINEINTERFACE_SUPPORT
292   // - default identification
293         fMan.clear();
294   fMod.clear();
295         #endif
296   // - init device limit
297   #ifdef CUSTOMIZABLE_DEVICES_LIMIT
298   fConcurrentDeviceLimit=CONCURRENT_DEVICES_LIMIT;
299   #endif
300   // remove, (re-)create and clear linked config branches (debug last to allow dbg output while clearing)
301   // - global scripting config
302   #ifdef SCRIPT_SUPPORT
303   if (fScriptConfigP) delete fScriptConfigP;
304   fScriptConfigP= new TScriptConfig(this);
305   if (fScriptConfigP) fScriptConfigP->clear();
306   #endif
307   // - communication config
308   if (fCommConfigP) delete fCommConfigP;
309   installCommConfig();
310   if (fCommConfigP) fCommConfigP->clear();
311   // - datatypes registry config
312   if (fDatatypesConfigP) delete fDatatypesConfigP;
313   installDatatypesConfig();
314   if (fDatatypesConfigP) fDatatypesConfigP->clear();
315   // - agent config
316   if (fAgentConfigP) delete fAgentConfigP;
317   installAgentConfig();
318   if (fAgentConfigP) fAgentConfigP->clear();
319   // clear embedded debug config (as the last action)
320   #ifdef SYDEBUG
321   fDebugConfig.clear();
322   #endif
323   // clear inherited
324   inherited::clear();
325 } // TRootConfig::clear
326
327
328 // save app state (such as settings in datastore configs etc.)
329 void TRootConfig::saveAppState(void)
330 {
331   if (fAgentConfigP) fAgentConfigP->saveAppState();
332   if (fDatatypesConfigP) fDatatypesConfigP->saveAppState();
333   if (fCommConfigP) fCommConfigP->saveAppState();
334 } // TRootConfig::saveAppState
335
336
337 // MUST be called after creating config to load (or pre-load) variable parts of config
338 // such as binfile profiles. If aDoLoose==false, situations, where existing config
339 // is detected but cannot be re-used will return an error. With aDoLoose==true, config
340 // files etc. are created even if it means a loss of data.
341 localstatus TRootConfig::loadVarConfig(bool aDoLoose)
342 {
343   // only agent may load variable config
344   if (fAgentConfigP)
345     return fAgentConfigP->loadVarConfig(aDoLoose);
346   else
347     return LOCERR_NOCFG; // no config yet
348 } // TRootConfig::loadVarConfig
349
350
351
352
353 #ifndef HARDCODED_CONFIG
354
355 // root config element parsing
356 bool TRootConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
357 {
358   // debug
359   if (strucmp(aElementName,"debug")==0) {
360     #ifdef SYDEBUG
361     // let static TDebugConfig member handle it
362     expectChildParsing(fDebugConfig);
363     #else
364     ReportError(false,"No debugging features available in this version");
365     expectAll(); // no debug code, simply ignore settings
366     #endif
367   }
368   // config ID/date
369   else if (strucmp(aElementName,"configdate")==0)
370     expectTimestamp(fConfigDate);
371   else if (strucmp(aElementName,"configidstring")==0)
372     expectMacroString(fConfigIDString);
373   else
374   #ifdef ENGINEINTERFACE_SUPPORT
375   // - product identification (in devInf)
376   if (strucmp(aElementName,"manufacturer")==0)
377     expectMacroString(fMan);
378   else if (strucmp(aElementName,"model")==0)
379     expectMacroString(fMod);
380   else if (strucmp(aElementName,"HardwareVersion")==0)
381     expectMacroString(fHwV);
382   else if (strucmp(aElementName,"FirmwareVersion")==0)
383     expectMacroString(fFwV);
384   else if (strucmp(aElementName,"DeviceType")==0)
385     expectMacroString(fDevTyp);
386   else
387         #endif
388   // config variables
389   if (strucmp(aElementName,"configvar")==0) {
390     const char *nam = getAttr(aAttributes,"name");
391     if (!nam)
392       return fail("Missing 'name' attribute in 'configvar'");
393     string val;
394     if (!getAttrExpanded(aAttributes,"value",val,false))
395       return fail("Missing 'value' attribute in 'configvar'");
396     getSyncAppBase()->setConfigVar(nam,val.c_str());
397     expectEmpty();
398   }
399   // options
400   else if (strucmp(aElementName,"neverputdevinf")==0)
401     expectBool(fNeverPutDevinf);
402   else if (strucmp(aElementName,"maxmsgsize")==0)
403     expectUInt32(fLocalMaxMsgSize);
404   else if (strucmp(aElementName,"maxobjsize")==0)
405     expectUInt32(fLocalMaxObjSize);
406   else if (strucmp(aElementName,"maxconcurrentsessions")==0) {
407     #ifdef CUSTOMIZABLE_DEVICES_LIMIT
408     expectInt32(fConcurrentDeviceLimit);
409     #else
410     expectAll(); // no configurable limit, simply ignore contents
411     #endif
412   }
413   // time zones
414   else if (strucmp(aElementName,"definetimezone")==0)
415     expectVTimezone(getSyncAppBase()->getAppZones()); // definition of custom time zone
416   else if (strucmp(aElementName,"systemtimezone")==0)
417     expectTimezone(fSystemTimeContext);
418   // license
419   else if (strucmp(aElementName,"licensename")==0) {
420     #ifdef SYSER_REGISTRATION
421     expectString(fLicenseName);
422     #else
423     expectAll(); // simply ignore contents for versions w/o registration
424     #endif
425   }
426   else if (strucmp(aElementName,"licensecode")==0) {
427     #ifdef SYSER_REGISTRATION
428     expectString(fLicenseCode);
429     #else
430     expectAll(); // simply ignore contents for versions w/o registration
431     #endif
432   }
433   #ifdef SCRIPT_SUPPORT
434   else if (strucmp(aElementName,"scripting")==0) {
435     // let linked TScriptConfig handle it
436     expectChildParsing(*fScriptConfigP);
437   }
438   #endif
439   else
440   // Agent: Server or client
441   if (
442         (IS_CLIENT && strucmp(aElementName,"client")==0) ||
443                 (IS_SERVER && strucmp(aElementName,"server")==0)
444   ) {
445     // Agent config
446     if (!fAgentConfigP) return false;
447     return parseAgentConfig(aAttributes, aLine);
448   }
449   // Transport
450   else if (strucmp(aElementName,"transport")==0) {
451     // transport config
452     if (!fCommConfigP) return false;
453     if (!parseCommConfig(aAttributes, aLine)) {
454       // not a transport we can understand, simply ignore
455       expectAll();
456     }
457   }
458   else if (strucmp(aElementName,"datatypes")==0) {
459     // datatypes (type registry) config
460     if (!fDatatypesConfigP) return false;
461     return parseDatatypesConfig(aAttributes, aLine);
462   }
463   else {
464     // invalid element
465     return false;
466   }
467   // ok
468   return true;
469 } // TRootConfig::localStartElement
470 #endif
471
472
473 // resolve (finish after all data is parsed)
474 void TRootConfig::localResolve(bool aLastPass)
475 {
476   // make sure static debug element is resolved so
477   // eventual debug information created by resolving other
478   // elements go to the correct locations/files
479   // Note: in XML configs, the debug element is resolved immediately
480   //       after parsing (has fResolveImmediately set) and will
481   //       not be re-resolved here unless the element was not parsed at all.
482   #if defined(SYDEBUG) && !defined(SYSYNC_TOOL)
483   // - for SysyTool, do not resolve here as we don't want to see all of the
484   //   DBG blurb on the screen created by resolving the config
485   fDebugConfig.Resolve(aLastPass);
486   #endif
487   // set zone for system if one was defined explicitly
488   if (!TCTX_IS_SYSTEM(fSystemTimeContext) && !TCTX_IS_UNKNOWN(fSystemTimeContext)) {
489     getSyncAppBase()->getAppZones()->predefinedSysTZ = fSystemTimeContext;
490     getSyncAppBase()->getAppZones()->ResetCache(); // make sure next query for SYSTEM tz will get new set zone
491   }
492
493   // MaxMessagesize must have a reasonable size
494   if (fLocalMaxMsgSize<512) {
495     SYSYNC_THROW(TConfigParseException("<maxmsgsize> must be at least 512 bytes"));
496   }
497   // make sure we have the registration info vars updated
498   #ifdef APP_CAN_EXPIRE
499   getSyncAppBase()->updateAppExpiry();
500   #elif defined(SYSER_REGISTRATION)
501   getSyncAppBase()->isRegistered();
502   #endif
503   // make sure linked elements are resolved
504   #ifdef SCRIPT_SUPPORT
505   if (fScriptConfigP) fScriptConfigP->Resolve(aLastPass);
506   #endif
507   if (fAgentConfigP) fAgentConfigP->Resolve(aLastPass);
508   if (fDatatypesConfigP) fDatatypesConfigP->Resolve(aLastPass);
509   if (fCommConfigP) fCommConfigP->Resolve(aLastPass);
510   // finally, get rid of macros (all scripts are now read)
511   #ifdef SCRIPT_SUPPORT
512   if (fScriptConfigP && aLastPass) fScriptConfigP->clearmacros();
513   #endif
514   #if defined(SYDEBUG) && defined(SYSYNC_TOOL)
515   // - for SysyTool, resolve now, where resolving dbg output has gone /dev/null already
516   fDebugConfig.Resolve(aLastPass);
517   #endif
518   // try to resolve variable config here, but without forcing new one
519   if (aLastPass) {
520         loadVarConfig(false);
521   }
522 } // TRootConfig::localResolve
523
524
525 // Base datatype config
526
527 // init defaults
528 void TDataTypeConfig::clear(void)
529 {
530   // clear properties
531   fTypeName.erase(); // no type
532   fTypeVersion.erase(); // no version
533   #ifdef ZIPPED_BINDATA_SUPPORT
534   fZippedBindata=false;
535   fZipCompressionLevel=-1; // valid range is 0-9, invalid value will select Z_DEFAULT_COMPRESSION
536   #endif
537   fBinaryParts=false; // no binary parts
538   fUseUTF16=false; // no UTF-16/Unicode translation
539   fMSBFirst=false; // default to Intel byte order for UTF16
540   // clear inherited
541   inherited::clear();
542 } // TMIMEDirTypeConfig::clear
543
544
545 #ifdef CONFIGURABLE_TYPE_SUPPORT
546
547 // config element parsing
548 bool TDataTypeConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
549 {
550   // checking the elements
551   if (strucmp(aElementName,"typestring")==0)
552     expectString(fTypeName);
553   else if (strucmp(aElementName,"versionstring")==0)
554     expectString(fTypeVersion);
555   else if (strucmp(aElementName,"binaryparts")==0)
556     expectBool(fBinaryParts);
557   #ifdef ZIPPED_BINDATA_SUPPORT
558   else if (strucmp(aElementName,"zippedbindata")==0)
559     expectBool(fZippedBindata);
560   else if (strucmp(aElementName,"zipcompressionlevel")==0)
561     expectInt16(fZipCompressionLevel);
562   #endif
563   else if (strucmp(aElementName,"unicodedata")==0)
564     expectBool(fUseUTF16);
565   else if (strucmp(aElementName,"bigendian")==0)
566     expectBool(fMSBFirst);
567   // - none known here
568   else
569     return false; // base class is TConfigElement
570   // ok
571   return true;
572 } // TDataTypeConfig::localStartElement
573
574
575 // resolve
576 void TDataTypeConfig::localResolve(bool aLastPass)
577 {
578   // check
579   if (aLastPass) {
580     // Note: type strings might be set explicitly or implicitly by derived classes
581     // Note2: all types must have a version (SCTS will fail without). For example
582     //        Starfish's text/plain notes have Version "1.0"
583     if (fTypeName.empty() || fTypeVersion.empty() )
584       SYSYNC_THROW(TConfigParseException("datatype must have non-empty 'typestring' and 'versionstring'"));
585   }
586 } // TDataTypeConfig::localResolve
587
588 #endif
589
590
591
592 // Datatype registry
593
594 TDatatypesConfig::TDatatypesConfig(const char* aName, TConfigElement *aParentElement) :
595   TConfigElement(aName,aParentElement)
596 {
597   clear();
598 } // TDatatypesConfig::TDatatypesConfig
599
600
601 TDatatypesConfig::~TDatatypesConfig()
602 {
603   clear();
604 } // TDatatypesConfig::~TDatatypesConfig
605
606
607 // init defaults
608 void TDatatypesConfig::clear(void)
609 {
610   // remove datatypes
611   TDataTypesList::iterator pos;
612   for(pos=fDataTypesList.begin();pos!=fDataTypesList.end();pos++)
613     delete *pos;
614   fDataTypesList.clear();
615   // clear inherited
616   inherited::clear();
617 } // TDatatypesConfig::clear
618
619
620 TDataTypeConfig *TDatatypesConfig::getDataType(const char *aName)
621 {
622   TDataTypesList::iterator pos;
623   for(pos=fDataTypesList.begin();pos!=fDataTypesList.end();pos++) {
624     if (strucmp((*pos)->getName(),aName)==0) {
625       // found
626       return *pos;
627     }
628   }
629   return NULL; // not found
630 } // TDatatypesConfig::getDataType
631
632
633 #ifdef CONFIGURABLE_TYPE_SUPPORT
634
635 // config element parsing
636 bool TDatatypesConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
637 {
638   // checking the elements
639   if (strucmp(aElementName,"datatype")==0) {
640     // definition of a new data type
641     // - get name
642     const char *nam = getAttr(aAttributes,"name");
643     if (!nam)
644       return fail("Missing 'name' attribute in 'datatype'");
645     // - get basetype
646     const char *basetype = getAttr(aAttributes,"basetype");
647     if (!basetype)
648       return fail("Missing 'basetype' attribute in 'datatype'");
649     // - call global function to get correct TDataTypeConfig derived object
650     TDataTypeConfig *datatypeP = getSyncAppBase()->getRootConfig()->newDataTypeConfig(nam,basetype,this);
651     if (!datatypeP)
652       return fail("Unknown basetype '%s'",basetype);
653     // - save in list
654     fDataTypesList.push_back(datatypeP);
655     // - let element handle parsing
656     expectChildParsing(*datatypeP);
657   }
658   // - none known here
659   else
660     return inherited::localStartElement(aElementName,aAttributes,aLine);
661   // ok
662   return true;
663 } // TDatatypesConfig::localStartElement
664
665 #endif
666
667
668 // resolve
669 void TDatatypesConfig::localResolve(bool aLastPass)
670 {
671   // resolve all types in list
672   TDataTypesList::iterator pos;
673   for(pos=fDataTypesList.begin();pos!=fDataTypesList.end();pos++)
674     (*pos)->Resolve(aLastPass);
675   // resolve inherited
676   inherited::localResolve(aLastPass);
677 } // TDatatypesConfig::localResolve
678
679
680
681 #ifdef SYDEBUG
682
683 // debug config constructor
684 TDebugConfig::TDebugConfig(const char *aElementName, TConfigElement *aParentElementP) :
685   TConfigElement(aElementName,aParentElementP)
686 {
687   // do not call clear(), because this is virtual!
688 } // TDebugConfig::TDebugConfig
689
690
691 void TDebugConfig::clear(void)
692 {
693   // set defaults
694   fGlobalDbgLoggerOptions.clear(); // set logger options to defaults
695   fSessionDbgLoggerOptions.clear(); // set logger options to defaults
696   fDebug = DEFAULT_DEBUG; // if <>0 (and #defined SYDEBUG), debug output is generated, value is used as mask
697   fMsgDump = DEFAULT_MSGDUMP; // if set (and #defined MSGDUMP), messages sent and received are logged;
698   fSingleGlobLog = false; // create a separate global log per app start
699   fSingleSessionLog = false; // create separate session logs
700   fTimedSessionLogNames = true; // add session start time into file name
701   fLogSessionsToGlobal = false; // use separate file(s) for session log
702   fXMLtranslate = DEFAULT_XMLTRANSLATE; // if set, communication will be translated to XML and logged
703   fSimMsgRead = DEFAULT_SIMMSGREAD; // if set (and #defined SIMMSGREAD), simulated input with "i_" prefixed incoming messages are supported
704   fGlobalDebugLogs = DEFAULT_GLOBALDEBUGLOGS;
705   fSessionDebugLogs = DEFAULT_SESSIONDEBUGLOGS;
706   if (getPlatformString(pfs_defout_path,fDebugInfoPath))
707     makeOSDirPath(fDebugInfoPath);
708   else
709     fDebugInfoPath.erase(); // none
710   // make sure that defaults are applied a first time NOW, BEFORE reading first config
711   localResolve(true);
712   #ifndef SYSYNC_TOOL
713   // and make sure final resolve takes place early when <debug> element finishes parsing
714   // (but not for SYSYNC_TOOL, where we want no debug output at all during config read & resolve!)
715   fResolveImmediately = true;
716   #endif
717   // clear inherited
718   inherited::clear();
719 } // TDebugConfig::clear
720
721
722 // resolve (finish after all data is parsed)
723 void TDebugConfig::localResolve(bool aLastPass)
724 {
725   if (aLastPass) {
726     #ifdef SYDEBUG
727     // we have debug
728     #ifndef HARDCODED_CONFIG
729       // XML config - user options settings are parsed into fSessionDbgLoggerOptions
730       // - by default, global logging has same options as configured for session...
731       fGlobalDbgLoggerOptions = fSessionDbgLoggerOptions;
732       // ...but we have a few hard-coded things for global logging:
733       #ifdef MULTITHREAD_PIPESERVER
734         // - for pipe server, global logs should be per-thread
735         fGlobalDbgLoggerOptions.fFlushMode=dbgflush_flush; // flush every log line
736         fGlobalDbgLoggerOptions.fSubThreadMode=dbgsubthread_separate; // separate per thread
737         fGlobalDbgLoggerOptions.fTimestampForAll=true; // timestamp for every message
738       #else
739         if (IS_SERVER) {
740           // - global log for ISAPI/XPT *Servers* is always in openclose mode (possibly multiple processes accessing it)
741           fGlobalDbgLoggerOptions.fFlushMode=dbgflush_openclose; // open and close for every log line
742         }
743         fGlobalDbgLoggerOptions.fSubThreadMode=dbgsubthread_linemix; // mix in one file
744         #ifdef MULTI_THREAD_SUPPORT
745           fGlobalDbgLoggerOptions.fThreadIDForAll=true; // thread ID for each message
746         #endif
747       #endif
748     #endif
749     // initialize global debug logging options
750     getSyncAppBase()->fAppLogger.setMask(fDebug); // set initial debug mask from config
751     getSyncAppBase()->fAppLogger.setEnabled(fGlobalDebugLogs); // init from config
752     getSyncAppBase()->fAppLogger.setOptions(&fGlobalDbgLoggerOptions);
753     // install outputter, but only if not yet installed in an earlier invocation. We only want ONE log per engine instantiation!
754     if (!getSyncAppBase()->fAppLogger.outputEstablished()) {
755       getSyncAppBase()->fAppLogger.installOutput(getSyncAppBase()->newDbgOutputter(true)); // install the output object (and pass ownership!)
756       getSyncAppBase()->fAppLogger.setDebugPath(fDebugInfoPath.c_str()); // global log all in one file
757       getSyncAppBase()->fAppLogger.appendToDebugPath(fGlobalDbgLoggerOptions.fBasename.empty() ?
758                                                      TARGETID :
759                                                      fGlobalDbgLoggerOptions.fBasename.c_str());
760       if (fSingleGlobLog) {
761         // One single log - in this case, we MUST append to current log
762         fGlobalDbgLoggerOptions.fAppend=true;
763       }
764       else {
765         // create a new global log for each app start
766         getSyncAppBase()->fAppLogger.appendToDebugPath("_");
767         string t;
768         TimestampToISO8601Str(t, getSyncAppBase()->getSystemNowAs(TCTX_UTC), TCTX_UTC, false, false);
769         getSyncAppBase()->fAppLogger.appendToDebugPath(t.c_str());
770         getSyncAppBase()->fAppLogger.appendToDebugPath("_global");
771       }
772     }
773     // define this as the main thread
774     getSyncAppBase()->fAppLogger.DebugDefineMainThread();
775     #endif
776   }
777 }; // TDebugConfig::localResolve
778
779
780 #ifndef HARDCODED_CONFIG
781
782 // debug option (combination) names
783 const char * const debugOptionNames[numDebugOptions] = {
784   // current categories
785   "hot",
786   "error",
787   "data",
788   "admin",
789   "syncml",
790   "remoteinfo",
791   "parse",
792   "generate",
793   "rtk_sml",
794   "rtk_xpt",
795   "session",
796   "lock",
797   "objinst",
798   "transp",
799   "scripts",
800   "profiling",
801   "rest",
802
803   // flags mostly (not always) used in combination with some of the basic categories
804   "userdata",
805   "dbapi",
806   "plugin",
807   "filter",
808   "match",
809   "conflict",
810   "details",
811   "exotic",
812   "expressions",
813
814   // useful sets
815   "all",
816   "minimal",
817   "normal",
818   "extended",
819   "maximal",
820   "db",
821   "syncml_rtk",
822
823   // old ones
824   "items",
825   "cmd",
826   "devinf",
827   "dataconf"
828 };
829
830 const uInt32 debugOptionMasks[numDebugOptions] = {
831   // current categories
832   DBG_HOT,
833   DBG_ERROR,
834   DBG_DATA,
835   DBG_ADMIN,
836   DBG_PROTO,
837   DBG_REMOTEINFO,
838   DBG_PARSE,
839   DBG_GEN,
840   DBG_RTK_SML,
841   DBG_RTK_XPT,
842   DBG_SESSION,
843   DBG_LOCK,
844   DBG_OBJINST,
845   DBG_TRANSP,
846   DBG_SCRIPTS,
847   DBG_PROFILE,
848   DBG_REST,
849
850   // flags mostly (not always) used in combination with some of the basic categories
851   DBG_USERDATA,
852   DBG_DBAPI,
853   DBG_PLUGIN,
854   DBG_FILTER,
855   DBG_MATCH,
856   DBG_CONFLICT,
857   DBG_DETAILS,
858   DBG_EXOTIC,
859   DBG_SCRIPTEXPR,
860
861   // useful sets
862   DBG_ALL,
863   DBG_MINIMAL,
864   DBG_NORMAL,
865   DBG_EXTENDED,
866   DBG_MAXIMAL,
867   DBG_ALLDB,
868   DBG_RTK_SML+DBG_RTK_XPT,
869
870   // old names that are mapped to new masks
871   DBG_DATA, // formerly: DBG_ITEMS
872   DBG_PROTO, // formerly: DBG_CMD
873   DBG_REMOTEINFO, // formerly: DBG_DEVINF
874   DBG_PARSE+DBG_GEN // formerly: DBG_DATACONV
875 };
876
877
878
879 uInt32 TDebugConfig::str2DebugMask(const char **aAttributes)
880 {
881   expectEmpty(); // enable may not have content
882   // process arguments
883   const char* dbgopt = getAttr(aAttributes,"option");
884   if (!dbgopt) {
885     ReportError(false,"debug enable/disable, missing 'option' attribute");
886   }
887   sInt16 k;
888   if (StrToEnum(debugOptionNames,numDebugOptions,k,dbgopt)) {
889     return debugOptionMasks[k];
890   }
891   ReportError(false,"unknown debug option '%s'",dbgopt);
892   return 0;
893 } // TDebugConfig::str2DebugMask
894
895
896 // debug config element parsing
897 bool TDebugConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
898 {
899   // checking the elements
900   if (strucmp(aElementName,"mask")==0)
901     expectUInt32(fDebug);
902   else if (strucmp(aElementName,"enable")==0)
903     fDebug = fDebug | str2DebugMask(aAttributes);
904   else if (strucmp(aElementName,"disable")==0)
905     fDebug = fDebug & ~str2DebugMask(aAttributes);
906   else if (strucmp(aElementName,"logpath")==0)
907     expectPath(fDebugInfoPath);
908   else if (strucmp(aElementName,"msgdump")==0)
909     expectBool(fMsgDump);
910   else if (strucmp(aElementName,"xmltranslate")==0)
911     expectBool(fXMLtranslate);
912   else if (strucmp(aElementName,"simmsgread")==0)
913     expectBool(fSimMsgRead);
914   else if (strucmp(aElementName,"sessionlogs")==0)
915     expectBool(fSessionDebugLogs);
916   else if (strucmp(aElementName,"globallogs")==0)
917     expectBool(fGlobalDebugLogs);
918   // options for TDebugLogger
919   else if (strucmp(aElementName,"logformat")==0)
920     expectEnum(sizeof(fSessionDbgLoggerOptions.fOutputFormat),&fSessionDbgLoggerOptions.fOutputFormat,DbgOutFormatNames,numDbgOutFormats);
921   else if (strucmp(aElementName,"folding")==0)
922     expectEnum(sizeof(fSessionDbgLoggerOptions.fFoldingMode),&fSessionDbgLoggerOptions.fFoldingMode,DbgFoldingModeNames,numDbgFoldingModes);
923   else if (strucmp(aElementName,"indentstring")==0)
924     expectCString(fSessionDbgLoggerOptions.fIndentString);
925   else if (strucmp(aElementName,"fileprefix")==0)
926     expectRawString(fSessionDbgLoggerOptions.fCustomPrefix);
927   else if (strucmp(aElementName,"filesuffix")==0)
928     expectRawString(fSessionDbgLoggerOptions.fCustomSuffix);
929   else if (strucmp(aElementName,"filename")==0)
930     expectRawString(fSessionDbgLoggerOptions.fBasename);
931   else if (strucmp(aElementName,"logflushmode")==0)
932     expectEnum(sizeof(fSessionDbgLoggerOptions.fFlushMode),&fSessionDbgLoggerOptions.fFlushMode,DbgFlushModeNames,numDbgFlushModes);
933   else if (strucmp(aElementName,"appendtoexisting")==0)
934     expectBool(fSessionDbgLoggerOptions.fAppend);
935   else if (strucmp(aElementName,"timestamp")==0)
936     expectBool(fSessionDbgLoggerOptions.fTimestampStructure);
937   else if (strucmp(aElementName,"timestampall")==0)
938     expectBool(fSessionDbgLoggerOptions.fTimestampForAll);
939   else if (strucmp(aElementName,"showthreadid")==0)
940     expectBool(fSessionDbgLoggerOptions.fThreadIDForAll);
941   else if (strucmp(aElementName,"subthreadmode")==0)
942     expectEnum(sizeof(fSessionDbgLoggerOptions.fSubThreadMode),&fSessionDbgLoggerOptions.fSubThreadMode,DbgSubthreadModeNames,numDbgSubthreadModes);
943   else if (strucmp(aElementName,"subthreadbuffersize")==0)
944     expectUInt32(fSessionDbgLoggerOptions.fSubThreadBufferMax);
945   else if (strucmp(aElementName,"singlegloballog")==0)
946     expectBool(fSingleGlobLog);
947   else if (strucmp(aElementName,"singlesessionlog")==0)
948     expectBool(fSingleSessionLog);
949   else if (strucmp(aElementName,"timedsessionlognames")==0)
950     expectBool(fTimedSessionLogNames);
951   else if (strucmp(aElementName,"logsessionstoglobal")==0)
952     expectBool(fLogSessionsToGlobal);
953   else
954     return false; // invalid element
955   return true;
956 } // TDebugConfig::localStartElement
957
958 #endif
959 #endif
960
961
962 #ifndef ENGINE_LIBRARY
963
964 // in engine library, all output must be in context of an engineInterface/appBase
965
966 // Debug output routines
967 //
968 // If a fDebugLogOutputFunc callback is defined,
969 // it will be used for output, otherwise global gDebugLogPath
970 // file will be written
971
972 uInt32 getDbgMask(void)
973 {
974   #ifdef SYDEBUG
975   TSyncAppBase *appBase = getExistingSyncAppBase();
976   if (!appBase) return 0; // no appbase -> no debug
977   return appBase->getDbgMask();
978   #else
979   return 0;
980   #endif
981 } // getDebugMask
982
983
984 TDebugLogger *getDbgLogger(void)
985 {
986   #ifdef SYDEBUG
987   TSyncAppBase *appBase = getExistingSyncAppBase();
988   if (!appBase) return NULL; // no appbase -> no debuglogger
989   return appBase->getDbgLogger();
990   #else
991   return NULL;
992   #endif
993 } // getDbgLogger
994
995
996 // non-class DebugPuts
997 void DebugPuts(uInt32 mask, const char *text)
998 {
999   #ifdef SYDEBUG
1000   // use global debug channel of appBase for non-object-context output
1001   TSyncAppBase *appBase = getExistingSyncAppBase();
1002   if (!appBase || appBase->getDbgMask()==0) return; // no appbase or debug off -> no output
1003   TDebugLogger *dbgLogger = appBase->getDbgLogger();
1004   if (!dbgLogger) return;
1005   dbgLogger->DebugPuts(mask,text,0,false);
1006   #endif
1007 } // DebugPuts
1008
1009
1010 // non-class print to debug channel
1011 void DebugVPrintf(uInt32 mask, const char *format, va_list args)
1012 {
1013   #ifdef SYDEBUG
1014   // use global debug channel of appBase for non-object-context output
1015   TSyncAppBase *appBase = getExistingSyncAppBase();
1016   if (!appBase || appBase->getDbgMask()==0) return; // no appbase or debug off -> no output
1017   TDebugLogger *dbgLogger = appBase->getDbgLogger();
1018   if (!dbgLogger) return;
1019   dbgLogger->DebugVPrintf(mask,format,args);
1020   #endif
1021 } // DebugVPrintf
1022
1023
1024 // non-class print to debug channel
1025 void DebugPrintf(const char *text, ...)
1026 {
1027   #ifdef SYDEBUG
1028   va_list args;
1029   if (PDEBUGMASK) {
1030     va_start(args, text);
1031     DebugVPrintf(DBG_TRANSP, text,args);
1032     va_end(args);
1033   } // if (PDEBUGMASK)
1034   #endif
1035 } // DebugPrintf
1036
1037
1038 void smlLibPrint(const char *text, ...)
1039 {
1040   #ifdef SYDEBUG
1041   va_list args;
1042   va_start(args, text);
1043   DebugVPrintf(DBG_RTK_SML,text,args);
1044   va_end(args);
1045   #endif
1046 } // smlLibPrint
1047
1048
1049 void smlLibVprintf(const char *format, va_list va)
1050 {
1051   #ifdef SYDEBUG
1052   DebugVPrintf(DBG_RTK_SML,format,va);
1053   #endif
1054 } // smlLibVprintf
1055
1056
1057 // entry point for SyncML-Toolkit with
1058 // #define TRACE_TO_STDOUT
1059 void localOutput(const char *aFormat, va_list aArgs)
1060 {
1061   #ifdef SYDEBUG
1062   NCDEBUGVPRINTFX(DBG_RTK_SML,aFormat,aArgs);
1063   #endif
1064 }
1065
1066
1067 #endif // not ENGINE_LIBRARY
1068
1069
1070 // Console printout. Only enabled when defined(CONSOLEINFO)
1071
1072 // non-class print to console
1073 void ConsolePrintf(const char *text, ...)
1074 {
1075   #ifdef CONSOLEINFO
1076   const sInt16 maxmsglen=1024;
1077   char msg[maxmsglen];
1078   va_list args;
1079
1080   msg[0]='\0';
1081   va_start(args, text);
1082   // assemble the message string
1083   vsnprintf(msg, maxmsglen, text, args);
1084   va_end(args);
1085   // write the string
1086   ConsolePuts(msg);
1087   #endif
1088 } // sysyncConsolePrintf
1089
1090
1091 // non-class Console output
1092 void ConsolePuts(const char *text)
1093 {
1094   #ifdef CONSOLEINFO
1095   // show on app's console equivalent
1096   AppConsolePuts(text);
1097   #endif // console enabled
1098 } // sysyncConsolePuts
1099
1100
1101
1102 // TSyncAppBase
1103 // ============
1104
1105
1106 /* SyncML toolkit callback function declarations */
1107
1108 extern "C" {
1109   /* message callbacks */
1110   static Ret_t smlStartMessageCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncHdrPtr_t pContent);
1111   static Ret_t smlEndMessageCallback(InstanceID_t id, VoidPtr_t userData, Boolean_t final);
1112   /* grouping commands */
1113   static Ret_t smlStartSyncCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncPtr_t pContent);
1114   static Ret_t smlEndSyncCallback(InstanceID_t id, VoidPtr_t userData);
1115   #ifdef ATOMIC_RECEIVE  /* these callbacks are NOT included in the Toolkit lite version */
1116     static Ret_t smlStartAtomicCallback(InstanceID_t id, VoidPtr_t userData, SmlAtomicPtr_t pContent);
1117     static Ret_t smlEndAtomicCallback(InstanceID_t id, VoidPtr_t userData);
1118   #endif
1119   #ifdef SEQUENCE_RECEIVE
1120     static Ret_t smlStartSequenceCallback(InstanceID_t id, VoidPtr_t userData, SmlSequencePtr_t pContent);
1121     static Ret_t smlEndSequenceCallback(InstanceID_t id, VoidPtr_t userData);
1122   #endif
1123   /* Sync Commands */
1124   static Ret_t smlAddCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAddPtr_t pContent);
1125   static Ret_t smlAlertCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent);
1126   static Ret_t smlDeleteCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlDeletePtr_t pContent);
1127   static Ret_t smlGetCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlGetPtr_t pContent);
1128   static Ret_t smlPutCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlPutPtr_t pContent);
1129   #ifdef MAP_RECEIVE
1130     static Ret_t smlMapCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMapPtr_t pContent);
1131   #endif
1132   #ifdef RESULT_RECEIVE
1133     static Ret_t smlResultsCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlResultsPtr_t pContent);
1134   #endif
1135   static Ret_t smlStatusCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlStatusPtr_t pContent);
1136   static Ret_t smlReplaceCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlReplacePtr_t pContent);
1137   /* othe commands */
1138   #ifdef COPY_RECEIVE  /* these callbacks are NOT included in the Toolkit lite version */
1139     static Ret_t smlCopyCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlCopyPtr_t param);
1140   #endif
1141   static Ret_t smlMoveCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMovePtr_t param);
1142   #ifdef EXEC_RECEIVE
1143     static Ret_t smlExecCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlExecPtr_t pContent);
1144   #endif
1145   #ifdef SEARCH_RECEIVE
1146     static Ret_t smlSearchCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlSearchPtr_t pContent);
1147   #endif
1148   /* Other Callbacks */
1149   static Ret_t smlHandleErrorCallback(InstanceID_t id, VoidPtr_t userData);
1150   static Ret_t smlTransmitChunkCallback(InstanceID_t id, VoidPtr_t userData);
1151
1152   /* print callback */
1153   void smlPrintCallback(String_t outputString);
1154 } // extern "C" declaration
1155
1156
1157 // constructor
1158 TSyncAppBase::TSyncAppBase() :
1159   fIsServer(false),
1160   fDeleting(false),
1161   fConfigP(NULL),
1162   fRequestCount(0),
1163   #ifdef PROGRESS_EVENTS
1164   fProgressEventFunc(NULL),
1165   #endif
1166   #ifdef ENGINEINTERFACE_SUPPORT
1167   fEngineInterfaceP(NULL),
1168   #else
1169   fMasterPointer(NULL),
1170   #endif
1171   fApiInterModuleContext(0)
1172   // reset all callbacks
1173   #ifdef SYDEBUG
1174   // app logger
1175   ,fAppLogger(&fAppZones)
1176   #endif
1177 {
1178   // at the moment of creation, this is now the SyncAppBase
1179   // (set it here already to allow getSyncAppBase() from derived constructors)
1180   #ifdef ENGINEINTERFACE_SUPPORT
1181     #ifdef DIRECT_APPBASE_GLOBALACCESS
1182       getEngineInterface()->setSyncAppBase(this); // set the link so getSyncAppBase() via EngineInterface works immediately
1183     #endif
1184   #else
1185     #ifdef ENGINE_LIBRARY
1186       #error "DIRECT_APPBASE_GLOBALACCESS is not allowed with ENGINE_LIBRARY"
1187     #endif
1188     sysync_glob_setanchor(this);
1189   #endif
1190   #ifdef MD5_TEST_FUNCS
1191   md5::dotest();
1192   #endif
1193   // init profiling
1194   TP_INIT(fTPInfo);
1195   TP_START(fTPInfo,TP_general);
1196   #ifdef EXPIRES_AFTER_DATE
1197   // - get current time
1198   sInt16 y,m,d;
1199   lineartime2date(getSystemNowAs(TCTX_UTC),&y,&m,&d);
1200   // - calculate scrambled version thereof
1201   fScrambledNow=
1202     ((sInt32)y-1720l)*12l*42l+
1203     ((sInt32)m-1)*42l+
1204     ((sInt32)d+7l);
1205   /*
1206   #define SCRAMBLED_EXPIRY_VALUE \
1207    (EXPIRY_DAY+7)+ \
1208    (EXPIRY_MONTH-1)*42+ \
1209    (EXPIRY_YEAR-1720)*12*42
1210   */
1211   #endif
1212   #ifdef APP_CAN_EXPIRE
1213   fAppExpiryStatus=LOCERR_OK; // do not compare here, would be too easy
1214   #endif
1215   #if defined(SYDEBUG) && defined(HARDCODED_CONFIG)
1216   fConfigFilePath="<hardcoded config>";
1217   #endif
1218   #ifdef SYSER_REGISTRATION
1219   // make sure that license is standard (no phone home, no expiry) in case there is NO license at all
1220   fRegLicenseType=0;
1221   fRelDuration=0;
1222   fRegDuration=0;
1223   #endif
1224
1225   // TODO: put this somewhere where the return code can be checked and
1226   // reported to the user of TSyncAppBase
1227   fAppZones.initialize();
1228
1229   /* %%%%
1230   string zoneName;
1231   sInt16 stdBias;
1232   sInt16 dstBias;
1233   lineartime_t stdStart;
1234   lineartime_t dstStart;
1235   bool ok = getSystemTimeZone(zoneName, stdBias, dstBias, stdStart, dstStart);
1236   printf("getSystemTimeZone: name=%s, stdBias=%hd, dstBias=%hd, stdStart=%lld, dstStart=%lld\n", zoneName.c_str(), stdBias, dstBias, stdStart, dstStart);
1237   */
1238
1239 } // TSyncAppBase::TSyncAppBase
1240
1241
1242 // destructor
1243 TSyncAppBase::~TSyncAppBase()
1244 {
1245   fDeleting=true; // flag deletion to block calling critical (virtual) methods
1246   #if !defined(ENGINEINTERFACE_SUPPORT) || defined(DIRECT_APPBASE_GLOBALACCESS)
1247   sysync_glob_setanchor(NULL);
1248   #endif
1249   // stop and show profiling info
1250   TP_STOP(fTPInfo);
1251   #ifdef TIME_PROFILING
1252   if (PDEBUGMASK & DBG_PROFILE) {
1253     sInt16 i;
1254     PDEBUGPRINTFX(DBG_PROFILE,("Non-Session CPU usage statistics: (system/user)"));
1255     // sections
1256     for (i=0; i<numTPTypes; i++) {
1257       PNCDEBUGPRINTFX(DBG_PROFILE,(
1258         "- %-20s : (%10ld/%10ld) ms",
1259         TP_TypeNames[i],
1260         TP_GETSYSTEMMS(fTPInfo,(TTP_Types)i),
1261         TP_GETUSERMS(fTPInfo,(TTP_Types)i)
1262       ));
1263     }
1264     // total
1265     PNCDEBUGPRINTFX(DBG_PROFILE,(
1266       "- %-20s : (%10ld/%10ld) ms",
1267       "TOTAL",
1268       TP_GETTOTALSYSTEMMS(fTPInfo),
1269       TP_GETTOTALUSERMS(fTPInfo)
1270     ));
1271   }
1272   #endif
1273   // delete the config now
1274   #ifdef SYDEBUG
1275   // - but first make sure applogger does not refer to it any more
1276   fAppLogger.setOptions(NULL);
1277   #endif
1278   // - now delete
1279   if (fConfigP) delete fConfigP;
1280 } // TSyncAppBase::~TSyncAppBase
1281
1282
1283
1284 #ifndef HARDCODED_CONFIG
1285
1286 // get config variables
1287 bool TSyncAppBase::getConfigVar(cAppCharP aVarName, string &aValue)
1288 {
1289   // look up in user-defined variables first
1290   TStringToStringMap::iterator pos = fConfigVars.find(aVarName);
1291   if (pos!=fConfigVars.end()) {
1292     aValue = (*pos).second;
1293     return true; // found in user defined vars
1294   }
1295   // check some globals
1296   if (strucmp(aVarName,"version")==0) {
1297     // version
1298     aValue = SYSYNC_FULL_VERSION_STRING;
1299     return true;
1300   }
1301   if (strucmp(aVarName,"hexversion")==0) {
1302     // string-comparable version as 8-digit hex MMmmrrbb (Major, minor, rev, build)
1303     StringObjPrintf(aValue,"%02X%02X%02X%02X",SYSYNC_VERSION_MAJOR,SYSYNC_VERSION_MINOR,SYSYNC_SUBVERSION,SYSYNC_BUILDNUMBER);
1304     return true;
1305   }
1306   if (strucmp(aVarName,"manufacturer")==0) {
1307         aValue = getManufacturer();
1308     return true;
1309   }
1310   if (strucmp(aVarName,"model")==0) {
1311         aValue = getModel();
1312     return true;
1313   }
1314   if (strucmp(aVarName,"variant")==0) {
1315     // variant
1316     #if SYSER_VARIANT_CODE==SYSER_VARIANT_STD
1317     aValue = "STD";
1318     #elif SYSER_VARIANT_CODE==SYSER_VARIANT_PRO
1319     aValue  ="PRO";
1320     #elif SYSER_VARIANT_CODE==SYSER_VARIANT_CUSTOM
1321     aValue = "CUSTOM";
1322     #elif SYSER_VARIANT_CODE==SYSER_VARIANT_DEMO
1323     aValue = "DEMO";
1324     #else
1325     aValue = "unknown";
1326     #endif
1327     return true;
1328   }
1329   if (strucmp(aVarName,"productcode")==0) {
1330     #ifdef SYSER_PRODUCT_CODE
1331     StringObjPrintf(aValue,"%d",SYSER_PRODUCT_CODE);
1332     #else
1333     aValue = "unknown";
1334     #endif
1335     return true;
1336   }
1337   if (strucmp(aVarName,"extraid")==0) {
1338     #ifdef SYSER_PRODUCT_CODE
1339     StringObjPrintf(aValue,"%d",SYSER_EXTRA_ID);
1340     #else
1341     aValue = "unknown";
1342     #endif
1343     return true;
1344   }
1345   if (strucmp(aVarName,"platformname")==0) {
1346     aValue = SYSYNC_PLATFORM_NAME;
1347     return true;
1348   }
1349   // look up in platform strings
1350   int plsId;
1351   for (plsId=0; plsId<numPlatformStrings; plsId++) {
1352     if (strucmp(aVarName,PlatformStringNames[plsId])==0) {
1353       // get platform string
1354       return getPlatformString((TPlatformStringID)plsId,aValue);
1355     }
1356   }
1357   // not found
1358   return false;
1359 } // TSyncAppBase::getConfigVar
1360
1361
1362 // set config variable
1363 bool TSyncAppBase::setConfigVar(cAppCharP aVarName, cAppCharP aNewValue)
1364 {
1365   // set user-defined config variable
1366   fConfigVars[aVarName] = aNewValue;
1367   return true;
1368 } // TSyncAppBase::setConfigVar
1369
1370
1371 // expand config vars in string
1372 bool TSyncAppBase::expandConfigVars(string &aString, sInt8 aCfgVarExp, TConfigElement *aCfgElement, cAppCharP aElementName)
1373 {
1374   string::size_type n,n2;
1375
1376   if (aCfgVarExp<=0 || aString.empty()) return true; // no expansion
1377   if (aCfgElement && !aElementName) aElementName="*Unknown*";
1378   n=0;
1379   while(true) {
1380     n = aString.find("$(",n);
1381     if (n==string::npos)
1382       break; // no more macros
1383     // found macro name start - now search end
1384     n+=2; // position after $( lead-in
1385     n2 = aString.find(")",n);
1386     if (n2!=string::npos) {
1387       // macro name found
1388       string vn,vv;
1389       vn.assign(aString,n,n2-n); // name
1390       if (getConfigVar(vn.c_str(),vv)) {
1391         if (aCfgVarExp==2) {
1392           // check for recursion loop
1393           vn.insert(0,"$("); vn.append(")");
1394           if (vv.find(vn,0)!=string::npos) {
1395             if (aCfgElement) aCfgElement->ReportError(true,"Recursive config variable $(%s) in <%s>",vn.c_str(),aElementName);
1396             n=n2+1; // do not expand
1397             continue;
1398           }
1399         }
1400         // found value - substitute
1401         n-=2; // substitute beginning with leadin
1402         n2+=1; // include closing paranthesis
1403         aString.replace(n,n2-n,vv);
1404         if (aCfgVarExp<2) {
1405           // do not allow recursive macro expansion
1406           n+=vv.size(); // continue searching past substituted chars
1407         }
1408       }
1409       else {
1410         // not found - leave macro as-is
1411         if (aCfgElement) aCfgElement->ReportError(false,"Undefined config variable $(%s) in <%s>",vn.c_str(),aElementName);
1412         n=n2+1; // continue search after closing paranthesis
1413       }
1414     }
1415     else {
1416       if (aCfgElement) aCfgElement->ReportError(false,"Unterminated $(xxx)-style config variable in <%s>",aElementName);
1417     }
1418   }
1419   return true;
1420 } // TSyncAppBase::expandConfigVars
1421
1422 #endif // not HARDCODED_CONFIG
1423
1424
1425
1426 #ifdef HARDCODED_CONFIG
1427
1428
1429 localstatus TSyncAppBase::initHardcodedConfig(void)
1430 {
1431   localstatus err;
1432
1433   // initialize for receiving new config
1434   fConfigP->clear();
1435   // now call initializer in derived root config
1436   err=fConfigP->createHardcodedConfig();
1437   if (err!=LOCERR_OK) return err;
1438   // make sure it gets all resolved
1439   fConfigP->ResolveAll();
1440   // is ok now
1441   return LOCERR_OK;
1442 } // TSyncAppBase::initHardcodedConfig
1443
1444
1445 #else
1446
1447
1448 // report config errors
1449 void TSyncAppBase::ConferrPrintf(const char *text, ...)
1450 {
1451   const sInt16 maxmsglen=1024;
1452   char msg[maxmsglen];
1453   va_list args;
1454
1455   msg[0]='\0';
1456   va_start(args, text);
1457   // assemble the message string
1458   vsnprintf(msg, maxmsglen, text, args);
1459   va_end(args);
1460   // output config errors
1461   ConferrPuts(msg);
1462 } // TSyncAppBase::ConferrPrintf
1463
1464
1465 // report config errors to appropriate channel
1466 void TSyncAppBase::ConferrPuts(const char *msg)
1467 {
1468   #ifdef ENGINE_LIBRARY
1469     // engine variant
1470     string filename;
1471     // - get config var to see where we should put config errors
1472     if (!getConfigVar("conferrpath", filename))
1473       return; // no output defined for config errors
1474     // a config error path is defined
1475     if (strucmp(filename.c_str(),"console")==0) {
1476       // put message directly to what is supposed to be the console
1477       AppConsolePuts(msg);
1478       return; // done
1479     }
1480   #else // ENGINE_LIBRARY
1481     // old variant - output to predefined path
1482     #ifdef CONSOLEINFO
1483     ConsolePuts(msg);
1484     return;
1485     #elif defined(__PALM_OS__)
1486     return; // PalmOS has no file output
1487     #endif
1488     // prepare file name
1489     string filename;
1490     if (!getPlatformString(pfs_defout_path,filename)) return;
1491     makeOSDirPath(filename);
1492     filename+=CONFERRPREFIX;
1493     filename+=TARGETID;
1494     filename+=CONFERRSUFFIX;
1495   #endif // not ENGINE_LIBRARY
1496   #ifndef __PALM_OS__
1497   // now write to file
1498   FILE * logfile=fopen(filename.c_str(),"a");
1499   if (logfile) {
1500     string ts;
1501     StringObjTimestamp(ts,getSystemNowAs(TCTX_SYSTEM));
1502     ts.append(": ");
1503     fputs(ts.c_str(),logfile);
1504     fputs(msg,logfile);
1505     fputs("\n",logfile);
1506     fclose(logfile);
1507   }
1508   #endif // __PALM_OS__
1509 } // TSyncAppBase::ConferrPuts
1510
1511
1512 /* config reading */
1513
1514 // XML parser's "userdata"
1515 typedef struct {
1516   XML_Parser parser;
1517   TRootConfigElement *rootconfig;
1518 } TXMLUserData;
1519
1520
1521 // prototypes
1522 extern "C" {
1523   static void startElement(void *userData, const char *name, const char **atts);
1524   static void charData(void *userData, const XML_Char *s, int len);
1525   static void endElement(void *userData, const char *name);
1526 }
1527
1528 static localstatus checkErrors(TRootConfigElement *aRootConfigP,XML_Parser aParser)
1529 {
1530   const char *errmsg = aRootConfigP->getErrorMsg();
1531   if (errmsg) {
1532     aRootConfigP->getSyncAppBase()->ConferrPrintf(
1533       "%s at line %ld col %ld",
1534       errmsg,
1535       (sInt32)XML_GetCurrentLineNumber(aParser),
1536       (sInt32)XML_GetCurrentColumnNumber(aParser)
1537     );
1538     aRootConfigP->resetError();
1539     return aRootConfigP->getFatalError(); // return when fatal
1540   }
1541   else
1542     return LOCERR_OK; // no (fatal) error
1543 } // checkErrors
1544
1545
1546 // callback for expat
1547 static void startElement(void *userData, const char *name, const char **atts)
1548 {
1549   TRootConfigElement *cfgP = static_cast<TXMLUserData *>(userData)->rootconfig;
1550   XML_Parser parser = static_cast<TXMLUserData *>(userData)->parser;
1551   SYSYNC_TRY {
1552     cfgP->startElement(name,atts,XML_GetCurrentLineNumber(parser));
1553   }
1554   SYSYNC_CATCH (exception &e)
1555     cfgP->ReportError(true,"Exception in StartElement: %s",e.what());
1556   SYSYNC_ENDCATCH
1557   // check for errors
1558   checkErrors(cfgP,parser);
1559 } // startElement
1560
1561
1562 // callback for expat
1563 static void charData(void *userData, const XML_Char *s, int len)
1564 {
1565   TRootConfigElement *cfgP = static_cast<TXMLUserData *>(userData)->rootconfig;
1566   XML_Parser parser = static_cast<TXMLUserData *>(userData)->parser;
1567   SYSYNC_TRY {
1568     cfgP->charData(s,len);
1569   }
1570   SYSYNC_CATCH (exception &e)
1571     cfgP->ReportError(true,"Exception in charData: %s",e.what());
1572   SYSYNC_ENDCATCH
1573   // check for errors
1574   checkErrors(cfgP,parser);
1575 } // charData
1576
1577
1578
1579 // callback for expat
1580 static void endElement(void *userData, const char *name)
1581 {
1582   TRootConfigElement *cfgP = static_cast<TXMLUserData *>(userData)->rootconfig;
1583   XML_Parser parser = static_cast<TXMLUserData *>(userData)->parser;
1584   SYSYNC_TRY {
1585     cfgP->endElement(name);
1586   }
1587   SYSYNC_CATCH (exception &e)
1588     cfgP->ReportError(true,"Exception in endElement: %s",e.what());
1589   SYSYNC_ENDCATCH
1590   // check for errors
1591   checkErrors(cfgP,parser);
1592 } // endElement
1593
1594
1595 // config stream reading
1596 localstatus TSyncAppBase::readXMLConfigStream(TXMLConfigReadFunc aReaderFunc, void *aContext)
1597 {
1598   localstatus fatalerr;
1599
1600   // clear (reset to default) all config
1601   TP_DEFIDX(last);
1602   TP_SWITCH(last,fTPInfo,TP_configread);
1603   MP_SHOWCURRENT(DBG_HOT,"start reading config");
1604
1605   // initialize for new config
1606   fConfigP->clear();
1607   fConfigP->ResetParsing();
1608   // read XML
1609   appChar buf[CONFIG_READ_BUFSIZ];
1610   sInt16 done;
1611   // - create parser
1612   XML_Parser parser = XML_ParserCreate(NULL);
1613   SYSYNC_TRY {
1614     // init user data struct
1615     TXMLUserData userdata;
1616     userdata.parser=parser;
1617     userdata.rootconfig=fConfigP;
1618     // pass pointer to root config here
1619     XML_SetUserData(parser, &userdata);
1620     XML_SetElementHandler(parser, startElement, endElement);
1621     XML_SetCharacterDataHandler(parser, charData);
1622     do {
1623       bufferIndex len=0;
1624       // callback reader func
1625       if (!(aReaderFunc)(buf,CONFIG_READ_BUFSIZ,&len,aContext)) {
1626         fConfigP->setFatalError(LOCERR_CFGREAD); // this is also fatal
1627         ConferrPrintf(
1628           "Error reading from config"
1629         );
1630         break;
1631       }
1632       // %%% changed stop criterium to smooth EngineInterface API
1633       //done = len < CONFIG_READ_BUFSIZ;
1634       done = len == 0;
1635       if (!XML_Parse(parser, buf, len, done)) {
1636         fConfigP->setFatalError(LOCERR_CFGPARSE); // this is also fatal
1637         ConferrPrintf(
1638           "%s at line %ld col %ld",
1639           XML_ErrorString(XML_GetErrorCode(parser)),
1640           (sInt32)XML_GetCurrentLineNumber(parser),
1641           (sInt32)XML_GetCurrentColumnNumber(parser)
1642         );
1643         break;
1644       }
1645       else
1646         if (fConfigP->getFatalError()) break;
1647     } while (!done);
1648     XML_ParserFree(parser);
1649     if (!fConfigP->getFatalError()) {
1650       // now resolve
1651       fConfigP->ResolveAll();
1652       // display resolve error, if any
1653       const char *msg = fConfigP->getErrorMsg();
1654       if (msg) {
1655         // this is fatal only if error was thrown in Resolve.
1656         // Some warnings might be set with ReportError and
1657         // will not cause abort.
1658         ConferrPrintf(msg);
1659       }
1660     }
1661     // check if ok or not
1662     if ((fatalerr=fConfigP->getFatalError())!=LOCERR_OK) {
1663       // config failed, reset
1664       // %%% do not clear the config here
1665       //fConfigP->clear();
1666       ConferrPrintf(
1667         "Fatal error %hd, no valid configuration could be read from XML file",
1668         (sInt16)fatalerr
1669       );
1670       TP_START(fTPInfo,last);
1671       return fatalerr;
1672     }
1673   }
1674   SYSYNC_CATCH (...)
1675     XML_ParserFree(parser);
1676     fConfigP->clear();
1677     fConfigP->setFatalError(LOCERR_CFGPARSE); // this is also fatal
1678     ConferrPrintf(
1679       "Exception while parsing XML, no valid configuration"
1680     );
1681     TP_START(fTPInfo,last);
1682     return LOCERR_CFGPARSE;
1683   SYSYNC_ENDCATCH
1684   TP_START(fTPInfo,last);
1685   #if defined(APP_CAN_EXPIRE) && defined(RELEASE_YEAR) && defined(SYSER_REGISTRATION)
1686   if (fAppExpiryStatus==LOCERR_TOONEW) {
1687     ConferrPrintf(
1688       "License is invalid for software released on or after %04d-%02d-01",
1689       (fRelDuration / 12) + 2000,
1690       (fRelDuration) % 12 + 1
1691     );
1692   }
1693   #endif
1694   #ifdef APP_CAN_EXPIRE
1695   if (fAppExpiryStatus!=LOCERR_OK) {
1696     if (fAppExpiryStatus==LOCERR_EXPIRED) {
1697       ConferrPrintf("Time-limited License expired");
1698     } else {
1699       ConferrPrintf("Missing or bad License Information");
1700     }
1701     ConferrPrintf("Please contact Synthesis AG to obtain new license information");
1702     fConfigP->setFatalError(fAppExpiryStatus); // this is also fatal
1703     return fAppExpiryStatus;
1704   }
1705   #endif
1706   #ifdef SYSER_REGISTRATION
1707   // check if config should have locked sections with a certain CRC value
1708   sInt16 daysleft;
1709   uInt32 shouldcrc;
1710   string s;
1711   bool ok=false;
1712   // - get restriction string from licensed info
1713   ok = getAppEnableInfo(daysleft, NULL, &s)==LOCERR_OK;
1714   string restrid,restrval;
1715   const char *p = s.c_str(); // start of license info string
1716   while (ok && (p=getLicenseRestriction(p,restrid,restrval))!=NULL) {
1717     if (restrid=="l") { // lock CRC
1718       StrToULong(restrval.c_str(),shouldcrc);
1719       ok=shouldcrc==fConfigP->getConfigLockCRC();
1720     }
1721   }
1722   if (!ok) {
1723     ConferrPrintf("Locked config sections are not valid");
1724     fConfigP->setFatalError(LOCERR_BADREG); // this is also fatal
1725     return LOCERR_BADREG;
1726   }
1727   #endif
1728   // ok if done
1729   MP_SHOWCURRENT(DBG_HOT,"finished reading config");
1730   return LOCERR_OK;
1731 } // TSyncAppBase::readXMLConfigStream
1732
1733
1734 #ifdef CONSTANTXML_CONFIG
1735
1736 // stream reader for reading from compiled-in text constant
1737 static int _CALLING_ ConstantReader(
1738   sysync::appCharP aBuffer,
1739   sysync::bufferIndex aMaxSize,
1740   sysync::bufferIndex *aReadCharsP,
1741   void *aContext // const char **
1742 )
1743 {
1744   // get cursor
1745   const char *readptr = *((const char **)aContext);
1746   // read from constant
1747   if (!readptr) return false;
1748   size_t len = strlen(readptr);
1749   if (len>aMaxSize) len=aMaxSize;
1750   // - copy
1751   if (len>0) strncpy(aBuffer,readptr,len);
1752   // - update cursor
1753   *((const char **)aContext)=readptr+len;
1754   *aReadCharsP=len; // return number of chars actually read
1755   return true; // successful
1756 } // ConstantReader
1757
1758
1759 // config file reading from hard-wired Constant
1760 localstatus TSyncAppBase::readXMLConfigConstant(const char *aConstantXML)
1761 {
1762   const char *aCursor = aConstantXML;
1763   return readXMLConfigStream(&ConstantReader, &aCursor);
1764   #ifdef SYDEBUG
1765   // signal where config came from
1766   fConfigFilePath="<XML read from string constant>";
1767   #endif
1768 } // TSyncAppBase::readXMLConfigConstant
1769
1770
1771 #endif
1772
1773
1774 // stream reader for cfile
1775 static int _CALLING_ CFileReader(
1776   appCharP aBuffer,
1777   bufferIndex aMaxSize,
1778   bufferIndex *aReadCharsP,
1779   void *aContext // FILE *
1780 )
1781 {
1782   FILE *cfgfile = (FILE *)aContext;
1783   // read from file
1784   size_t len = fread(aBuffer, 1, aMaxSize, cfgfile);
1785   if (len<0) {
1786     if (!feof(cfgfile))
1787       return appFalse; // not EOF, other error: failed
1788     len=0; // nothing read, end of file
1789   }
1790   *aReadCharsP=len; // return number of chars actually read
1791   return appTrue; // successful
1792 } // CFileReader
1793
1794
1795 // config file reading from C file
1796 localstatus TSyncAppBase::readXMLConfigCFile(FILE *aCfgFile)
1797 {
1798   return readXMLConfigStream(&CFileReader, (void *)aCfgFile);
1799 } // TSyncAppBase::readXMLConfigCFile
1800
1801
1802 // read config from file path
1803 localstatus TSyncAppBase::readXMLConfigFile(cAppCharP aFilePath)
1804 {
1805   localstatus fatalerr;
1806
1807   // open file
1808   FILE* cfgfile=fopen(aFilePath,"r");
1809   if (cfgfile) {
1810     // yes, there is a config file. Get its date
1811     getRootConfig()->fConfigDate = getFileModificationDate(aFilePath);
1812     // now read the config file (and possibly override fConfigDate)
1813     if ((fatalerr=readXMLConfigCFile(cfgfile))!=LOCERR_OK) {
1814       fclose(cfgfile);
1815       PDEBUGPRINTFX(DBG_ERROR,(
1816         "==== Fatal Error %hd reading config file '%s'",
1817         fatalerr,
1818         aFilePath
1819       ));
1820       return fatalerr; // config file with fatal errors
1821     }
1822     fclose(cfgfile);
1823     #if defined(SYDEBUG) && !defined(SYSYNC_TOOL)
1824     string t;
1825     StringObjTimestamp(t,getRootConfig()->fConfigDate);
1826     // now write settings to log
1827     PDEBUGPRINTFX(DBG_HOT,(
1828       "==== Config file '%s' read: Last Config Change=%s, Debug=0x%08lX, Lock=%ld",
1829       aFilePath,
1830       t.c_str(),
1831       (long)PDEBUGMASK,
1832       (long)fConfigP->getConfigLockCRC()
1833     ));
1834     PDEBUGPRINTFX(DBG_HOT,(
1835       "==== Config file ID = '%s'",
1836       getRootConfig()->fConfigIDString.c_str()
1837     ));
1838     #endif
1839     CONSOLEPRINTF(("- Config file read from '%s'",aFilePath));
1840     #ifdef SYDEBUG
1841     if (getRootConfig()->fDebugConfig.fSessionDebugLogs || getRootConfig()->fDebugConfig.fGlobalDebugLogs)
1842       CONSOLEPRINTF(("- Debug log path: %s",getRootConfig()->fDebugConfig.fDebugInfoPath.c_str()));
1843     // signal where config came from
1844     fConfigFilePath=aFilePath;
1845     #endif
1846     // config found
1847     return LOCERR_OK;
1848   }
1849   else {
1850     DEBUGPRINTFX(DBG_ERROR,("==== No Config file found under '%s'",aFilePath));
1851     // reset config to defaults
1852     fConfigP->clear();
1853     return LOCERR_NOCFGFILE;
1854   }
1855 } // TSyncAppBase::readXMLConfigFile
1856
1857
1858 #ifndef ENGINE_LIBRARY
1859
1860 // standard reading of config on predefined paths
1861 localstatus TSyncAppBase::readXMLConfigStandard(const char *aConfigFileName, bool aOnlyGlobal, bool aAbsolute)
1862 {
1863   localstatus fatalerr;
1864
1865   SYSYNC_TRY {
1866     // - get path where config file should be
1867     string cfgfilename;
1868     sInt16 attempt= aOnlyGlobal ? 1 : 0; // if only global, don't look in exe dir (e.g. for ISAPI modules)
1869     while (true) {
1870       // count attempt
1871       attempt++;
1872       if (aAbsolute) {
1873         if (attempt>1) {
1874           // no config file found
1875           return LOCERR_NOCFGFILE;
1876         }
1877         // just use path as it is
1878         cfgfilename=aConfigFileName;
1879       }
1880       else {
1881         // try paths
1882         if (attempt==1) {
1883           // first check if there's a local copy
1884           if (!getPlatformString(pfs_loccfg_path,cfgfilename))
1885             continue; // none found, try next
1886         }
1887         else if (attempt==2) {
1888           // if no local config file, look for a global one
1889           if (!getPlatformString(pfs_globcfg_path,cfgfilename))
1890             continue; // none found, try next
1891         }
1892         else {
1893           CONSOLEPRINTF(("- No config file found, using default settings"));
1894           // reset config to defaults
1895           fConfigP->clear();
1896           return LOCERR_NOCFGFILE; // no config
1897         }
1898         // add file name
1899         makeOSDirPath(cfgfilename,false);
1900         cfgfilename+=aConfigFileName;
1901       }
1902       fatalerr=readXMLConfigFile(cfgfilename.c_str());
1903       if (fatalerr==LOCERR_OK)
1904         break; // config found, ok
1905     } // attempt loop
1906   } // try
1907   SYSYNC_CATCH (...)
1908     ConferrPrintf("Fatal Error (exception) while reading config file");
1909     return LOCERR_CFGREAD;
1910   SYSYNC_ENDCATCH
1911   return LOCERR_OK; // ok
1912 } // TSyncAppBase::readXMLConfigStandard
1913
1914 #endif // ENGINE_LIBRARY
1915
1916 #endif // HARDCODED_CONFIG
1917
1918
1919 /* progress sevent notification */
1920
1921 #ifdef PROGRESS_EVENTS
1922
1923 // event generator
1924 bool TSyncAppBase::NotifyProgressEvent(
1925   TProgressEventType aEventType,
1926   TLocalDSConfig *aDatastoreID,
1927   sInt32 aExtra1,
1928   sInt32 aExtra2,
1929   sInt32 aExtra3
1930 )
1931 {
1932   TProgressEvent theevent;
1933
1934   if (fProgressEventFunc) {
1935     // there is a progress event callback
1936     // - prepare event
1937     theevent.eventtype=aEventType;
1938     theevent.datastoreID=aDatastoreID;
1939     theevent.extra=aExtra1;
1940     theevent.extra2=aExtra2;
1941     theevent.extra3=aExtra3;
1942     // - invoke callback (returns false if aborted)
1943     return fProgressEventFunc(
1944       theevent,
1945       fProgressEventContext
1946     );
1947   }
1948   // if no callback, never abort
1949   return true; // ok, no abort
1950 } // TSyncAppBase::NotifyProgressEvent
1951
1952 #endif
1953
1954
1955
1956 /* logfile outputs */
1957
1958
1959 /* SyncML toolkit callback handlers */
1960
1961
1962 Ret_t TSyncAppBase::EndMessage(VoidPtr_t userData, Boolean_t aFinal)
1963 {
1964   if (!userData) return SML_ERR_WRONG_PARAM;
1965   // call Session
1966   SYSYNC_TRY {
1967     return ((TSyncSession *) userData)->EndMessage(aFinal);
1968   }
1969   SYSYNC_CATCH (exception &e)
1970     return HandleDecodingException((TSyncSession *)userData,"EndMessage",&e);
1971   SYSYNC_ENDCATCH
1972   SYSYNC_CATCH (...)
1973     return HandleDecodingException((TSyncSession *)userData,"EndMessage",NULL);
1974   SYSYNC_ENDCATCH
1975 } // TSyncAppBase::EndMessage
1976
1977
1978 Ret_t TSyncAppBase::StartSync(VoidPtr_t userData, SmlSyncPtr_t aContentP)
1979 {
1980   if (!userData) return SML_ERR_WRONG_PARAM;
1981   // call Session
1982   SYSYNC_TRY {
1983     return ((TSyncSession *) userData)->StartSync(aContentP);
1984   }
1985   SYSYNC_CATCH (exception &e)
1986     return HandleDecodingException((TSyncSession *)userData,"StartSync",&e);
1987   SYSYNC_ENDCATCH
1988   SYSYNC_CATCH (...)
1989     return HandleDecodingException((TSyncSession *)userData,"StartSync",NULL);
1990   SYSYNC_ENDCATCH
1991 } // TSyncAppBase::StartSync
1992
1993
1994 Ret_t TSyncAppBase::EndSync(VoidPtr_t userData)
1995 {
1996   if (!userData) return SML_ERR_WRONG_PARAM;
1997   // call Session
1998   SYSYNC_TRY {
1999     return ((TSyncSession *) userData)->EndSync();
2000   }
2001   SYSYNC_CATCH (exception &e)
2002     return HandleDecodingException((TSyncSession *)userData,"EndSync",&e);
2003   SYSYNC_ENDCATCH
2004   SYSYNC_CATCH (...)
2005     return HandleDecodingException((TSyncSession *)userData,"EndSync",NULL);
2006   SYSYNC_ENDCATCH
2007 } // TSyncAppBase::EndSync
2008
2009
2010 #ifdef SEQUENCE_RECEIVE
2011 Ret_t TSyncAppBase::StartSequence(VoidPtr_t userData, SmlSequencePtr_t aContentP)
2012 {
2013   if (!userData) return SML_ERR_WRONG_PARAM;
2014   // call Session
2015   SYSYNC_TRY {
2016     return ((TSyncSession *) userData)->StartSequence(aContentP);
2017   }
2018   SYSYNC_CATCH (exception &e)
2019     return HandleDecodingException((TSyncSession *)userData,"StartSequence",&e);
2020   SYSYNC_ENDCATCH
2021   SYSYNC_CATCH (...)
2022     return HandleDecodingException((TSyncSession *)userData,"StartSequence",NULL);
2023   SYSYNC_ENDCATCH
2024 } // TSyncAppBase::StartSequence
2025
2026
2027 Ret_t TSyncAppBase::EndSequence(VoidPtr_t userData)
2028 {
2029   if (!userData) return SML_ERR_WRONG_PARAM;
2030   // call Session
2031   SYSYNC_TRY {
2032     return ((TSyncSession *) userData)->EndSequence();
2033   }
2034   SYSYNC_CATCH (exception &e)
2035     return HandleDecodingException((TSyncSession *)userData,"EndSequence",&e);
2036   SYSYNC_ENDCATCH
2037   SYSYNC_CATCH (...)
2038     return HandleDecodingException((TSyncSession *)userData,"EndSequence",NULL);
2039   SYSYNC_ENDCATCH
2040 } // TSyncAppBase::EndSequence
2041 #endif
2042
2043
2044 #ifdef ATOMIC_RECEIVE
2045 Ret_t TSyncAppBase::StartAtomic(VoidPtr_t userData, SmlAtomicPtr_t aContentP)
2046 {
2047   if (!userData) return SML_ERR_WRONG_PARAM;
2048   // call Session
2049   SYSYNC_TRY {
2050     return ((TSyncSession *) userData)->StartAtomic(aContentP);
2051   }
2052   SYSYNC_CATCH (exception &e)
2053     return HandleDecodingException((TSyncSession *)userData,"StartAtomic",&e);
2054   SYSYNC_ENDCATCH
2055   SYSYNC_CATCH (...)
2056     return HandleDecodingException((TSyncSession *)userData,"StartAtomic",NULL);
2057   SYSYNC_ENDCATCH
2058 } // TSyncAppBase::StartAtomic
2059
2060
2061 Ret_t TSyncAppBase::EndAtomic(VoidPtr_t userData)
2062 {
2063   if (!userData) return SML_ERR_WRONG_PARAM;
2064   // call Session
2065   SYSYNC_TRY {
2066     return ((TSyncSession *) userData)->EndAtomic();
2067   }
2068   SYSYNC_CATCH (exception &e)
2069     return HandleDecodingException((TSyncSession *)userData,"EndAtomic",&e);
2070   SYSYNC_ENDCATCH
2071   SYSYNC_CATCH (...)
2072     return HandleDecodingException((TSyncSession *)userData,"EndAtomic",NULL);
2073   SYSYNC_ENDCATCH
2074 } // TSyncAppBase::EndAtomic
2075 #endif
2076
2077
2078 Ret_t TSyncAppBase::AddCmd(VoidPtr_t userData, SmlAddPtr_t aContentP)
2079 {
2080   if (!userData) return SML_ERR_WRONG_PARAM;
2081   // call Session
2082   SYSYNC_TRY {
2083     return ((TSyncSession *) userData)->AddCmd(aContentP);
2084   }
2085   SYSYNC_CATCH (exception &e)
2086     return HandleDecodingException((TSyncSession *)userData,"AddCmd",&e);
2087   SYSYNC_ENDCATCH
2088   SYSYNC_CATCH (...)
2089     return HandleDecodingException((TSyncSession *)userData,"AddCmd",NULL);
2090   SYSYNC_ENDCATCH
2091 } // TSyncAppBase::AddCmd
2092
2093
2094 Ret_t TSyncAppBase::AlertCmd(VoidPtr_t userData, SmlAlertPtr_t aContentP)
2095 {
2096   if (!userData) return SML_ERR_WRONG_PARAM;
2097   // call Session
2098   SYSYNC_TRY {
2099     return ((TSyncSession *) userData)->AlertCmd(aContentP);
2100   }
2101   SYSYNC_CATCH (exception &e)
2102     return HandleDecodingException((TSyncSession *)userData,"AlertCmd",&e);
2103   SYSYNC_ENDCATCH
2104   SYSYNC_CATCH (...)
2105     return HandleDecodingException((TSyncSession *)userData,"AlertCmd",NULL);
2106   SYSYNC_ENDCATCH
2107 } // TSyncAppBase::AlertCmd
2108
2109
2110 Ret_t TSyncAppBase::DeleteCmd(VoidPtr_t userData, SmlDeletePtr_t aContentP)
2111 {
2112   if (!userData) return SML_ERR_WRONG_PARAM;
2113   // call Session
2114   SYSYNC_TRY {
2115     return ((TSyncSession *) userData)->DeleteCmd(aContentP);
2116   }
2117   SYSYNC_CATCH (exception &e)
2118     return HandleDecodingException((TSyncSession *)userData,"DeleteCmd",&e);
2119   SYSYNC_ENDCATCH
2120   SYSYNC_CATCH (...)
2121     return HandleDecodingException((TSyncSession *)userData,"DeleteCmd",NULL);
2122   SYSYNC_ENDCATCH
2123 } // TSyncAppBase::DeleteCmd
2124
2125
2126 Ret_t TSyncAppBase::GetCmd(VoidPtr_t userData, SmlGetPtr_t aContentP)
2127 {
2128   if (!userData) return SML_ERR_WRONG_PARAM;
2129   // call Session
2130   SYSYNC_TRY {
2131     return ((TSyncSession *) userData)->GetCmd(aContentP);
2132   }
2133   SYSYNC_CATCH (exception &e)
2134     return HandleDecodingException((TSyncSession *)userData,"GetCmd",&e);
2135   SYSYNC_ENDCATCH
2136   SYSYNC_CATCH (...)
2137     return HandleDecodingException((TSyncSession *)userData,"GetCmd",NULL);
2138   SYSYNC_ENDCATCH
2139 } // TSyncAppBase::GetCmd
2140
2141
2142 Ret_t TSyncAppBase::PutCmd(VoidPtr_t userData, SmlPutPtr_t aContentP)
2143 {
2144   if (!userData) return SML_ERR_WRONG_PARAM;
2145   // call Session
2146   SYSYNC_TRY {
2147     return ((TSyncSession *) userData)->PutCmd(aContentP);
2148   }
2149   SYSYNC_CATCH (exception &e)
2150     return HandleDecodingException((TSyncSession *)userData,"PutCmd",&e);
2151   SYSYNC_ENDCATCH
2152   SYSYNC_CATCH (...)
2153     return HandleDecodingException((TSyncSession *)userData,"PutCmd",NULL);
2154   SYSYNC_ENDCATCH
2155 } // TSyncAppBase::PutCmd
2156
2157
2158 #ifdef MAP_RECEIVE
2159 Ret_t TSyncAppBase::MapCmd(VoidPtr_t userData, SmlMapPtr_t aContentP)
2160 {
2161   if (!userData) return SML_ERR_WRONG_PARAM;
2162   // call Session
2163   SYSYNC_TRY {
2164     return ((TSyncSession *) userData)->MapCmd(aContentP);
2165   }
2166   SYSYNC_CATCH (exception &e)
2167     return HandleDecodingException((TSyncSession *)userData,"MapCmd",&e);
2168   SYSYNC_ENDCATCH
2169   SYSYNC_CATCH (...)
2170     return HandleDecodingException((TSyncSession *)userData,"MapCmd",NULL);
2171   SYSYNC_ENDCATCH
2172 } // TSyncAppBase::MapCmd
2173 #endif
2174
2175
2176 #ifdef RESULT_RECEIVE
2177 Ret_t TSyncAppBase::ResultsCmd(VoidPtr_t userData, SmlResultsPtr_t aContentP)
2178 {
2179   if (!userData) return SML_ERR_WRONG_PARAM;
2180   // call Session
2181   SYSYNC_TRY {
2182     return ((TSyncSession *) userData)->ResultsCmd(aContentP);
2183   }
2184   SYSYNC_CATCH (exception &e)
2185     return HandleDecodingException((TSyncSession *)userData,"ResultsCmd",&e);
2186   SYSYNC_ENDCATCH
2187   SYSYNC_CATCH (...)
2188     return HandleDecodingException((TSyncSession *)userData,"ResultsCmd",NULL);
2189   SYSYNC_ENDCATCH
2190 } // TSyncAppBase::ResultsCmd
2191 #endif
2192
2193
2194 Ret_t TSyncAppBase::StatusCmd(VoidPtr_t userData, SmlStatusPtr_t aContentP)
2195 {
2196   if (!userData) return SML_ERR_WRONG_PARAM;
2197   // call Session
2198   SYSYNC_TRY {
2199     return ((TSyncSession *) userData)->StatusCmd(aContentP);
2200   }
2201   SYSYNC_CATCH (exception &e)
2202     return HandleDecodingException((TSyncSession *)userData,"StatusCmd",&e);
2203   SYSYNC_ENDCATCH
2204   SYSYNC_CATCH (...)
2205     return HandleDecodingException((TSyncSession *)userData,"StatusCmd",NULL);
2206   SYSYNC_ENDCATCH
2207 } // TSyncAppBase::StatusCmd
2208
2209
2210 Ret_t TSyncAppBase::ReplaceCmd(VoidPtr_t userData, SmlReplacePtr_t aContentP)
2211 {
2212   if (!userData) return SML_ERR_WRONG_PARAM;
2213   // call Session
2214   SYSYNC_TRY {
2215     return ((TSyncSession *) userData)->ReplaceCmd(aContentP);
2216   }
2217   SYSYNC_CATCH (exception &e)
2218     return HandleDecodingException((TSyncSession *)userData,"ReplaceCmd",&e);
2219   SYSYNC_ENDCATCH
2220   SYSYNC_CATCH (...)
2221     return HandleDecodingException((TSyncSession *)userData,"ReplaceCmd",NULL);
2222   SYSYNC_ENDCATCH
2223 } // TSyncAppBase::ReplaceCmd
2224
2225
2226 #ifdef COPY_RECEIVE
2227 Ret_t TSyncAppBase::CopyCmd(VoidPtr_t userData, SmlCopyPtr_t aContentP)
2228 {
2229   if (!userData) return SML_ERR_WRONG_PARAM;
2230   // call Session
2231   SYSYNC_TRY {
2232     return ((TSyncSession *) userData)->CopyCmd(aContentP);
2233   }
2234   SYSYNC_CATCH (exception &e)
2235     return HandleDecodingException((TSyncSession *)userData,"CopyCmd",&e);
2236   SYSYNC_ENDCATCH
2237   SYSYNC_CATCH (...)
2238     return HandleDecodingException((TSyncSession *)userData,"CopyCmd",NULL);
2239   SYSYNC_ENDCATCH
2240 } // TSyncAppBase::CopyCmd
2241 #endif
2242
2243
2244 Ret_t TSyncAppBase::MoveCmd(VoidPtr_t userData, SmlMovePtr_t aContentP)
2245 {
2246   if (!userData) return SML_ERR_WRONG_PARAM;
2247   // call Session
2248   SYSYNC_TRY {
2249     return ((TSyncSession *) userData)->MoveCmd(aContentP);
2250   }
2251   SYSYNC_CATCH (exception &e)
2252     return HandleDecodingException((TSyncSession *)userData,"MoveCmd",&e);
2253   SYSYNC_ENDCATCH
2254   SYSYNC_CATCH (...)
2255     return HandleDecodingException((TSyncSession *)userData,"MoveCmd",NULL);
2256   SYSYNC_ENDCATCH
2257 } // TSyncAppBase::MoveCmd
2258
2259
2260 /* Other Callbacks */
2261 Ret_t TSyncAppBase::HandleError(VoidPtr_t userData)
2262 {
2263   if (!userData) return SML_ERR_WRONG_PARAM;
2264   // call Session
2265   SYSYNC_TRY {
2266     return ((TSyncSession *) userData)->HandleError();
2267   }
2268   SYSYNC_CATCH (exception &e)
2269     return HandleDecodingException((TSyncSession *)userData,"HandleError",&e);
2270   SYSYNC_ENDCATCH
2271   SYSYNC_CATCH (...)
2272     return HandleDecodingException((TSyncSession *)userData,"HandleError",NULL);
2273   SYSYNC_ENDCATCH
2274 } // TSyncAppBase::HandleError
2275
2276
2277
2278 // %%%%
2279 Ret_t TSyncAppBase::DummyHandler(VoidPtr_t userData, const char* msg)
2280 {
2281   if (!userData) return SML_ERR_WRONG_PARAM;
2282   if (userData) {
2283     // session is attached
2284     SYSYNC_TRY {
2285       return ((TSyncSession *) userData)->DummyHandler(msg);
2286     }
2287     SYSYNC_CATCH (exception &e)
2288       return HandleDecodingException((TSyncSession *)userData,"DummyHandler",&e);
2289     SYSYNC_ENDCATCH
2290     SYSYNC_CATCH (...)
2291       return HandleDecodingException((TSyncSession *)userData,"DummyHandler",NULL);
2292     SYSYNC_ENDCATCH
2293   }
2294   else {
2295     DEBUGPRINTFX(DBG_HOT,("DummyHandler (without session attached): msg=%s",msg));
2296     return SML_ERR_OK;
2297   }
2298 } // TSyncAppBase::DummyHandler
2299
2300
2301 /* SyncML toolkit callback address table */
2302
2303 static const SmlCallbacks_t mySmlCallbacks = {
2304   /* message callbacks */
2305   smlStartMessageCallback,
2306   smlEndMessageCallback,
2307   /* grouping commands */
2308   smlStartSyncCallback,
2309   smlEndSyncCallback,
2310   #ifdef ATOMIC_RECEIVE  /* these callbacks are NOT included in the Toolkit lite version */
2311     smlStartAtomicCallback,
2312     smlEndAtomicCallback,
2313   #endif
2314   #ifdef SEQUENCE_RECEIVE
2315     smlStartSequenceCallback,
2316     smlEndSequenceCallback,
2317   #endif
2318   /* Sync Commands */
2319   smlAddCmdCallback,
2320   smlAlertCmdCallback,
2321   smlDeleteCmdCallback,
2322   smlGetCmdCallback,
2323   smlPutCmdCallback,
2324   #ifdef MAP_RECEIVE
2325     smlMapCmdCallback,
2326   #endif
2327   #ifdef RESULT_RECEIVE
2328     smlResultsCmdCallback,
2329   #endif
2330   smlStatusCmdCallback,
2331   smlReplaceCmdCallback,
2332   /* other commands */
2333   #ifdef COPY_RECEIVE  /* these callbacks are NOT included in the Toolkit lite version */
2334     smlCopyCmdCallback,
2335   #endif
2336   #ifdef EXEC_RECEIVE
2337     smlExecCmdCallback,
2338   #endif
2339   #ifdef SEARCH_RECEIVE
2340     smlSearchCmdCallback,
2341   #endif
2342   smlMoveCmdCallback,
2343   /* Other Callbacks */
2344   smlHandleErrorCallback,
2345   smlTransmitChunkCallback
2346 }; /* sml_callbacks struct */
2347
2348
2349 /* Context record to find back to appbase and store userData */
2350
2351 typedef struct {
2352   TSyncAppBase *appBaseP;
2353   void *userDataP;
2354 } TSmlContextDataRec;
2355
2356
2357 /* SyncML toolkit callback implementations */
2358
2359 // macros to simplify access to contex
2360 #define GET_APPBASE(x) (((TSmlContextDataRec *)x)->appBaseP)
2361 #define GET_USERDATA(x) (((TSmlContextDataRec *)x)->userDataP)
2362
2363 /* message callbacks */
2364 static Ret_t smlStartMessageCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncHdrPtr_t pContent) { return GET_APPBASE(userData)->StartMessage(id,GET_USERDATA(userData),pContent); }
2365 static Ret_t smlEndMessageCallback(InstanceID_t id, VoidPtr_t userData, Boolean_t final) { return GET_APPBASE(userData)->EndMessage(GET_USERDATA(userData),final); }
2366 /* grouping commands */
2367 static Ret_t smlStartSyncCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncPtr_t pContent) { return GET_APPBASE(userData)->StartSync(GET_USERDATA(userData),pContent); }
2368 static Ret_t smlEndSyncCallback(InstanceID_t id, VoidPtr_t userData) { return GET_APPBASE(userData)->EndSync(GET_USERDATA(userData)); }
2369 #ifdef ATOMIC_RECEIVE  /* these callbacks are NOT included in the Toolkit lite version */
2370   static Ret_t smlStartAtomicCallback(InstanceID_t id, VoidPtr_t userData, SmlAtomicPtr_t pContent) { return GET_APPBASE(userData)->StartAtomic(GET_USERDATA(userData),pContent); }
2371   static Ret_t smlEndAtomicCallback(InstanceID_t id, VoidPtr_t userData) { return GET_APPBASE(userData)->EndAtomic(GET_USERDATA(userData)); }
2372 #endif
2373 #ifdef SEQUENCE_RECEIVE
2374   static Ret_t smlStartSequenceCallback(InstanceID_t id, VoidPtr_t userData, SmlSequencePtr_t pContent) { return GET_APPBASE(userData)->StartSequence(GET_USERDATA(userData),pContent); }
2375   static Ret_t smlEndSequenceCallback(InstanceID_t id, VoidPtr_t userData) { return GET_APPBASE(userData)->EndSequence(GET_USERDATA(userData)); }
2376 #endif
2377 /* Sync Commands */
2378 static Ret_t smlAddCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAddPtr_t pContent) { return GET_APPBASE(userData)->AddCmd(GET_USERDATA(userData),pContent); }
2379 static Ret_t smlAlertCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent) { return GET_APPBASE(userData)->AlertCmd(GET_USERDATA(userData),pContent); }
2380 static Ret_t smlDeleteCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlDeletePtr_t pContent) { return GET_APPBASE(userData)->DeleteCmd(GET_USERDATA(userData),pContent); }
2381 static Ret_t smlGetCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlGetPtr_t pContent) { return GET_APPBASE(userData)->GetCmd(GET_USERDATA(userData),pContent); }
2382 static Ret_t smlPutCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlPutPtr_t pContent) { return GET_APPBASE(userData)->PutCmd(GET_USERDATA(userData),pContent); }
2383 #ifdef MAP_RECEIVE
2384   static Ret_t smlMapCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMapPtr_t pContent) { return GET_APPBASE(userData)->MapCmd(GET_USERDATA(userData),pContent); }
2385 #endif
2386 #ifdef RESULT_RECEIVE
2387   static Ret_t smlResultsCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlResultsPtr_t pContent) { return GET_APPBASE(userData)->ResultsCmd(GET_USERDATA(userData),pContent); }
2388 #endif
2389 static Ret_t smlStatusCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlStatusPtr_t pContent) { return GET_APPBASE(userData)->StatusCmd(GET_USERDATA(userData),pContent); }
2390 static Ret_t smlReplaceCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlReplacePtr_t pContent) { return GET_APPBASE(userData)->ReplaceCmd(GET_USERDATA(userData),pContent); }
2391 /* other commands */
2392 #ifdef COPY_RECEIVE  /* these callbacks are NOT included in the Toolkit lite version */
2393   static Ret_t smlCopyCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlCopyPtr_t pContent) { return GET_APPBASE(userData)->CopyCmd(GET_USERDATA(userData),pContent); }
2394 #endif
2395 static Ret_t smlMoveCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMovePtr_t pContent) { return GET_APPBASE(userData)->MoveCmd(GET_USERDATA(userData),pContent); }
2396 #ifdef EXEC_RECEIVE
2397   static Ret_t smlExecCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlExecPtr_t pContent) { /*%%%tbd return GET_APPBASE(userData)->ExecCmd(GET_USERDATA(userData),pContent); */ return SML_ERR_INVALID_OPTIONS; }
2398 #endif
2399 #ifdef SEARCH_RECEIVE
2400   static Ret_t smlSearchCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlSearchPtr_t pContent) { /*%%%tbd return GET_APPBASE(userData)->SearchCmd(GET_USERDATA(userData),pContent); */ return SML_ERR_INVALID_OPTIONS; }
2401 #endif
2402 /* Other Callbacks */
2403 static Ret_t smlHandleErrorCallback(InstanceID_t id, VoidPtr_t userData) { return GET_APPBASE(userData)->DummyHandler(GET_USERDATA(userData),"ErrorCallback"); }
2404 static Ret_t smlTransmitChunkCallback(InstanceID_t id, VoidPtr_t userData) { /*%%%tdb return GET_APPBASE(userData)->TransmitChunk(GET_USERDATA(userData),pContent); */ return SML_ERR_INVALID_OPTIONS; }
2405
2406
2407 /* end callback implementations */
2408
2409
2410 /* RTK interfacing */
2411
2412
2413 Ret_t TSyncAppBase::setSmlInstanceUserData(
2414   InstanceID_t aInstanceID,
2415   void *aUserDataP
2416 )
2417 {
2418   void *ctxP = NULL;
2419   if (smlGetUserData(aInstanceID,&ctxP)==SML_ERR_OK && ctxP) {
2420     static_cast<TSmlContextDataRec *>(ctxP)->userDataP=aUserDataP;
2421     return SML_ERR_OK;
2422   }
2423   return SML_ERR_MGR_INVALID_INSTANCE_INFO; // invalid instance (has no TSmlContextDataRec in userData)
2424 } // TSyncAppBase::setSmlInstanceUserData
2425
2426
2427 Ret_t TSyncAppBase::getSmlInstanceUserData(
2428   InstanceID_t aInstanceID,
2429   void **aUserDataPP
2430 )
2431 {
2432   void *ctxP = NULL;
2433   if (smlGetUserData(aInstanceID,&ctxP)==SML_ERR_OK && ctxP) {
2434     *aUserDataPP = static_cast<TSmlContextDataRec *>(ctxP)->userDataP;
2435     return SML_ERR_OK;
2436   }
2437   return SML_ERR_MGR_INVALID_INSTANCE_INFO; // invalid instance (has no TSmlContextDataRec in userData)
2438 } // TSyncAppBase::setSmlInstanceUserData
2439
2440
2441
2442
2443 // create new SyncML toolkit instance
2444 bool TSyncAppBase::newSmlInstance(
2445   SmlEncoding_t aEncoding,
2446   sInt32 aWorkspaceMem,
2447   InstanceID_t &aInstanceID
2448 )
2449 {
2450   SmlInstanceOptions_t myInstanceOptions;
2451   Ret_t err;
2452
2453   #ifndef NOWSM
2454     #error "Only NOWSM version is supported any more"
2455   #endif
2456   // Set options
2457   // - encoding
2458   myInstanceOptions.encoding=aEncoding;
2459   // - total size of instance buffer
2460   //  (must have room for both incoming and outgoing message if instance
2461   //  is used for both)
2462   myInstanceOptions.workspaceSize=aWorkspaceMem;
2463   // - maximum outgoing message size
2464   myInstanceOptions.maxOutgoingSize=0; // %%% disabled for now, can be set later with setMaxOutgoingSize()
2465   // - create user data record
2466   TSmlContextDataRec *smlContextRecP = new TSmlContextDataRec;
2467   if (!smlContextRecP) return false;
2468   smlContextRecP->appBaseP=this; // pointer to find back to this syncappbase w/o the help of global vars
2469   smlContextRecP->userDataP=NULL; // userData will be SySyncSession pointer, but now session is not yet determined
2470   // - now instantiate (thread-safe!)
2471   err=smlInitInstance(
2472     &mySmlCallbacks, // callbacks
2473     &myInstanceOptions,
2474     smlContextRecP,
2475     &aInstanceID // where to store the instance ID
2476   );
2477   // - return instance or NULL if failed
2478   if (err==SML_ERR_OK) {
2479     DEBUGPRINTFX(DBG_RTK_SML,("////////////// sml Instance created, id(=instanceInfoPtr)=0x%08lX",(long)aInstanceID));
2480     return true; // success
2481   }
2482   else {
2483     DEBUGPRINTFX(DBG_ERROR,("************ smlInitInstance returned 0x%hX",(sInt16)err));
2484     aInstanceID=NULL; // none
2485     return false; // failed
2486   }
2487 } // TSyncAppBase::newSmlInstance
2488
2489
2490 void TSyncAppBase::freeSmlInstance(InstanceID_t aInstance)
2491 {
2492   // forget instance now
2493   // - accept no-instance
2494   if (aInstance==NULL) return;
2495   // - there is an instance
2496   // - free context record
2497   void *ctxP;
2498   if (smlGetUserData(aInstance,&ctxP)==SML_ERR_OK && ctxP)
2499     delete static_cast<TSmlContextDataRec *>(ctxP);
2500   // - free instance itself
2501   Ret_t err=smlTerminateInstance(aInstance);
2502   DEBUGPRINTFX(DBG_RTK_SML,("////////////// sml Instance freed, id(=instanceInfoPtr)=0x%08lX, err=0x%hX",(long)aInstance,(sInt16)err));
2503   #ifdef SYDEBUG
2504   if (err!=SML_ERR_OK) {
2505     DEBUGPRINTFX(DBG_ERROR,("smlTerminateInstance returned 0x%hX",(sInt16)err));
2506   }
2507   #endif
2508 } // TSyncAppBase::freeSmlInstance
2509
2510
2511 // determine encoding from beginning of SyncML message data
2512 SmlEncoding_t TSyncAppBase::encodingFromData(cAppPointer aData, memSize aDataSize)
2513 {
2514         SmlEncoding_t enc = SML_UNDEF;
2515   if (aData && aDataSize>=5) {
2516         // check for WBXML intro sequences
2517     if (
2518       (memcmp((cAppCharP)aData,"\x02\x00\x00",3)==0) || // WBXML V2 + Public identifier as string
2519       (memcmp((cAppCharP)aData,"\x02\xA4\x01",3)==0) || // WBXML V2 + Public identifier 0x1201 for SyncML 1.2
2520       (memcmp((cAppCharP)aData,"\x02\x9F\x53",3)==0) || // WBXML V2 + Public identifier 0x0FD3 for SyncML 1.1
2521       (memcmp((cAppCharP)aData,"\x02\x9F\x51",3)==0)    // WBXML V2 + Public identifier 0x0FD1 for SyncML 1.0
2522     )
2523       enc=SML_WBXML;
2524     else {
2525         // could be XML
2526       // - skip UTF-8 BOM if there is one
2527       cUInt8P p = (cUInt8P)aData;
2528       if (p[0]==0xEF && p[1]==0xBB && p[2]==0xBF)
2529         p+=3; // skip the BOM
2530       // now check for XML
2531             if (strnncmp((cAppCharP)p,"<?xml",5)==0 ||
2532                 strnncmp((cAppCharP)p,"<SyncML",7)==0)
2533                 enc=SML_XML;
2534         }
2535   }
2536   return enc;
2537 } // TSyncAppBase::encodingFromData
2538
2539
2540 // determine encoding from Content-Type: header value
2541 SmlEncoding_t TSyncAppBase::encodingFromContentType(cAppCharP aTypeString)
2542 {
2543         sInt16 enc = SML_UNDEF;
2544   if (aTypeString) {
2545     stringSize l=strlen(SYNCML_MIME_TYPE SYNCML_ENCODING_SEPARATOR);
2546     if (strucmp(aTypeString,SYNCML_MIME_TYPE SYNCML_ENCODING_SEPARATOR,l)==0) {
2547       // is SyncML, check encoding
2548       cAppCharP p = strchr(aTypeString,';');
2549       sInt16 cl = p ? p-aTypeString-l : 0; // length of encoding string (charset could be appended here)  
2550       StrToEnum(SyncMLEncodingMIMENames,numSyncMLEncodings,enc,aTypeString+l,cl);
2551     }
2552   }
2553   return static_cast<SmlEncoding_t>(enc);
2554 } // TSyncAppBase::encodingFromContentType
2555
2556
2557
2558
2559 // save app state (such as settings in datastore configs etc.)
2560 void TSyncAppBase::saveAppState(void)
2561 {
2562   if (fConfigP) fConfigP->saveAppState();
2563 } // TSyncAppBase::saveAppState
2564
2565
2566 // manufacturer of overall solution (can be configured, while OEM is fixed to Synthesis)
2567 string TSyncAppBase::getManufacturer(void)
2568 {
2569         #ifdef ENGINEINTERFACE_SUPPORT
2570   if (fConfigP && !(fConfigP->fMan.empty()))
2571         return fConfigP->fMan;
2572   #endif
2573   // if no string configured, return default
2574         return CUST_SYNC_MAN;
2575 } // TSyncAppBase::getManufacturer
2576
2577
2578 // model (application name) of overall solution
2579 string TSyncAppBase::getModel(void) {
2580         #ifdef ENGINEINTERFACE_SUPPORT
2581   if (fConfigP && !(fConfigP->fMod.empty()))
2582         return fConfigP->fMod;
2583   #endif
2584   // if no string configured, return default
2585         return CUST_SYNC_MODEL;
2586 } // TSyncAppBase::getModel
2587
2588
2589 // hardware version
2590 string TSyncAppBase::getHardwareVersion(void) {
2591   #ifdef ENGINEINTERFACE_SUPPORT
2592   if (fConfigP && !(fConfigP->fHwV.empty())) {
2593     return fConfigP->fHwV;
2594   }
2595   #endif
2596   string s;
2597   // if no string configured, return default
2598   getPlatformString(pfs_device_name, s);
2599   return s;
2600 } // TSyncAppBase::getHardwareVersion
2601
2602
2603 // firmware version (depends a lot on the context - OS version?)
2604 string TSyncAppBase::getFirmwareVersion(void) {
2605   #ifdef ENGINEINTERFACE_SUPPORT
2606   if (fConfigP && !(fConfigP->fFwV.empty())) {
2607     return fConfigP->fFwV;
2608   }
2609   #endif
2610   string s;
2611   // if no string configured, return default
2612   getPlatformString(pfs_platformvers, s);
2613   return s;
2614 } // TSyncAppBase::getHardwareVersion
2615
2616
2617 // hardware type (PDA, PC, ...)
2618 string TSyncAppBase::getDevTyp() {
2619   #ifdef ENGINEINTERFACE_SUPPORT
2620   if (fConfigP && !(fConfigP->fDevTyp.empty())) {
2621     return fConfigP->fDevTyp;
2622   }
2623   #endif
2624   // if no string configured, return default
2625   if (isServer())
2626         return SYNCML_SERVER_DEVTYP;
2627   else
2628     return SYNCML_CLIENT_DEVTYP;
2629 } // TSyncAppBase::getDevTyp
2630
2631
2632 #ifdef APP_CAN_EXPIRE
2633
2634 void TSyncAppBase::updateAppExpiry(void)
2635 {
2636   // this is the basic check. Some other checks
2637   // are spread in various files to disguise checking a little
2638   #if defined(EXPIRES_AFTER_DAYS) || defined(SYSER_REGISTRATION)
2639   // check soft expiry
2640   fAppExpiryStatus = appEnableStatus();
2641   // check hard expiry only if demo, that is, if no valid license is installed
2642   if (fAppExpiryStatus==LOCERR_OK && fDaysLeft>=0 && !fRegOK)
2643   #endif
2644   {
2645     fAppExpiryStatus =
2646       #ifdef EXPIRES_AFTER_DATE
2647       // check hard expiry date
2648       fScrambledNow>SCRAMBLED_EXPIRY_VALUE ?
2649         LOCERR_EXPIRED : LOCERR_OK;
2650       #else
2651       // no hard expiry, just ok if enabled
2652       LOCERR_OK;
2653       #endif
2654   }
2655 } // TSyncAppBase::updateAppExpiry
2656
2657 #endif
2658
2659
2660
2661 #ifdef SYSER_REGISTRATION
2662
2663 // checks if registered (must be implemented in base class)
2664 // returns LOCERR_EXPIRED, LOCERR_TOONEW or LOCERR_BADREG if not registered correctly
2665 localstatus TSyncAppBase::isRegistered(void)
2666 {
2667   #if !defined(HARDCODED_CONFIG) || defined(ENGINEINTERFACE_SUPPORT)
2668   // we have licensing in the config file or using engine interface, check it
2669   return fConfigP ? checkRegInfo(fConfigP->fLicenseName.c_str(),fConfigP->fLicenseCode.c_str(),false) : LOCERR_BADREG;
2670   #else
2671   // no license checking at this level (maybe overriden method provides check)
2672   return LOCERR_EXPIRED;
2673   #endif
2674 } // TSyncAppBase::isRegistered
2675
2676
2677 // get (entire) registration string
2678 void TSyncAppBase::getRegString(string &aString)
2679 {
2680   #if !defined(HARDCODED_CONFIG) || defined(ENGINEINTERFACE_SUPPORT)
2681   // we have licensing in the config file or set via engine interface, use it
2682   if (fConfigP)
2683     aString=fConfigP->fLicenseName;
2684   else
2685   #endif
2686   {
2687     aString.erase();
2688   }
2689 } // TSyncAppBase::getRegString
2690
2691
2692 #endif
2693
2694
2695 // safety checks
2696 #if !defined(APP_CAN_EXPIRE) && defined(RELEASE_VERSION) && !defined(NEVER_EXPIRES_IS_OK)
2697   #error "Warning: Release version that never expires!"
2698 #endif
2699
2700
2701 #ifdef APP_CAN_EXPIRE
2702
2703 // make sure we have a valid variant code for the target
2704 #ifndef SYSER_VARIANT_CODE
2705   #error "SYSER_VARIANT_CODE must be defined in target_options.h"
2706 #endif
2707
2708 // check enable status of application
2709 localstatus TSyncAppBase::appEnableStatus(void)
2710 {
2711   // safety check - app w/o initialized config is NOT enabled
2712   // Note: agentconfig tested here, but all other config sections are created in clear() at the same time
2713   if (!fConfigP || !(fConfigP->fAgentConfigP)) {
2714     return LOCERR_WRONGUSAGE;
2715   }
2716   // check registration (which will disable normal expiry)
2717   #ifdef SYSER_REGISTRATION
2718   localstatus regsta = isRegistered(); // ok if registered
2719   #else
2720     #ifndef APP_CAN_EXPIRE
2721     localstatus regsta = LOCERR_OK; // not registerable, not exprining - just run forever
2722     #ifdef RELEASE_VERSION
2723     #error "WARNING: Completely unlimited operation w/o license or expiry - is this intended??"
2724     #endif
2725     #else
2726     localstatus regsta = LOCERR_BADREG; // not registerable, assume no license, must be eval which expires
2727     #endif
2728   #endif
2729   localstatus sta = regsta;
2730   // check expiry (only if registration has not already defined one)
2731   #ifdef APP_CAN_EXPIRE
2732     #ifdef NO_LICENSE_UNTIL_HARDEXPIRY
2733     // we don't need a valid license code until hard expiry date hits
2734     // so if we have a failure now, let the hard expiry decide
2735     if (regsta!=LOCERR_OK) {
2736                   #ifdef SYSER_REGISTRATION
2737       fDaysLeft=1; // simulate expiring tomorrow
2738       #endif
2739       sta = LOCERR_OK; // ok if we find no hard expiry later
2740       // then let hard expiry decide
2741       #ifndef EXPIRES_AFTER_DATE
2742       #error "WARNING: NO_LICENSE_UNTIL_HARDEXPIRY without EXPIRES_AFTER_DATE - running forever w/o license - indended that way?"
2743       #endif
2744     }
2745     #endif
2746     sInt32 td = lineartime2dateonly(getSystemNowAs(TCTX_UTC));
2747     #if defined(EXPIRES_AFTER_DAYS) || defined(NO_LICENSE_UNTIL_HARDEXPIRY)
2748     if (regsta==LOCERR_BADREG || regsta==LOCERR_WRONGPROD) {
2749       // not registered (for this product), check if we are in evaluation period
2750       #ifdef EXPIRES_AFTER_DATE
2751       // check hard expiry first
2752       if (fScrambledNow>SCRAMBLED_EXPIRY_VALUE) {
2753                           #ifdef SYSER_REGISTRATION
2754         fDaysLeft=0; // hard-expired
2755         #endif
2756         sta = LOCERR_EXPIRED;
2757       }
2758       else
2759       #endif
2760       {
2761         #ifdef EXPIRES_AFTER_DAYS
2762         // (bfo found that we need to chec for > demo days too, as
2763         // otherwise some clever guys could install with the
2764         // clock 20 years in the future and then set the clock back)
2765         uInt32 vers;
2766         lineardate_t firstuse;
2767         getFirstUseInfo(SYSER_VARIANT_CODE,firstuse,vers);
2768         sInt32 d = firstuse+EXPIRES_AFTER_DAYS - td;
2769         fDaysLeft = d>0 && d<=EXPIRES_AFTER_DAYS ? d : 0;
2770         sta = d>0 ? LOCERR_OK : LOCERR_EXPIRED;
2771         #else
2772         // Do NOT change the status to expired! - just pass on the license error status
2773         // which might be ok here in case we have a NO_LICENSE_UNTIL_HARDEXPIRY
2774         //sta = LOCERR_EXPIRED;
2775         #endif
2776       }
2777     }
2778     #endif
2779   #else
2780     fDaysLeft=-1; // App cannot expire, no limit
2781   #endif
2782   return sta;
2783 } // TSyncAppBase::appEnableStatus
2784
2785
2786 // get registration information to display (and check internally)
2787 localstatus TSyncAppBase::getAppEnableInfo(sInt16 &aDaysLeft, string *aRegnameP, string *aRegInternalsP)
2788 {
2789   #ifndef SYSER_REGISTRATION
2790   // no registration
2791   if (aRegnameP) aRegnameP->erase();
2792   if (aRegInternalsP) aRegInternalsP->erase();
2793   aDaysLeft = -1; // no expiring license (altough hardexpiry might still apply
2794   return appEnableStatus();
2795   #else
2796   // we have registration
2797   localstatus sta = appEnableStatus();
2798   if (sta!=LOCERR_OK) {
2799     if (aRegnameP) aRegnameP->erase();
2800     if (aRegInternalsP) aRegInternalsP->erase();
2801     return sta; // not enabled
2802   }
2803   // do an extra check here
2804   aDaysLeft = fDaysLeft;
2805   if (fDaysLeft==0) SYSYNC_THROW(exception()); // exit app, as fDaysLeft must be > 0 or -1 here
2806   // get strings if requested
2807   if (aRegnameP || aRegInternalsP) {
2808     string s;
2809     // get entire string
2810     getRegString(s);
2811     // check for separation into a visible (name) and and invisible (email or license restriction) part
2812     size_t n,m;
2813     size_t l=s.size();
2814     // - first priority: license restrictions in form  ::x=something
2815     n=s.find("::");
2816     if (n!=string::npos) {
2817       // found a license restriction
2818       m=n; // everthing before special separator belongs to name, rest to internals
2819     }
2820     else {
2821       // - second priority: a simple email address at the end of the string
2822       n=s.rfind('@');
2823       if (n==string::npos) {
2824         n=l; m=l; // no email, entire string is name
2825       }
2826       else {
2827         n=s.rfind(' ',n);
2828         if (n==string::npos) {
2829           n=l; m=l; // no email, entire string is name
2830         }
2831         else {
2832           m=n+1; // do not return separating space in either name nor internals
2833         }
2834       }
2835     }
2836
2837     // now remove spaces at the beginning to avoid
2838     // invisible registration info
2839     int i = s.find_first_not_of(' ');
2840     if (i==string::npos) i=0; // no non-space -> start at beginning
2841     /*
2842     int  i;
2843     for (i= 0; i<n; i++) {
2844       if (s.find( ' ',i )!=i) break; // search for the first char, which is not ' '
2845     } // if
2846     */
2847
2848     // now assign
2849     if (aRegnameP) aRegnameP->assign(s, i,n);
2850     if (aRegInternalsP) {
2851        if (m<l) aRegInternalsP->assign(s,m,l-m);
2852        else aRegInternalsP->erase();
2853     }
2854   }
2855   return sta;
2856   #endif
2857 } // TSyncAppBase::getAppEnableInfo
2858
2859 #endif // APP_CAN_EXPIRE
2860
2861
2862
2863 #ifdef EXPIRES_AFTER_DAYS
2864
2865 // make sure we have a valid variant code for the target
2866 #ifndef SYSER_VERSCHECK_MASK
2867   #error "SYSER_VERSCHECK_MASK must be defined in target_options.h"
2868 #endif
2869
2870 /// @brief update first use info to allow for repeated eval when user installs an all-new version
2871 /// @return true if update was needed
2872 /// @param[in] aVariant variant context to perform update
2873 /// @param[in,out] aFirstUseDate date when this variant and version was used first.
2874 ///   Will be updated
2875 /// @param[in,out] aFirstUseVers version that relates to aFirstUseDate.
2876 ///   Will be updated to current version if version check allows re-starting demo period.
2877 bool TSyncAppBase::updateFirstUseInfo(lineardate_t &aFirstUseDate, uInt32 &aFirstUseVers)
2878 {
2879   // update if current version is significantly newer than what we used last time
2880   if (
2881     aFirstUseDate==0 ||
2882     ((aFirstUseVers & SYSER_VERSCHECK_MASK) <
2883      (SYSYNC_VERSION_UINT32 & SYSER_VERSCHECK_MASK))
2884   ) {
2885     // current version is different in a relevant part of the version number, so reset first use
2886     aFirstUseDate = getSystemNowAs(TCTX_UTC) / linearDateToTimeFactor;
2887     aFirstUseVers = SYSYNC_VERSION_UINT32;
2888     return true;
2889   }
2890   // no updates
2891   return false;
2892 } // TSyncAppBase::updateFirstUseInfo
2893
2894 #endif
2895
2896
2897 #ifdef SYSER_REGISTRATION
2898
2899 // helper to get next restriction out of license string
2900 const char * TSyncAppBase::getLicenseRestriction(const char *aInfo, string &aID, string &aVal)
2901 {
2902   const char *p;
2903   char c;
2904   bool quotedval=false;
2905
2906   // if no info or at end of string, return NULL
2907   if (!aInfo || *aInfo==0) return NULL;
2908   // find next restriction
2909   aInfo=strstr(aInfo,"::");
2910   if (!aInfo) return NULL; // no more restrictions found
2911   // get ID of restriction
2912   aInfo+=2; // skip ::
2913   p=strchr(aInfo,'=');
2914   if (!p) return NULL; // no more restrictions found
2915   // - assign ID
2916   aID.assign(aInfo,p-aInfo);
2917   aInfo=p+1; // skip =
2918   // get value of restriction
2919   aVal.erase();
2920   if (*aInfo==0x22) {
2921     // starts with doublequote -> treat as quoted value
2922     quotedval=true;
2923     aInfo++;
2924   }
2925   while ((c=*aInfo)) {
2926     aInfo++;
2927     if (quotedval) {
2928       // quoted string ends with " and can contain backslash escapes
2929       if (c==0x22) break; // done after closing quote
2930       if (c=='\\') {
2931         if (*aInfo==0) break; // quote without char following, stop here
2932         c=*aInfo++; // get next char
2933       }
2934     }
2935     else {
2936       // unquoted string ends with next space
2937       if (isspace(c)) break;
2938     }
2939     // save char
2940     aVal+=c;
2941   }
2942   // return where to continue scanning for next item
2943   return aInfo;
2944 } // TSyncAppBase::getLicenseRestriction
2945
2946
2947 // checks registration code for CRC ok and compare with
2948 // predefined constants for product code etc.
2949 localstatus TSyncAppBase::checkRegInfo(const char *aRegKey, const char *aRegCode, bool aMangled)
2950 {
2951   uInt32 infocrc;
2952
2953   // extract information from registration code
2954   localstatus regSta = LOCERR_OK;
2955   fRelDuration=0;
2956   // do not allow registration text less than 12 chars
2957   if (strlen(aRegKey)<12)
2958     regSta=LOCERR_BADREG; // too short code is a bad code
2959   else if (!getSySerialInfo(
2960     aRegCode, // code
2961     fRegProductFlags,
2962     fRegProductCode,
2963     fRegLicenseType,
2964     fRegQuantity,
2965     fRegDuration,
2966     fLicCRC, // CRC as included in the license code
2967     infocrc,
2968     aMangled
2969   ))
2970     regSta=LOCERR_BADREG; // no code is a bad code
2971   if (regSta==LOCERR_OK) {
2972     // code is basically ok, check standard info
2973     if (!(
2974       // product code
2975       (
2976         (fRegProductCode == SYSER_PRODUCT_CODE_MAIN)
2977         #ifdef SYSER_PRODUCT_CODE_ALT1
2978         || (fRegProductCode == SYSER_PRODUCT_CODE_ALT1)
2979         #endif
2980         #ifdef SYSER_PRODUCT_CODE_ALT2
2981         || (fRegProductCode == SYSER_PRODUCT_CODE_ALT2)
2982         #endif
2983         #ifdef SYSER_PRODUCT_CODE_ALT3
2984         || (fRegProductCode == SYSER_PRODUCT_CODE_ALT3)
2985         #endif
2986         #ifdef SYSER_PRODUCT_CODE_ALT4
2987         || (fRegProductCode == SYSER_PRODUCT_CODE_ALT4)
2988         #endif
2989         #ifdef SYSER_PRODUCT_CODE_ALT5
2990         || (fRegProductCode == SYSER_PRODUCT_CODE_ALT5)
2991         #endif
2992         #ifdef SYSER_PRODUCT_CODE_ALT6
2993         || (fRegProductCode == SYSER_PRODUCT_CODE_ALT6)
2994         #endif
2995         #ifdef SYSER_PRODUCT_CODE_ALT7
2996         || (fRegProductCode == SYSER_PRODUCT_CODE_ALT7)
2997         #endif
2998         #ifdef SYSER_PRODUCT_CODE_ALT8
2999         || (fRegProductCode == SYSER_PRODUCT_CODE_ALT8)
3000         #endif
3001         #ifdef SYSER_PRODUCT_CODE_ALT9
3002         || (fRegProductCode == SYSER_PRODUCT_CODE_ALT9)
3003         #endif
3004       )
3005       // special SYSER_PRODFLAG_MAXRELDATE product flag
3006       && (
3007         ((SYSER_NEEDED_PRODUCT_FLAGS & SYSER_PRODFLAG_MAXRELDATE)==0) || // either maxreldate flag is not required...
3008         (fRegProductFlags & SYSER_PRODFLAG_MAXRELDATE) || // ..or it is present..
3009         (fRegDuration!=0) // ..or there is a time limit, which means that the even if the flag is required, product can run with a timed license without the flag
3010       )
3011       // test other product flags (all except SYSER_PRODFLAG_MAXRELDATE)
3012       && ((fRegProductFlags & (SYSER_NEEDED_PRODUCT_FLAGS & ~SYSER_PRODFLAG_MAXRELDATE)) == (SYSER_NEEDED_PRODUCT_FLAGS & ~SYSER_PRODFLAG_MAXRELDATE))
3013       && ((fRegProductFlags & SYSER_FORBIDDEN_PRODUCT_FLAGS) == 0)
3014     ))
3015       regSta=LOCERR_WRONGPROD;
3016     // anyway, check if CRC is ok
3017     if (!(fLicCRC == addNameToCRC(infocrc,aRegKey,aMangled))) {
3018       regSta=LOCERR_BADREG; // bad CRC is bad registration (and has precedence over wrong product)
3019       // make sure we do not enable exotic functionality
3020       fRegProductFlags=0;
3021       fRegLicenseType=0;
3022       fRegQuantity=1;
3023       fRegDuration=0;
3024       fRelDuration=0;
3025     }
3026   }
3027   // Now if we have OK, app is registered, but maybe limited
3028   // by time or max release date.
3029   if (regSta==LOCERR_OK) {
3030     // - check max release date limit first
3031     if (fRegProductFlags & SYSER_PRODFLAG_MAXRELDATE) {
3032       // duration is a release duration
3033       // - in case this is a max release date limited license, this
3034       //   excludes time limited - max release date licenses are always permanent
3035       fRelDuration = fRegDuration;
3036       fRegDuration = 0;
3037       // Now check if this build has a hard-coded release date
3038       #ifdef RELEASE_YEAR
3039       // license valid only up to release date specified, check that
3040       // if (fRegDuration > (RELEASE_YEAR-2000)*12+RELEASE_MONTH-1)  // plain
3041       if (
3042         fRelDuration &&
3043         (fRelDuration*3+48 <= 3*((RELEASE_YEAR-2000)*12+RELEASE_MONTH-1)+48)
3044       ) { // a bit disguised
3045         // license is not valid any more for a build as new as this one
3046         fRegOK=false;
3047         return LOCERR_TOONEW;
3048       }
3049       #endif
3050     }
3051     // - check for expired license now
3052     //   Note: we don't check demo period (days after first use) here
3053     if (fRegDuration!=0) {
3054       lineardate_t ending = date2lineardate(fRegDuration/12+2000,fRegDuration%12+1,1);
3055       sInt32 d=ending-lineartime2dateonly(getSystemNowAs(TCTX_UTC));
3056       fDaysLeft = d>0 ? d : 0;
3057       if (d<=0) {
3058         // license has expired
3059         fRegOK=false;
3060         return LOCERR_EXPIRED;
3061       }
3062     }
3063     else
3064       fDaysLeft=-1; // unlimited by registration
3065   }
3066   // if it is not ok here, registration code is bad
3067   fRegOK=regSta==LOCERR_OK; // save for further reference
3068   return regSta; // return status
3069 } // TSyncAppBase::checkRegInfo
3070
3071
3072 // checks if current license is properly activated
3073 // - note: must be called at a time when internet connection is available
3074 localstatus TSyncAppBase::checkLicenseActivation(lineardate_t &aLastcheck, uInt32 &aLastcrc)
3075 {
3076   bool ok = true;
3077
3078   // - check if already activated
3079   if (
3080     fRegLicenseType && (
3081       (aLastcrc != fLicCRC) ||  // different license than at last activation
3082       (aLastcheck==0) || // never checked at all
3083       (aLastcheck+180 < lineartime2dateonly(getSystemNowAs(TCTX_UTC))) // not checked in the last half year
3084     )
3085   ) {
3086     // re-check needed
3087     ok = checkLicenseType(fRegLicenseType);
3088     // update check date even if we fail (but only if we checked once before)
3089     // to prevent failed sync sessions when reg server has a problem
3090     if (ok || aLastcheck) {
3091       aLastcheck = lineartime2dateonly(getSystemNowAs(TCTX_UTC));
3092     }
3093     // update CRC only if we could validate the new CRC
3094     if (ok) {
3095       aLastcrc = fLicCRC;
3096     }
3097   }
3098   // show it to user
3099   if (!ok) {
3100     OBJ_PROGRESS_EVENT(this,pev_error,NULL,LOCERR_BADREG,0,0);
3101   }
3102   // return status
3103   return ok ? LOCERR_OK : LOCERR_BADREG;
3104 } // TSyncAppBase::checkLicenseActivation
3105
3106 #endif
3107
3108
3109 #ifdef CONCURRENT_DEVICES_LIMIT
3110
3111 // check if session count is exceeded
3112 void TSyncAppBase::checkSessionCount(sInt32 aSessionCount, TSyncSession *aSessionP)
3113 {
3114   // Check if session must be busy (due to licensing restrictions)
3115   // - some minor arithmetic to hide actual comparison with limit
3116   #ifdef SYSER_REGISTRATION
3117   // number of users from license or hardcoded limit, whichever is lower
3118   sInt32 scrambledlimit = 3*(CONCURRENT_DEVICES_LIMIT!=0 && CONCURRENT_DEVICES_LIMIT<fRegQuantity ? CONCURRENT_DEVICES_LIMIT : fRegQuantity)+42;
3119   #else
3120   // only hardcoded limit
3121   sInt32 scrambledlimit = 3*CONCURRENT_DEVICES_LIMIT+42;
3122   #endif
3123   if (
3124     // compare with hard limit (if zero, count is unlimited)
3125     ((aSessionCount<<1)+aSessionCount+42>=scrambledlimit && scrambledlimit!=42)
3126     #ifdef CUSTOMIZABLE_DEVICES_LIMIT
3127     // compare with configurable limit, if not set to zero (=unlimited)
3128     || (((aSessionCount<<2) > fConfigP->fConcurrentDeviceLimit*4) && fConfigP->fConcurrentDeviceLimit)
3129     #endif
3130   ) {
3131     // make session busy (not really responding)
3132     aSessionP->setSessionBusy(true);
3133     PDEBUGPRINTFX(DBG_HOT,("***** Limit of concurrent sessions reached, server response is 'busy'"));
3134     // info
3135     CONSOLEPRINTF(("- concurrent session limit reached, rejecting session with BUSY status"));
3136   }
3137 } // TSyncAppBase::checkSessionCount
3138
3139 #endif
3140
3141
3142 // factory methods of Rootconfig
3143 // =============================
3144
3145
3146 // Create datatypes registry - default to Multifield-capable version
3147 void TRootConfig::installDatatypesConfig(void)
3148 {
3149   fDatatypesConfigP = new TMultiFieldDatatypesConfig(this); // default
3150 } // TSyncAppBase::newDatatypesConfig
3151
3152
3153 #ifndef HARDCODED_CONFIG
3154
3155 bool TRootConfig::parseDatatypesConfig(const char **aAttributes, sInt32 aLine)
3156 {
3157   // currently, only one type of datatype registry exists per app, so we do not
3158   // need to check attributes
3159   expectChildParsing(*fDatatypesConfigP);
3160   return true;
3161 } // TRootConfig::parseDatatypesConfig
3162
3163 #endif
3164
3165
3166 } // namespace sysync
3167
3168
3169
3170 #ifdef CONFIGURABLE_TYPE_SUPPORT
3171
3172 #ifdef MIMEDIR_SUPPORT
3173   #include "vcarditemtype.h"
3174   #include "vcalendaritemtype.h"
3175 #endif
3176 #ifdef TEXTTYPE_SUPPORT
3177   #include "textitemtype.h"
3178 #endif
3179 #ifdef DATAOBJ_SUPPORT
3180   #include "dataobjtype.h"
3181 #endif
3182
3183
3184 namespace sysync {
3185
3186 // create new datatype config by name
3187 // returns NULL if none found
3188 TDataTypeConfig *TRootConfig::newDataTypeConfig(const char *aName, const char *aBaseType, TConfigElement *aParentP)
3189 {
3190   #ifdef MIMEDIR_SUPPORT
3191   if (strucmp(aBaseType,"mimedir")==0)
3192     return new TMIMEDirTypeConfig(aName,aParentP);
3193   else if (strucmp(aBaseType,"vcard")==0)
3194     return new TVCardTypeConfig(aName,aParentP);
3195   else if (strucmp(aBaseType,"vcalendar")==0)
3196     return new TVCalendarTypeConfig(aName,aParentP);
3197   else
3198   #endif
3199   #ifdef TEXTTYPE_SUPPORT
3200   if (strucmp(aBaseType,"text")==0)
3201     return new TTextTypeConfig(aName,aParentP);
3202   else
3203   #endif
3204   #ifdef DATAOBJ_SUPPORT
3205   if (strucmp(aBaseType,"dataobj")==0)
3206     return new TDataObjConfig(aName,aParentP);
3207   else
3208   #endif
3209     return NULL; // unknown basetype
3210 } // TRootConfig::newDataTypeConfig
3211
3212
3213 // create new profile config by name
3214 // returns NULL if none found
3215 TProfileConfig *TRootConfig::newProfileConfig(const char *aName, const char *aTypeName, TConfigElement *aParentP)
3216 {
3217   // create profiles by name
3218   #ifdef MIMEDIR_SUPPORT
3219   if (strucmp(aTypeName,"mimeprofile")==0)
3220     return new TMIMEProfileConfig(aName,aParentP);
3221   else
3222   #endif
3223   #ifdef TEXTTYPE_SUPPORT
3224   if (strucmp(aTypeName,"textprofile")==0)
3225     return new TTextProfileConfig(aName,aParentP);
3226   else
3227   #endif
3228     return NULL; // unknown profile
3229 } // TRootConfig::newProfileConfig
3230
3231 } // namespace sysync
3232
3233 #endif
3234
3235
3236
3237
3238 // only one of XML2GO or SDK/Plugin can be on top of customagent
3239 #ifdef XML2GO_SUPPORT
3240   #include "xml2goapiagent.h"
3241 #elif defined(SDK_SUPPORT)
3242   #include "pluginapiagent.h"
3243 #endif
3244 // ODBC can be in-between if selected
3245 #ifdef SQL_SUPPORT
3246   #include "odbcapiagent.h"
3247 #endif
3248
3249
3250 namespace sysync {
3251
3252 // Create agent config - default to customImpl based agent
3253 void TRootConfig::installAgentConfig(void)
3254 {
3255   #if defined(XML2GO_SUPPORT)
3256   fAgentConfigP = new TXml2goAgentConfig(this); // xml2go (eventually on top of ODBC)
3257   #elif defined(SDK_SUPPORT)
3258   fAgentConfigP = new TPluginAgentConfig(this); // plugin/SDK (eventually on top of ODBC)
3259   #elif defined(SQL_SUPPORT)
3260   fAgentConfigP = new TOdbcAgentConfig(this); // ODBC only
3261   #else
3262   fAgentConfigP = NULL; // none
3263   #endif
3264 } // TSyncAppBase::installAgentConfig
3265
3266
3267 #ifndef HARDCODED_CONFIG
3268
3269 bool TRootConfig::parseAgentConfig(const char **aAttributes, sInt32 aLine)
3270 {
3271   const char *typenam = getAttr(aAttributes,"type");
3272   bool parseit=false;
3273
3274   #ifdef XML2GO_SUPPORT
3275   if (strucmp(typenam,"xml2go")==0) parseit=true;
3276   #endif
3277   #ifdef SDK_SUPPORT
3278   if (strucmp(typenam,"plugin")==0) parseit=true;
3279   #endif
3280   #ifdef SQL_SUPPORT
3281   if (strucmp(typenam,"odbc")==0 || strucmp(typenam,"sql")==0) parseit=true;
3282   #endif
3283   if (parseit) {
3284     expectChildParsing(*fAgentConfigP);
3285   }
3286   return parseit;
3287 } // TRootConfig::parseAgentConfig
3288
3289 #endif
3290
3291
3292 // Support for SySync Diagnostic Tool
3293 #ifdef SYSYNC_TOOL
3294
3295 #include <errno.h>
3296
3297 // special RTK instance just for translating WBXML to XML
3298
3299
3300 #define GET_XMLOUTINSTANCE(u) ((InstanceID_t)GET_USERDATA(u))
3301
3302 // SyncML toolkit callback implementations for sysytool debug decoder
3303
3304 // userData must be instance_id of XML instance to generate XML message into
3305
3306 static Ret_t sysytoolStartMessageCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncHdrPtr_t pContent)
3307 {
3308   // Note: we must find the SyncML version in advance, as fSyncMLVersion is not yet valid here
3309   sInt16 hdrVers;
3310   StrToEnum(SyncMLVerDTDNames,numSyncMLVersions,hdrVers,smlPCDataToCharP(pContent->version));
3311   smlStartMessageExt(GET_XMLOUTINSTANCE(userData),pContent,SmlVersionCodes[hdrVers]);
3312   return SML_ERR_OK;
3313 }
3314
3315 static Ret_t sysytoolEndMessageCallback(InstanceID_t id, VoidPtr_t userData, Boolean_t final)
3316 {
3317   smlEndMessage(GET_XMLOUTINSTANCE(userData),final);
3318   return SML_ERR_OK;
3319 }
3320
3321 static Ret_t sysytoolStartSyncCallback(InstanceID_t id, VoidPtr_t userData, SmlSyncPtr_t pContent)
3322 {
3323   smlStartSync(GET_XMLOUTINSTANCE(userData),pContent);
3324   return SML_ERR_OK;
3325 }
3326
3327 static Ret_t sysytoolEndSyncCallback(InstanceID_t id, VoidPtr_t userData)
3328 {
3329   smlEndSync(GET_XMLOUTINSTANCE(userData));
3330   return SML_ERR_OK;
3331 }
3332
3333
3334 #ifdef ATOMIC_RECEIVE  /* these callbacks are NOT included in the Toolkit lite version */
3335
3336 static Ret_t sysytoolStartAtomicCallback(InstanceID_t id, VoidPtr_t userData, SmlAtomicPtr_t pContent)
3337 {
3338   smlStartAtomic(GET_XMLOUTINSTANCE(userData),pContent);
3339   return SML_ERR_OK;
3340 }
3341
3342 static Ret_t sysytoolEndAtomicCallback(InstanceID_t id, VoidPtr_t userData)
3343 {
3344   smlEndAtomic(GET_XMLOUTINSTANCE(userData));
3345   return SML_ERR_OK;
3346 }
3347
3348 #endif
3349
3350 #ifdef SEQUENCE_RECEIVE
3351
3352 static Ret_t sysytoolStartSequenceCallback(InstanceID_t id, VoidPtr_t userData, SmlSequencePtr_t pContent)
3353 {
3354   smlStartSequence(GET_XMLOUTINSTANCE(userData),pContent);
3355   return SML_ERR_OK;
3356 }
3357
3358 static Ret_t sysytoolEndSequenceCallback(InstanceID_t id, VoidPtr_t userData)
3359 {
3360   smlEndSequence(GET_XMLOUTINSTANCE(userData));
3361   return SML_ERR_OK;
3362 }
3363
3364 #endif
3365
3366 static Ret_t sysytoolAddCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAddPtr_t pContent)
3367 {
3368   smlAddCmd(GET_XMLOUTINSTANCE(userData),pContent);
3369   return SML_ERR_OK;
3370 }
3371
3372 static Ret_t sysytoolAlertCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlAlertPtr_t pContent)
3373 {
3374   smlAlertCmd(GET_XMLOUTINSTANCE(userData),pContent);
3375   return SML_ERR_OK;
3376 }
3377
3378
3379 static Ret_t sysytoolDeleteCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlDeletePtr_t pContent)
3380 {
3381   smlDeleteCmd(GET_XMLOUTINSTANCE(userData),pContent);
3382   return SML_ERR_OK;
3383 }
3384
3385 static Ret_t sysytoolGetCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlGetPtr_t pContent)
3386 {
3387   smlGetCmd(GET_XMLOUTINSTANCE(userData),pContent);
3388   return SML_ERR_OK;
3389 }
3390
3391 static Ret_t sysytoolPutCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlPutPtr_t pContent)
3392 {
3393   smlPutCmd(GET_XMLOUTINSTANCE(userData),pContent);
3394   return SML_ERR_OK;
3395 }
3396
3397 #ifdef MAP_RECEIVE
3398 static Ret_t sysytoolMapCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMapPtr_t pContent)
3399 {
3400   smlMapCmd(GET_XMLOUTINSTANCE(userData),pContent);
3401   return SML_ERR_OK;
3402 }
3403 #endif
3404
3405 #ifdef RESULT_RECEIVE
3406 static Ret_t sysytoolResultsCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlResultsPtr_t pContent)
3407 {
3408   smlResultsCmd(GET_XMLOUTINSTANCE(userData),pContent);
3409   return SML_ERR_OK;
3410 }
3411 #endif
3412
3413 static Ret_t sysytoolStatusCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlStatusPtr_t pContent)
3414 {
3415   smlStatusCmd(GET_XMLOUTINSTANCE(userData),pContent);
3416   return SML_ERR_OK;
3417 }
3418
3419 static Ret_t sysytoolReplaceCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlReplacePtr_t pContent)
3420 {
3421   smlReplaceCmd(GET_XMLOUTINSTANCE(userData),pContent);
3422   return SML_ERR_OK;
3423 }
3424
3425 #ifdef COPY_RECEIVE
3426 static Ret_t sysytoolCopyCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlCopyPtr_t pContent)
3427 {
3428   smlCopyCmd(GET_XMLOUTINSTANCE(userData),pContent);
3429   return SML_ERR_OK;
3430 }
3431 #endif
3432
3433 static Ret_t sysytoolMoveCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlMovePtr_t pContent)
3434 {
3435   smlMoveCmd(GET_XMLOUTINSTANCE(userData),pContent);
3436   return SML_ERR_OK;
3437 }
3438
3439 #ifdef EXEC_RECEIVE
3440 static Ret_t sysytoolExecCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlExecPtr_t pContent)
3441 {
3442   smlExecCmd(GET_XMLOUTINSTANCE(userData),pContent);
3443   return SML_ERR_OK;
3444 }
3445 #endif
3446
3447
3448 #ifdef SEARCH_RECEIVE
3449 static Ret_t sysytoolSearchCmdCallback(InstanceID_t id, VoidPtr_t userData, SmlSearchPtr_t pContent)
3450 {
3451   smlSearchCmd(GET_XMLOUTINSTANCE(userData),pContent);
3452   return SML_ERR_OK;
3453 }
3454 #endif
3455
3456
3457 static Ret_t sysytoolHandleErrorCallback(InstanceID_t id, VoidPtr_t userData)
3458 {
3459   return SML_ERR_OK;
3460 }
3461
3462 static Ret_t sysytoolTransmitChunkCallback(InstanceID_t id, VoidPtr_t userData)
3463 {
3464   return SML_ERR_INVALID_OPTIONS;
3465 }
3466
3467
3468 static const SmlCallbacks_t sysyncToolCallbacks = {
3469   /* message callbacks */
3470   sysytoolStartMessageCallback,
3471   sysytoolEndMessageCallback,
3472   /* grouping commands */
3473   sysytoolStartSyncCallback,
3474   sysytoolEndSyncCallback,
3475   #ifdef ATOMIC_RECEIVE  /* these callbacks are NOT included in the Toolkit lite version */
3476     sysytoolStartAtomicCallback,
3477     sysytoolEndAtomicCallback,
3478   #endif
3479   #ifdef SEQUENCE_RECEIVE
3480     sysytoolStartSequenceCallback,
3481     sysytoolEndSequenceCallback,
3482   #endif
3483   /* Sync Commands */
3484   sysytoolAddCmdCallback,
3485   sysytoolAlertCmdCallback,
3486   sysytoolDeleteCmdCallback,
3487   sysytoolGetCmdCallback,
3488   sysytoolPutCmdCallback,
3489   #ifdef MAP_RECEIVE
3490     sysytoolMapCmdCallback,
3491   #endif
3492   #ifdef RESULT_RECEIVE
3493     sysytoolResultsCmdCallback,
3494   #endif
3495   sysytoolStatusCmdCallback,
3496   sysytoolReplaceCmdCallback,
3497   /* other commands */
3498   #ifdef COPY_RECEIVE  /* these callbacks are NOT included in the Toolkit lite version */
3499     sysytoolCopyCmdCallback,
3500   #endif
3501   #ifdef EXEC_RECEIVE
3502     sysytoolExecCmdCallback,
3503   #endif
3504   #ifdef SEARCH_RECEIVE
3505     sysytoolSearchCmdCallback,
3506   #endif
3507   sysytoolMoveCmdCallback,
3508   /* Other Callbacks */
3509   sysytoolHandleErrorCallback,
3510   sysytoolTransmitChunkCallback
3511 }; /* sml_callbacks struct */
3512
3513
3514
3515 // WBXML to XML conversion
3516 int wbxmlConv(int argc, const char *argv[])
3517 {
3518   if (argc<0) {
3519     // help requested
3520     CONSOLEPRINTF(("  wbxml2xml <wbxml binary message file> [<xml output file>]"));
3521     CONSOLEPRINTF(("    Converts to XML using SyncML-Toolkit"));
3522     CONSOLEPRINTF(("    If no output file is specified, input file name with suffix '.xml' is used"));
3523     return EXIT_SUCCESS;
3524   }
3525
3526   // check for argument
3527   if (argc<1 || argc>2) {
3528     CONSOLEPRINTF(("1 or 2 arguments required"));
3529     return EXIT_FAILURE;
3530   }
3531
3532   // open input file
3533   FILE *inFile = fopen(argv[0],"rb");
3534   if (!inFile) {
3535     CONSOLEPRINTF(("Error opening input file '%s', error=%d",argv[0],errno));
3536     return EXIT_FAILURE;
3537   }
3538
3539   // prepare a instance for decoding the WBXML
3540   InstanceID_t wbxmlInInstance;
3541   if (!getSyncAppBase()->newSmlInstance(
3542     SML_WBXML,
3543     500*1024, // 500k should be waaaaay enough
3544     wbxmlInInstance
3545   )) {
3546     CONSOLEPRINTF(("Error creating WBXML parser"));
3547     return EXIT_FAILURE;
3548   }
3549   // install the sysytool callbacks (default are the normal appbase ones)
3550   smlSetCallbacks(wbxmlInInstance, &sysyncToolCallbacks);
3551
3552   // prepare instance for generating XML output
3553   InstanceID_t xmlOutInstance;
3554   if (!getSyncAppBase()->newSmlInstance(
3555     SML_XML,
3556     500*1024, // 500k should be waaaaay enough
3557     xmlOutInstance
3558   )) {
3559     CONSOLEPRINTF(("Error creating XML generator"));
3560     return EXIT_FAILURE;
3561   }
3562   // link output instance as userdata into input instance
3563   getSyncAppBase()->setSmlInstanceUserData(wbxmlInInstance, xmlOutInstance);
3564
3565   // read WBXML original file into workspace
3566   MemPtr_t wbxmlBufP;
3567   MemSize_t wbxmlBufSiz, wbxmlDataSiz;
3568   Ret_t rc;
3569   rc = smlLockWriteBuffer(wbxmlInInstance, &wbxmlBufP, &wbxmlBufSiz);
3570   if (rc!=SML_ERR_OK) {
3571     CONSOLEPRINTF(("Error getting WBXML buffer, err=%d",rc));
3572     return EXIT_FAILURE;
3573   }
3574   wbxmlDataSiz = fread(wbxmlBufP,1,wbxmlBufSiz,inFile);
3575   if (wbxmlDataSiz==0) {
3576     CONSOLEPRINTF(("No data in WBXML input file or error, err=%d",errno));
3577     return EXIT_FAILURE;
3578   }
3579   fclose(inFile);
3580   CONSOLEPRINTF(("Read %ld bytes from WBXML input file '%s'",wbxmlDataSiz,argv[0]));
3581   smlUnlockWriteBuffer(wbxmlInInstance,wbxmlDataSiz);
3582
3583   // decode
3584   do {
3585     rc = smlProcessData(wbxmlInInstance, SML_NEXT_COMMAND);
3586   } while (rc==SML_ERR_CONTINUE);
3587   if (rc!=SML_ERR_OK) {
3588     CONSOLEPRINTF(("Error while decoding WBXML document, rc=%d",rc));
3589   }
3590   else {
3591     CONSOLEPRINTF(("Successfully completed decoding WBXML document"));
3592   }
3593
3594   // write to output file
3595   // - determine name
3596   string outFileName;
3597   outFileName = argv[0];
3598   if (argc>1)
3599     outFileName = argv[1];
3600   else
3601     outFileName += ".xml";
3602   // save output
3603   FILE *outFile = fopen(outFileName.c_str(),"w");
3604   if (!outFile) {
3605     CONSOLEPRINTF(("Error opening output file '%s', error=%d",outFileName.c_str(),errno));
3606     return EXIT_FAILURE;
3607   }
3608   MemPtr_t xmlBufP;
3609   MemSize_t xmlDataSiz;
3610   rc = smlLockReadBuffer(xmlOutInstance,&xmlBufP,&xmlDataSiz);
3611   if (rc!=SML_ERR_OK) {
3612     CONSOLEPRINTF(("Error reading decoded XML from converter WBXML document, rc=%d",rc));
3613     return EXIT_FAILURE;
3614   }
3615   CONSOLEPRINTF(("Writing %ld bytes of XML translation to '%s'",xmlDataSiz,outFileName.c_str()));
3616   fwrite(xmlBufP,1,xmlDataSiz,outFile);
3617   fclose(outFile);
3618   // done
3619   return EXIT_SUCCESS;
3620 } // wbxmlConv
3621
3622 #endif // SYSYNC_TOOL
3623
3624
3625
3626 } // namespace sysync
3627
3628 // eof