Imported Upstream version 1.0beta3
[platform/upstream/syncevolution.git] / src / synthesis / src / sysync / dataobjtype.cpp
1 /*
2  *  File:         DataObjType.cpp
3  *
4  *  Author:       Beat Forster (bfo@synthesis.ch)
5  *
6  *  TDataObjType
7  *    base class for data object based items (EMAILOBJ, FILEOBJ, FOLDEROBJ)
8  *    implemented for OMA DS V1.2
9  *
10  *  Copyright (c) 2005-2009 by Synthesis AG (www.synthesis.ch)
11  *
12  *  2005-07-20 : bfo : created from TextItemType
13  *
14  */
15
16 // includes
17 #include "prefix_file.h"
18
19 #include "sysync.h"
20 #include "dataobjtype.h"
21
22
23 using namespace sysync;
24
25 namespace sysync {
26
27 const char* BeginCDATA=          "<![CDATA[";
28 const char*   EndCDATA= "]]>";
29 const char* LowerCDATA= "]]]]>&gt;<![CDATA["; // only for the xml representation
30 const char* DoubleCEnd= "]]]]";
31
32
33 // Config
34 // ======
35 TTagMapDefinition::TTagMapDefinition(TConfigElement *aParentElementP, sInt16 aFid) :
36   TConfigElement("lm",aParentElementP)
37 {
38   fFid= aFid;  // save field ID
39   clear();     // init others
40 } // TTagMapDefinition::TTagMapDefinition
41
42
43
44 void TTagMapDefinition::clear(void)
45 {
46   // clear
47   TCFG_CLEAR(fXmlTag);
48   TCFG_CLEAR(fXmlAttr);
49   fBoolType= false;
50   fEmbedded= false;
51   TCFG_CLEAR(fParent);
52   #ifdef OBJECT_FILTERING
53   TCFG_CLEAR(fFilterKeyword);
54   #endif
55 } // TTagMapDefinition::clear
56
57
58
59 #ifdef CONFIGURABLE_TYPE_SUPPORT
60 // server config element parsing
61 bool TTagMapDefinition::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
62 {
63   // checking the elements
64   if      (strucmp(aElementName,"xmltag"       )==0) expectString(fXmlTag);        // the tag's name
65   else if (strucmp(aElementName,"xmlattr"      )==0) expectString(fXmlAttr);       // and its (optional) attribute(s)
66   else if (strucmp(aElementName,"booltype"     )==0) expectBool  (fBoolType);      // a boolean field
67   else if (strucmp(aElementName,"embeddeditem" )==0) expectBool  (fEmbedded);      // an embedded item
68   else if (strucmp(aElementName,"parent"       )==0) expectString(fParent);        // the parent's tag name
69   #ifdef OBJECT_FILTERING
70   else if (strucmp(aElementName,"filterkeyword")==0) expectString(fFilterKeyword); // the parent's tag name
71   #endif
72
73   // - none known here
74   else return TConfigElement::localStartElement(aElementName,aAttributes,aLine);
75
76   // ok
77   return true;
78 } // TTagMapDefinition::localStartElement
79 #endif
80
81
82 TTagMapDefinition::~TTagMapDefinition() {
83   // nop so far
84 } // TTagMapDefinition::~TTagMapDefinition
85
86
87
88 // -------------------------------------------------------------------------------------------
89 // XML based datatype config
90 TDataObjConfig::TDataObjConfig(const char* aName, TConfigElement *aParentElement) :
91   TMultiFieldTypeConfig(aName,aParentElement)
92 {
93   clear();
94 } // TDataObjConfig::TDataObjConfig
95
96
97 TDataObjConfig::~TDataObjConfig()
98 {
99   // make sure everything is deleted (was missing long time and caused mem leaks!)
100   clear();
101 } // TDataObjConfig::~TDataObjConfig
102
103
104 // init defaults
105 void TDataObjConfig::clear(void)
106 {
107   // clear properties
108   // - remove tag maps
109   TTagMapList::iterator pos;
110   for (pos= fTagMaps.begin();
111        pos!=fTagMaps.end(); pos++) delete *pos;
112
113   fTagMaps.clear();
114   // clear inherited
115   inherited::clear();
116 } // TDataObjConfig::clear
117
118
119 // create Sync Item Type of appropriate type from config
120 TSyncItemType *TDataObjConfig::newSyncItemType(TSyncSession *aSessionP, TSyncDataStore *aDatastoreP)
121 {
122   if (!fFieldListP)
123     SYSYNC_THROW(TSyncException(DEBUGTEXT("TDataObjConfig::newSyncItemType: no fFieldListP","txit1")));
124   return
125     new TDataObjType(
126       aSessionP,
127       this,
128       fTypeName.c_str(),
129       fTypeVersion.c_str(),
130       aDatastoreP,
131       fFieldListP
132     );
133 } // TDataObjConfig::newSyncItemType
134
135
136 #ifdef CONFIGURABLE_TYPE_SUPPORT
137 // config element parsing
138 bool TDataObjConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
139 {
140   // checking the elements
141   if (strucmp(aElementName,"tagmap")==0) {
142     // <tagmap field="SUBJECT">
143     const char* nam = getAttr(aAttributes,"field");
144
145     if (!fFieldListP)
146       return fail("'use' must be specfied before first <tagmap>");
147     // search field
148     // %%% add context here if we have any
149     sInt16 fid = TConfigElement::getFieldIndex(nam,fFieldListP);
150     if    (fid==VARIDX_UNDEFINED) return fail("'field' references unknown field '%s'",nam);
151
152     // create new tagmap
153     TTagMapDefinition *tagmapP= new TTagMapDefinition(this,fid);
154     // - save in list
155     fTagMaps.push_back(tagmapP);
156     // - let element handle parsing
157     expectChildParsing(*tagmapP);
158     return true;
159   } // if
160
161   // - none known here
162   if (TMultiFieldTypeConfig::localStartElement(aElementName,aAttributes,aLine)) return true;
163
164   // let the profile parse as if it was inside a "textprofile"
165   if (fProfileConfigP)
166     return delegateParsingTo(fProfileConfigP,aElementName,aAttributes,aLine);
167
168   return false; // cannot parse
169 } // TDataObjConfig::localStartElement
170
171
172 // resolve
173 void TDataObjConfig::localResolve(bool aLastPass)
174 {
175   // nop
176   // resolve inherited
177   inherited::localResolve(aLastPass);
178 } // TDataObjConfig::localResolve
179 #endif
180
181
182 #ifdef HARDCODED_TYPE_SUPPORT
183 TTagMapDefinition *TDataObjConfig::addTagMap( sInt16 aFid, const char* aXmlTag,
184                                               bool aBoolType, bool aEmbedded,
185                                               const char* aParent )
186 {
187   // create new tagmap
188   TTagMapDefinition *tagmapP = new TTagMapDefinition(this,aFid);
189
190   // save the options
191   TCFG_ASSIGN(tagmapP->fXmlTag,aXmlTag);
192   tagmapP->fBoolType= aBoolType;
193   tagmapP->fEmbedded= aEmbedded;
194   tagmapP->fParent  = aParent;
195
196   // save in list
197   fTagMaps.push_back(tagmapP);
198   // return pointer
199   return tagmapP;
200 } // TDataObjConfig::addTagMap
201 #endif
202
203
204
205 /*
206  * Implementation of TDataObjType
207  */
208 TDataObjType::TDataObjType(
209   TSyncSession *aSessionP,
210   TDataTypeConfig *aTypeCfgP, // type config
211   const char *aCTType,
212   const char *aVerCT,
213   TSyncDataStore *aRelatedDatastoreP,
214   TFieldListConfig *aFieldDefinitions // field definitions
215 ) :
216   TMultiFieldItemType(aSessionP,aTypeCfgP,aCTType,aVerCT,aRelatedDatastoreP,aFieldDefinitions),
217   fProfileHandlerP(NULL)
218 {
219   // save typed config pointer again
220   fTypeCfgP = static_cast<TDataObjConfig *>(aTypeCfgP);
221   // create the profile handler
222   fProfileHandlerP =  static_cast<TDataObjConfig *>(aTypeCfgP)->fProfileConfigP->newProfileHandler(this);
223   // set profile mode
224   fProfileHandlerP->setProfileMode(fTypeCfgP->fProfileMode);
225 } // TDataObjType::TDataObjType
226
227
228 TDataObjType::~TDataObjType()
229 {
230   // handler not needed any more
231   if (fProfileHandlerP)
232         delete fProfileHandlerP;
233 } // TDataObjType::~TDataObjType
234
235
236
237 #ifdef OBJECT_FILTERING
238 // get field index of given filter expression identifier.
239 sInt16 TDataObjType::getFilterIdentifierFieldIndex(const char *aIdentifier, uInt16 aIndex)
240 {
241   // check if explicit field level identifier
242   if (strucmp(aIdentifier,"F.",2)==0) {
243     // explicit field identifier, skip property lookup
244     aIdentifier+=2;
245   }
246   else {
247     // search tagmaps for tagged fields
248     TTagMapList::iterator pos;
249     for (pos= fTypeCfgP->fTagMaps.begin();
250          pos!=fTypeCfgP->fTagMaps.end(); pos++) {
251       // first priority: compare with explicit filterkeyword, if any
252       if (!TCFG_ISEMPTY((*pos)->fFilterKeyword)) {
253         // compare with filterkeyword
254         if (strucmp(TCFG_CSTR((*pos)->fFilterKeyword),aIdentifier)==0)
255           return (*pos)->fFid;
256       }
257       else if (!TCFG_ISEMPTY((*pos)->fXmlTag)) {
258         // compare with xml tag name itself
259         if (strucmp(TCFG_CSTR((*pos)->fXmlTag),aIdentifier)==0)
260           return (*pos)->fFid;
261       } // if
262     } // for
263     // no matching tag found, let TTextProfileHandler search for its own filter identifiers
264     if (fProfileHandlerP) {
265       sInt16 fid = fProfileHandlerP->getFilterIdentifierFieldIndex(aIdentifier,aIndex);
266       if (fid!=FID_NOT_SUPPORTED)
267         return fid;
268     }
269     // no tagged field found, search underlying field list
270   }
271   // if no field ID found so far, look up in field list
272   return TMultiFieldItemType::getFilterIdentifierFieldIndex(aIdentifier, aIndex);
273 } // TDataObjType::getFilterIdentifierFieldIndex
274 #endif
275
276
277
278 // helper to create same-typed instance via base class
279 TSyncItemType *TDataObjType::newCopyForSameType(
280   TSyncSession *aSessionP,     // the session
281   TSyncDataStore *aDatastoreP  // the datastore
282 )
283 {
284   // create new itemtype of appropriate derived class type that can handle
285   // this type
286   MP_RETURN_NEW(TDataObjType,DBG_OBJINST,"TDataObjType",TDataObjType(
287     aSessionP,
288     fTypeConfigP,
289     getTypeName(),
290     getTypeVers(),
291     aDatastoreP,
292     fFieldDefinitionsP
293   ));
294 } // TDataObjType::newCopyForSameType
295
296
297 // create new sync item of proper type and optimization for specified target
298 TSyncItem *TDataObjType::internalNewSyncItem(TSyncItemType *aTargetItemTypeP, TLocalEngineDS *aLocalDatastoreP)
299 {
300   // All DataObjs are stored in MultiFieldItems
301   if (!aTargetItemTypeP->isBasedOn(ity_multifield))
302     SYSYNC_THROW(TSyncException(DEBUGTEXT("TDataObjType::internalNewSyncItem with bad-typed target","txit3")));
303   TMultiFieldItemType *targetitemtypeP =
304     static_cast<TMultiFieldItemType *> (aTargetItemTypeP);
305   return new TMultiFieldItem(this,targetitemtypeP);
306 } // TDataObjType::internalNewSyncItem
307
308
309 // fill in SyncML data (but leaves IDs empty)
310 bool TDataObjType::internalFillInData(
311   TSyncItem *aSyncItemP,      // SyncItem to be filled with data
312   SmlItemPtr_t aItemP,        // SyncML toolkit item Data to be converted into SyncItem (may be NULL if no data, in case of Delete or Map)
313   TLocalEngineDS *aLocalDatastoreP, // local datastore
314   TStatusCommand &aStatusCmd  // status command that might be modified in case of error
315 )
316 {
317   // check type
318   if (!aSyncItemP->isBasedOn(ity_multifield))
319     SYSYNC_THROW(TSyncException(DEBUGTEXT("TDataObjType::internalFillInData: incompatible item class","txit4")));
320   TMultiFieldItem *itemP = static_cast<TMultiFieldItem *> (aSyncItemP);
321   // process data if any
322   if (aItemP->data) {
323     // parse data
324     stringSize sz;
325     cAppCharP t = smlPCDataToCharP(aItemP->data,&sz);
326     if (!parseData(t,sz,*itemP,aLocalDatastoreP)) {
327       // format error
328       aStatusCmd.setStatusCode(415); // Unsupported media type or format
329       ADDDEBUGITEM(aStatusCmd,"Error parsing Text content");
330       return false;
331     }
332   }
333   else {
334     // no data
335     aStatusCmd.setStatusCode(412); // incomplete command
336     ADDDEBUGITEM(aStatusCmd,"No data found in item");
337     return false;
338   }
339   // ok, let ancestor process data as well
340   return TMultiFieldItemType::internalFillInData(aSyncItemP,aItemP,aLocalDatastoreP,aStatusCmd);
341 } // TDataObjType::internalFillInData
342
343
344 // sets data and meta from SyncItem data, but leaves source & target untouched
345 bool TDataObjType::internalSetItemData(
346   TSyncItem *aSyncItemP,  // the syncitem to be represented as SyncML
347   SmlItemPtr_t aItem,     // item with NULL meta and NULL data
348   TLocalEngineDS *aLocalDatastoreP // local datastore
349 )
350 {
351   // check type
352   if (!aSyncItemP->isBasedOn(ity_multifield))
353     SYSYNC_THROW(TSyncException(DEBUGTEXT("TDataObjType::internalSetItemData: incompatible item class","txit4")));
354   TMultiFieldItem *itemP = static_cast<TMultiFieldItem *> (aSyncItemP);
355   // let ancestor prepare first
356   if (!TMultiFieldItemType::internalSetItemData(aSyncItemP,aItem,aLocalDatastoreP)) return false;
357   // generate data item
358   string dataitem;
359   generateData(aLocalDatastoreP,*itemP,dataitem);
360   // put data item into opaque/cdata PCData (note that dataitem could be BINARY string, so we need to pass size!)
361   aItem->data=newPCDataStringX((const uInt8 *)dataitem.c_str(),true,dataitem.size());
362   // can't go wrong
363   return true;
364 } // TDataObjType::internalSetItemData
365
366
367
368 static void AddXmlTag( string &aString, string aTag, bool newline= false )
369 {
370                aString+= "<"  + aTag + ">";
371   if (newline) aString+= "\n";
372 } // AddXmlTag
373
374
375 static void AddXmlTagEnd( string &aString, string aTag, bool newline= true )
376 {
377                aString+= "</"  + aTag + ">";
378   if (newline) aString+= "\n";
379 } // AddXmlTagEnd
380
381
382 static void AppendXml( string &aString, string aTag, const char* value, bool tagDone= false )
383 {
384   if (*value!='\0') {
385     if (!tagDone) AddXmlTag( aString, aTag );
386
387                   aString+= value;
388     AddXmlTagEnd( aString, aTag );
389   } // if
390 } // AppendXml
391
392
393 static void AppendCDATA( string &aString, const char* aTag, const char* aValue )
394 {
395   AddXmlTag( aString, aTag ); // must be shown according to OMA DS 1.2,
396                               // even if the body is empty
397   if (*aValue!='\0') {
398     aString+= BeginCDATA; // only for the xml representation
399     aString+= aValue;     // the body
400     //%%% luz: not needed here as there is no enclosing CData any more (or this will be done when needed by the SML encoder!)
401     //aString+= LowerCDATA; // only for the xml representation
402     aString+=  '\n';
403   } // if
404
405   AddXmlTagEnd( aString, aTag );
406 } // AppendCDATA
407
408
409 string TDataObjType::getAttr( TMultiFieldItem &aItem, const char* aXmlTag, const char* aXmlAttr )
410 {
411   TItemField* fieldP;
412   TTagMapList::iterator pos;
413   string v;
414
415   // search for an element, which matches for both <aXmlTag> and <aXmlAttr>
416   for (pos= fTypeCfgP->fTagMaps.begin();
417        pos!=fTypeCfgP->fTagMaps.end(); pos++) {
418      fieldP= aItem.getField( (*pos)->fFid ); // this is the element we're looking for
419      if (strcmp( aXmlTag, TCFG_CSTR((*pos)->fXmlTag ))==0 &&
420          strcmp( aXmlAttr,TCFG_CSTR((*pos)->fXmlAttr))==0) {
421        fieldP->getAsString( v ); // this is the attribute we're looking for
422        break;
423      } // if
424   } // for
425
426   return v; // <v> is "", if no attribute found
427 } // TDataObjType::getAttr
428
429
430 // generate Data item (includes header and footer)
431 void TDataObjType::generateData(TLocalEngineDS *aDatastoreP, TMultiFieldItem &aItem, string &aString)
432 {
433   #ifdef SYDEBUG
434   PDEBUGPRINTFX(DBG_GEN+DBG_HOT,("Generating:") );
435   aItem.debugShowItem(DBG_GEN);
436   #endif
437
438   TItemField* fieldP;
439   TItemField* fieldDocType; // the reference to the "DOCTYPE" (File/Folder/Email) field
440   TTagMapList::iterator pos;
441
442   string docT, v;
443   string inParent;
444   bool   tagOpened= false;
445
446   //%%% luz: do not add extra CData here, this will be done when needed by the SML encoder!
447   //aString= BeginCDATA; // start a CDATA sequence
448
449       fieldDocType= aItem.getField( "DOCTYPE" );
450   if (fieldDocType) {
451       fieldDocType->getAsString( docT ); // main tag, straight forward implementation
452     AddXmlTag         ( aString, docT, true );
453   } // if
454
455   // go thru the tag element list and get the matching element
456   for (pos= fTypeCfgP->fTagMaps.begin();
457        pos!=fTypeCfgP->fTagMaps.end(); pos++) {
458          fieldP= aItem.getField( (*pos)->fFid ); // this is the element we're looking for
459     if (!fieldP ||                               // not a valid field
460          fieldP==fieldDocType) continue;         // has been done outside already, ignore it
461
462     bool isArr= fieldP->isArray();
463     sInt16      maxI= 1; // do it once w/o array
464     if  (isArr) maxI= fieldP->arraySize();
465     if         (maxI==0) continue; // it is an array w/o any items, skip it
466
467     if ((*pos)->fEmbedded && fProfileHandlerP) {
468       // set related datastore so handler can access session specific datastore state
469                 fProfileHandlerP->setRelatedDatastore(aDatastoreP);
470       fProfileHandlerP->generateText(aItem, v);
471       AppendCDATA( aString, TCFG_CSTR((*pos)->fXmlTag), v.c_str());
472       continue; // it's done now for this element
473     } // if
474
475     // the standard case (not embedded): cover both cases: array AND no array
476     sInt16 i;
477     for (i= 0; i<maxI; i++) {
478       TItemField* fieldAP= fieldP;
479       if (isArr)  fieldAP= fieldP->getArrayField( i );
480
481       if (fieldAP->isAssigned() &&
482           TCFG_ISEMPTY((*pos)->fParent) &&
483           !inParent.empty()) { AddXmlTagEnd( aString,inParent.c_str() ); inParent= ""; }
484
485       // there is no native bool representation for the field lists
486       // So do it with the help of an additional flag
487       v= "";
488       if   (fieldAP->isAssigned()) {
489         if (fieldAP->isBasedOn( fty_blob ) &&
490             fieldAP->hasProxy()) {
491           #ifdef STREAMFIELD_SUPPORT
492             string enc= getAttr( aItem, TCFG_CSTR((*pos)->fXmlTag), "enc" );
493
494             // according OMA DS 1.2, the endcoding attribute "enc" must be
495             // either not present (no encoding), "quoted-printable" or "base64".
496             // The implementation SHOULD NOT use other enc attribute values.
497             const char*  ptr = fieldAP->getCStr();
498             size_t       size= fieldAP->getStreamSize();
499
500             if (enc.empty()) {      // no encoding
501               v.assign( ptr,size ); // => assign directly
502             }
503             else if (strucmp( enc.c_str(),"base64" )==0) {
504               char*        bb= b64::encode( (uInt8*)ptr,size );
505               v=           bb;   // maybe one copy operation can be avoided in future
506               b64::free( bb ); // does never contain '\0' => can be treated as normal string
507             }
508             else if (strucmp( enc.c_str(),"quoted-printable")==0) {
509               sysync::appendEncoded( (uInt8*)ptr,size, v, enc_quoted_printable,
510                                      MIME_MAXLINESIZE,0,true,true );
511             }
512             else {
513               v = "unknown_encoding: \"";
514               v+=  enc;
515               v+= "\"";
516             }
517           #endif
518         }
519         else if ((*pos)->fBoolType) {   // best fitting for integer and string
520           int      ii= fieldAP->getAsInteger();
521           switch ( ii ) {                // this is the integer - boolean mapping
522             case -1: v= "false"; break; // a strange value for "false"
523             case  0: v= "";      break; // 0 is the empty value (not assigned at parsing)
524             case  1: v= "true";  break; // the 'normal' value for "true"
525           } // switch
526         } // if
527         else fieldAP->getAsString( v );
528
529         if (!v.empty()) {
530           // avoid multiple identical elements (even if it's allowed in OMA DS 1.2)
531           if  (!TCFG_ISEMPTY((*pos)->fParent)) {
532             bool ok=                inParent.empty(); // either iParent==0 or strings are different
533             if (!ok &&     strucmp( inParent.c_str(),TCFG_CSTR((*pos)->fParent) )!=0) {
534               AddXmlTagEnd( aString,inParent.c_str() );   // switch off old tag first, if different
535               ok= true;
536             } // if
537
538             if (ok) {            inParent= (*pos)->fParent;
539               AddXmlTag( aString,inParent.c_str(), true );
540             } // if
541           } // if
542         } // if
543       } // if
544
545       if (TCFG_ISEMPTY((*pos)->fXmlAttr)) { // w/o attributes
546         if (tagOpened) aString+= ">";
547         AppendXml    ( aString, (*pos)->fXmlTag, v.c_str(), tagOpened );
548         tagOpened= false;
549       }
550       else if (!v.empty()) { // with XML attribute handling
551         if (!tagOpened) {
552           aString+= "<";
553           aString+= TCFG_CSTR((*pos)->fXmlTag);
554         } // if
555
556         aString+=   " ";
557         aString+=   TCFG_CSTR((*pos)->fXmlAttr);
558         aString+=   "=\"" + v + "\"";
559         tagOpened= true;
560       } // if
561     } // for
562
563   } // for
564   // Leave the block finally
565   if (!inParent.empty()) AddXmlTagEnd( aString,inParent.c_str());
566
567   if (fieldDocType) {
568       fieldDocType->getAsString( docT ); // end of main tag, straight forward implementation
569     AddXmlTagEnd      ( aString, docT );
570   } // if
571
572   //%%% luz: do not add extra CData here, this will be done when needed by the SML encoder!
573   //aString+= EndCDATA; // terminate the sequence
574
575   #ifdef SYDEBUG
576     if (PDEBUGTEST(DBG_GEN)) {
577       // note, do not use debugprintf because string is too long
578       PDEBUGPRINTFX(DBG_GEN,("%s", "") );
579       PDEBUGPRINTFX(DBG_GEN,("Successfully generated:") );
580       PDEBUGPUTSX  (DBG_GEN+DBG_USERDATA, aString.c_str() );
581       PDEBUGPRINTFX(DBG_GEN+DBG_USERDATA,("%s", "") );
582     } // if
583   #endif
584 } // TDataObjType::generateData
585
586
587 /*! Skip white spaces */
588 static void SkipWhiteSP( const char* &p )
589 {
590   while (*p==0x0D || // CR
591          *p==0x0A || // LF
592          *p==0x09 || // HT
593          *p==' ') p++; // skip leading CR/LF/Tabs and spaces
594 } // SkipWhiteSP
595
596
597 // simple straight forward letter recognition
598 static bool IsLetter( char ch )
599 {
600   ch= toupper(ch);
601   return ch>='A' && ch<='Z';
602 } // IsLetter
603
604
605 /*! Get the next tag and value of <p>
606  *
607  *  @param  <aTag>      may contain attributes, e.g. <body enc="base64">
608  *  @param  <direction> +1 go down in the hierarchy
609  *                       0 stay on the same level
610  *                      -1 go up   in the hierarchy
611  */
612 static bool NextTag( const char* &p, string &aTag, string &aAttr, const char* &aPos, int &aSize,
613                                      int &level, int &direction )
614 {
615   const char* BeginCom= "<!--";
616   const char*   EndCom=  "-->";
617   const char* q;
618
619   // default values
620   bool vDone= false;
621   aAttr     = "";
622   aPos      = "";
623   aSize     = 0;
624   direction = 0;
625
626   SkipWhiteSP( p );
627       q=  strstr( p,BeginCom ); // comment ?
628   if (q && q==p) {
629   }
630   else {
631     if (*p!='<') return false; // must start with a tag
632     p++;
633     if (*p=='/') { p++; direction= -1; }
634
635     q=   strstr( p,">" ); if (!q) return false;
636     aTag.assign( p, (unsigned int)( q-p ) );   // this is the tag
637
638     int pos= aTag.find(" ");
639     if (pos>=0) { // the tag contains some attributes
640       aAttr= aTag.substr( pos+1 ); //  ==> get them
641       aTag = aTag.substr( 0,pos ); // the real tag
642     } // if
643
644     p= q+1;
645   } // if
646
647   do {
648     SkipWhiteSP( p );
649     if (direction==-1) return true; // end tag reached
650
651     if (*p!='<') break; // a value is coming now, not nested
652
653     // is it a comment ?
654          q=  strstr( p,BeginCom );
655     if  (q && q==p) {
656          q=  strstr( q,  EndCom ); if (!q) return false;
657       p= q + strlen    ( EndCom ); continue; // comment is skipped
658     } // if
659
660          q=  strstr( p,"</" );
661     if  (q && q==p) break; // an empty value
662
663     // is it a CDATA section ?
664          q=  strstr( p,BeginCDATA );
665     if  (q && q==p) {
666          p+= strlen(   BeginCDATA );
667          q=  strstr( p,DoubleCEnd ); if (!q) return false;
668       aPos = p;
669       aSize= (unsigned int)( q-p );
670       vDone= true; // don't do it later again
671
672       p= q + strlen(   DoubleCEnd );
673
674       q=     strstr( p,BeginCDATA ); if (!q) return false; // nested CDATA skipped
675       p= q + strlen(   BeginCDATA );
676       break;
677     } // if
678
679     if (!IsLetter( p[ 1 ] )) break; // no nested tag ?
680
681     level++; direction= 1; // nested tags -> do handling outside
682     return true;
683   } while (true);
684
685   SkipWhiteSP( p );
686   q=   strstr( p, "</" ); if (!q) return false;
687
688   if (!vDone) {
689     aPos = p;
690     aSize= (unsigned int)( q-p );
691     vDone= true; // don't do it later again
692   } // if
693
694   p=  q+2;
695   q=  strstr( p, aTag.c_str() ); if (!q || p!=q) return false;
696   q+= aTag.size();
697   q=  strstr( p,">" ); if (!q) return false;
698
699   p= q+1;
700   return true;
701 } // NextTag
702
703
704 // the structure for attributes with values
705 struct TAField {
706   string name;
707   string value;
708 }; // AField
709
710
711 /*
712 struct VVV {
713   VVV() { printf( "hinein\n" ); }
714  ~VVV() { printf( "hinaus '%s'\n", a.c_str() ); }
715   string a;
716 };
717 */
718
719
720 // parse Data item (includes header and footer)
721 bool TDataObjType::parseData(const char *aText, stringSize aSize, TMultiFieldItem &aItem, TLocalEngineDS *aDatastoreP)
722 {
723   TItemField* fieldP;
724   TTagMapList::iterator pos;
725   string tag, attr, value, docType;
726   const char* vPos;
727   int         vSize;
728
729   // Hierarchical tags
730   typedef std::list<string> TStrList;
731   TStrList tagHierarchy;
732   TStrList::iterator px;
733
734   TAField a;
735   typedef std::list<TAField> TAList;
736   TAList  aList;
737   TAList::iterator ax;
738
739   #ifdef SYDEBUG
740   if (PDEBUGTEST(DBG_PARSE)) {
741     // very detailed, show item being parsed
742     PDEBUGPRINTFX(DBG_PARSE+DBG_HOT, ("Parsing: ") );
743     PDEBUGPUTSX  (DBG_PARSE+DBG_USERDATA, aText);
744   }
745   #endif
746
747   // Go thru all the tags and build temporary stack at <tList>
748   int  level= 0;
749   int  direction;
750   // bool found;
751
752   /*
753   VVV     vEl;
754   typedef std::list<VVV> VList;
755   VList  vv;
756   VList::iterator vx;
757
758   vEl.a= "beat";  vv.push_back( vEl );
759   vEl.a= "ee";    vv.push_back( vEl );
760   vEl.a= "luz";   vv.push_back( vEl );
761   vEl.a= "gugus"; vv.push_back( vEl );
762   vEl.a= "gurk";
763
764   for (vx= vv.begin();
765        vx!=vv.end(); vx++) {
766     printf( "soso '%s'\n", (*vx).a.c_str() );
767   }
768   */
769
770   const char*     p= aText;
771   if     (strstr( p, BeginCDATA )==p) p+= strlen( BeginCDATA );
772   while (NextTag( p, tag,attr, vPos,vSize, level,direction )) {
773     // parse all attributes and put them into <aList>
774     aList.clear();
775     while (!attr.empty()) {
776       int apos=     attr.find( "=\"" );
777       if (apos<0) { attr= ""; break; }
778
779       a.name = attr.substr( 0,apos );
780       attr   = attr.substr( apos+2 );
781
782           apos=     attr.find( "\"" );
783       if (apos<0) { attr= ""; break; }
784
785       a.value= attr.substr( 0,apos );
786       attr   = attr.substr( apos+1 );
787       while   (attr.find(" ")==0) attr= attr.substr( 1 );
788
789       aList.push_back( a );
790     } // while
791
792     switch (direction) {
793       case -1 :     px= tagHierarchy.end();         // are we going up in the hierarchy ?
794                 if (px!=tagHierarchy.begin())       // if the list is not empty ...
795                         tagHierarchy.erase( --px ); // erase last element
796                 break;
797
798       case  1 : tagHierarchy.push_back( tag );      // are we going down in the hierarchy ?
799                 if (level>1) break;
800
801                        docType= tag;
802                 vPos = docType.c_str();
803                 vSize= docType.length();
804                 tag  = "DOCTYPE"; // assign the document type
805                 // go on for this special case
806
807       default:
808         // go thru the tag element list and get the matching element
809         for (pos= fTypeCfgP->fTagMaps.begin();
810              pos!=fTypeCfgP->fTagMaps.end(); pos++) {
811           if        (strucmp( tag.c_str(),TCFG_CSTR((*pos)->fXmlTag) )==0) {
812                   fieldP= aItem.getField( (*pos)->fFid ); // this is the element we're looking for
813             if   (fieldP) {
814               if (fieldP->isAssigned() &&         // -> allow multiple fields with the same name
815                  !fieldP->isArray()) continue;    //    it is assigned already !
816
817               // for embedded structures, the content shouldn't be copied
818               // in all other cases, it can still be used as <value> string
819               if (!(*pos)->fEmbedded) value.assign( vPos,vSize ); // this is the value of this tag
820
821               bool  xAtt= !TCFG_ISEMPTY((*pos)->fXmlAttr);
822               if (aList.size()==0) {
823                 if (xAtt) continue; // does not fit, both must be either empty
824               }
825               else {
826                 if (xAtt) {
827                   value= "";
828
829                   for (ax= aList.begin();
830                        ax!=aList.end(); ax++) {
831                     if (strucmp( (*ax).name.c_str(),TCFG_CSTR((*pos)->fXmlAttr) )==0) {
832                       value=     (*ax).value.c_str(); break; // for attribute handling take as <value>.
833                     } // if
834                   } // for
835
836                   if (value.empty()) continue;
837                 } // if
838               } // if
839
840               do {
841                 // there must be either no parent, or the parent must match
842                 if (         !TCFG_ISEMPTY((*pos)->fParent) &&
843                      strucmp( TCFG_CSTR   ((*pos)->fParent),tagHierarchy.back().c_str() )!=0) break;
844
845                 if (fieldP->isBasedOn( fty_blob )) {
846                   #ifdef STREAMFIELD_SUPPORT
847                     string enc= getAttr( aItem, TCFG_CSTR((*pos)->fXmlTag), "enc" );
848
849                     if (enc.empty()) {
850                       // no encoding
851                     }
852                     else if (strucmp( enc.c_str(),"base64" )==0) {
853                       uInt32 oLen;
854                       uInt8*               bb= b64::decode( value.c_str(), 0, &oLen );
855                       fieldP->setAsString( (const char*)bb, oLen ); // assign the value
856                       b64::free        ( bb );
857                       break; // already done now (blob assigned correctly
858                     }
859                     else if (strucmp( enc.c_str(),"quoted-printable")==0) {
860                       string v;
861                       sysync::appendDecoded( value.c_str(),value.length(), v, enc_quoted_printable );
862                       fieldP->setAsString( v.c_str(), v.size() ); // assign the value
863                       break; // already done now (blob assigned correctly
864                     }
865                     else {
866                       value = "unknown_encoding: \"";
867                       value+=  enc;
868                       value+= "\"";
869                     }
870                   #endif
871                 } // if
872
873                 // we don't have a boolean type directly, so make a special conversion for it
874                 if ((*pos)->fBoolType) {            // assuming the field list item is integer for boolean
875                   if      (strucmp( value.c_str(),"0"     )==0) break;       // as defined in OMA DS 1.2
876                   else if (strucmp( value.c_str(),"false" )==0) value= "-1"; // special boolean treatement
877                   else if (strucmp( value.c_str(),"true"  )==0) value=  "1";
878                 } // if
879
880                 // delegate parsing of embedded object
881                 if ((*pos)->fEmbedded && fProfileHandlerP) {
882                                                       // set related datastore so handler can access session specific datastore state
883                   fProfileHandlerP->setRelatedDatastore(aDatastoreP);
884                   // vPos,vSize is not a copy, but a direct reference into <aText>
885                   bool ok= fProfileHandlerP->parseText(vPos,vSize, aItem);
886                   if (!ok) {
887                     // format error
888                     // aStatusCmd.setStatusCode(415); // Unsupported media type or format
889                     // ADDDEBUGITEM(aStatusCmd,"Error parsing Text content");
890                     return false;
891                   } // if
892
893                   break; // already done (at profile handler)
894                 } // if
895
896                 // it's ok now to fill in <value>
897                 if (fieldP->isArray()) fieldP->appendString( value.c_str() );
898                 else                   fieldP->setAsString ( value.c_str() ); // assign the value
899               } while (false);
900             } // if
901
902             if (aList.size()==0) break;
903           } // if
904         } // for
905     } // switch
906   } // while
907
908   #ifdef SYDEBUG
909     PDEBUGPRINTFX(DBG_PARSE,("Successfully parsed: "));
910 //%%%not again!    PDEBUGPUTSX(DBG_PARSE+DBG_USERDATA+DBG_EXOTIC, aText );
911     aItem.debugShowItem(DBG_DATA+DBG_PARSE);
912   #endif
913
914   return true;
915 } // TDataObjType::parseData
916
917
918 // generates SyncML-Devinf property list for type
919 SmlDevInfCTDataPropListPtr_t TDataObjType::newCTDataPropList(TTypeVariantDescriptor aVariantDescriptor)
920 {
921   // no properties here
922   return NULL;
923 } // TDataObjType::newCTDataPropList
924
925
926 // Filtering: add keywords and property names to filterCap
927 void TDataObjType::addFilterCapPropsAndKeywords(SmlPcdataListPtr_t &aFilterKeywords, SmlPcdataListPtr_t &aFilterProps, TTypeVariantDescriptor aVariantDescriptor)
928 {
929   // add keywords from tagmaps
930   #ifdef OBJECT_FILTERING
931   TTagMapList::iterator pos;
932   for(pos=fTypeCfgP->fTagMaps.begin();pos!=fTypeCfgP->fTagMaps.end();pos++) {
933     // first priority: compare with explicit filterkeyword, if any
934     if (!TCFG_ISEMPTY((*pos)->fFilterKeyword)) {
935       // has a filterkeyword, show it
936       addPCDataStringToList(TCFG_CSTR((*pos)->fFilterKeyword), &aFilterKeywords);
937     }
938   }
939   // let embedded profile add the keywords
940   if (fProfileHandlerP) {
941           fProfileHandlerP->addFilterCapPropsAndKeywords(aFilterKeywords, aFilterProps, aVariantDescriptor, this);
942   }
943   // let base class add own keywords/props
944   inherited::addFilterCapPropsAndKeywords(aFilterKeywords, aFilterProps, aVariantDescriptor);
945   #endif
946 } // TDataObjType::addFilterCapPropsAndKeywords
947
948
949 // intended for creating SyncItemTypes for remote databases from
950 // transmitted DevInf.
951 // SyncItemType MUST NOT take ownership of devinf structure passed
952 // (because multiple types might be created from a single CTCap entry)
953 bool TDataObjType::analyzeCTCap(SmlDevInfCTCapPtr_t aCTCapP)
954 {
955   // just let parent handle
956   return inherited::analyzeCTCap(aCTCapP);
957 } // TDataObjType::analyzeCTCap
958
959
960 /// @brief copy CTCap derived info from another SyncItemType
961 /// @return false if item not compatible
962 /// @note required to create remote type variants from ruleMatch type alternatives
963 bool TDataObjType::copyCTCapInfoFrom(TSyncItemType &aSourceItem)
964 {
965   // just let parent handle
966   return inherited::copyCTCapInfoFrom(aSourceItem);
967 } // TDataObjType::copyCTCapInfoFrom
968
969
970 } // namespace sysync
971
972 /* end of TDataObjType implementation */
973 // eof