Bug Summary

File:libsynthesis/src/sysync/dataobjtype.cpp
Warning:line 705, column 3
Value stored to 'q' is never read

Annotated Source Code

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