libsyncml  0.5.4
sml_xml_assm.c
1 /*
2  * libsyncml - A syncml protocol implementation
3  * Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
4  * Copyright (C) 2007-2009 Michael Bell <michael.bell@opensync.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include <libsyncml/syncml.h>
23 
24 #include <libsyncml/syncml_internals.h>
25 #include <libsyncml/sml_elements_internals.h>
26 #include <libsyncml/sml_command_internals.h>
27 #include <libsyncml/sml_devinf_internals.h>
28 #include <libsyncml/sml_session_internals.h>
29 #include "libsyncml/sml_error_internals.h"
30 
31 #include "sml_xml_assm.h"
32 #include "sml_xml_assm_internals.h"
33 
34 #define BUFFER_SIZE 500
35 
43 
44 static SmlBool _smlXmlAssemblerStartNodeNS(SmlXmlAssembler *assm, const char *name, const char *uri, SmlError **error)
45 {
46  CHECK_ERROR_REF
47  smlAssert(assm);
48  smlAssert(assm->writer);
49  smlAssert(name != NULL);
50  smlAssert(strlen(name) > 0);
51  smlTrace(TRACE_INTERNAL, "%s: Starting \"%s\"", __func__, VA_STRING(name));
52  int rc = xmlTextWriterStartElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri);
53  if (rc < 0) {
54  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to start node");
55  return FALSE;
56  }
57  return TRUE;
58 }
59 
60 static SmlBool _smlXmlAssemblerStartNode(SmlXmlAssembler *assm, const char *name, SmlError **error)
61 {
62  CHECK_ERROR_REF
63  return _smlXmlAssemblerStartNodeNS(assm, name, NULL, error);
64 }
65 
66 static SmlBool _smlXmlAssemblerEndNode(SmlXmlAssembler *assm, SmlError **error)
67 {
68  CHECK_ERROR_REF
69  smlTrace(TRACE_INTERNAL, "%s: Ending node", __func__);
70  int rc = xmlTextWriterEndElement(assm->writer);
71  if (rc < 0) {
72  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end node (%d).", rc);
73  return FALSE;
74  }
75  return TRUE;
76 }
77 
78 static SmlBool _smlXmlAssemblerAddStringNS(SmlXmlAssembler *assm, const char *name, const char *uri, const char *value, SmlError **error)
79 {
80  CHECK_ERROR_REF
81  int rc = xmlTextWriterWriteElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri, (xmlChar *)value);
82  if (rc < 0) {
83  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add string (%d - %s:%s -> %s).", rc, VA_STRING(uri), VA_STRING(name), VA_STRING(value));
84  return FALSE;
85  }
86  return TRUE;
87 }
88 
89 static SmlBool _smlXmlAssemblerAddString(SmlXmlAssembler *assm, const char *name, const char *value, SmlError **error)
90 {
91  CHECK_ERROR_REF
92  int rc = xmlTextWriterWriteElement(assm->writer, (xmlChar *)name, (xmlChar *)value);
93  if (rc < 0) {
94  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add pure string (%d - %s -> %s).", rc, VA_STRING(name), VA_STRING(value));
95  return FALSE;
96  }
97  return TRUE;
98 }
99 
100 static SmlBool _smlXmlAssemblerAddData(SmlXmlAssembler *assm, const char *name, const char *value, unsigned int size, SmlBool raw, SmlError **error)
101 {
102  CHECK_ERROR_REF
103  int rc = 0;
104  if (!_smlXmlAssemblerStartNode(assm, name, error))
105  return FALSE;
106 
107  if (raw)
108  rc = xmlTextWriterWriteRawLen(assm->writer, (xmlChar *)value, size);
109  else
110  rc = xmlTextWriterWriteFormatCDATA(assm->writer, "%*s", size, (xmlChar *)value);
111 
112  if (rc < 0) {
113  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add data (%d).", rc);
114  return FALSE;
115  }
116 
117  if (!_smlXmlAssemblerEndNode(assm, error))
118  return FALSE;
119 
120  return TRUE;
121 }
122 
123 static SmlBool _smlXmlAssemblerAddID(SmlXmlAssembler *assm, const char *name, unsigned int id, SmlError **error)
124 {
125  CHECK_ERROR_REF
126  int rc = xmlTextWriterWriteFormatElement(assm->writer, (xmlChar *)name, "%i", id);
127  if (rc < 0) {
128  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add id");
129  return FALSE;
130  }
131  return TRUE;
132 }
133 
134 static SmlBool _smlXmlAssemblerAddIDNS(SmlXmlAssembler *assm, const char *name, const char *uri, unsigned int id, SmlError **error)
135 {
136  CHECK_ERROR_REF
137  int rc = xmlTextWriterWriteFormatElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri, "%i", id);
138  if (rc < 0) {
139  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add id");
140  return FALSE;
141  }
142  return TRUE;
143 }
144 
145 static SmlBool _smlAssembleUseMaxObjSize(SmlXmlAssembler *assm)
146 {
147  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
148 
149  /* check the configuration */
150  const char *opt = smlAssemblerGetOption(assm->assembler, "USE_LARGEOBJECTS");
151  SmlBool supportsLargeObjects = (opt && !atoi(opt)) ? FALSE : TRUE;
152 
153  /* server should react only the client behavior */
154  if (assm->session->sessionType == SML_SESSION_TYPE_SERVER)
155  {
156  if (smlAssemblerGetRemoteMaxObjSize(assm->assembler) <= 0)
157  supportsLargeObjects = FALSE;
158  if (smlAssemblerGetRemoteMaxMsgSize(assm->assembler) <= 0)
159  supportsLargeObjects = FALSE;
160  }
161 
162  /* SyncML 1.0 does not support large objects */
163  if (assm->session->version == SML_VERSION_10)
164  supportsLargeObjects = FALSE;
165 
166  /* If MaxObjsize must really be sent
167  * then check if it is configured
168  */
169  if (smlSessionGetLocalMaxObjSize(assm->session) <= 0)
170  supportsLargeObjects = FALSE;
171 
172  smlTrace(TRACE_EXIT, "%s - %d", __func__, supportsLargeObjects);
173  return supportsLargeObjects;
174 }
175 
176 static SmlBool _smlAssembleMaxObjSize(SmlXmlAssembler *assm, SmlError **error)
177 {
178  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
179  CHECK_ERROR_REF
180 
181  /* It is allowed to add MaxObjSize to the Alert or Sync
182  * command and libsyncml adds the MaxObjSize to the Sync
183  * command. Explanation from OMA DS Protocol v1.2:
184  *
185  * 6 Protocol Fundamentals
186  * 6.10 Large Object Handling
187  * 6.10.1 Conformance statements
188  * Citation: If a device supports receiving Large
189  * Objects it MUST declare the maximum size of
190  * object (<MaxObjSize> tag) it is capable of
191  * receiving as Meta information within the
192  * Alert or Sync command, as specified in
193  * [META].
194  *
195  * SyncML 1.0 does not support large objects. Therefore
196  * MaxObjSize must not be sent because otherwise this
197  * could result in errors (bad request/message).
198  */
199  if (!_smlXmlAssemblerAddIDNS(assm, SML_ELEMENT_MAXOBJSIZE, SML_NAMESPACE_METINF, smlSessionGetLocalMaxObjSize(assm->session), error))
200  goto error;
201 
202  smlTrace(TRACE_EXIT, "%s", __func__);
203  return TRUE;
204 
205 error:
206  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
207  return FALSE;
208 }
209 
210 SmlBool smlLocationAssemble(SmlLocation *location, SmlXmlAssembler *assm, const char *name, SmlError **error)
211 {
212  smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, location, assm, VA_STRING(name), error);
213  CHECK_ERROR_REF
214  smlAssert(assm);
215  smlAssert(location);
216 
217  if (name) {
218  if (!_smlXmlAssemblerStartNode(assm, name, error))
219  goto error;
220  }
221 
222  if (!location->locURI) {
223  smlErrorSet(error, SML_ERROR_GENERIC, "No locURI set");
224  goto error;
225  }
226 
227  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_LOCURI, location->locURI, error))
228  goto error;
229 
230  if (location->locName) {
231  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_LOCNAME, location->locName, error))
232  goto error;
233  }
234 
235  if (name) {
236  if (!_smlXmlAssemblerEndNode(assm, error))
237  goto error;
238  }
239 
240  smlTrace(TRACE_EXIT, "%s", __func__);
241  return TRUE;
242 
243 error:
244  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
245  return FALSE;
246 }
247 
248 
249 
250 SmlBool smlAnchorAssemble(SmlAnchor *anchor, SmlXmlAssembler *assm, SmlError **error)
251 {
252  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, anchor, assm, error);
253  CHECK_ERROR_REF
254  smlAssert(assm);
255  smlAssert(anchor);
256 
257  /* Start the anchor and switch to syncml:metinf namespace.
258  * It is not possible to remove the namespace from Last and
259  * Next because otherwise the Status commands could be longer
260  * then the Alert commands. If this would happen then the
261  * Status commands alone could be longer than MaxMsgSize.
262  *
263  * It is not sure how a mobile would react in this situation.
264  */
265  if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_ANCHOR, SML_NAMESPACE_METINF, error))
266  goto error;
267 
268  if (!anchor->next) {
269  smlErrorSet(error, SML_ERROR_GENERIC, "No next set");
270  goto error;
271  }
272 
273  /* SyncML Meta-Information DTD 1.1, Page 6, 5.1 Anchor - Content Model: (Last?, Next)
274  Last is optional, don't write a empty Last node.
275  */
276  if (anchor->last) {
277  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_LAST, SML_NAMESPACE_METINF, anchor->last, error))
278  goto error;
279 
280  }
281 
282  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_NEXT, SML_NAMESPACE_METINF, anchor->next, error))
283  goto error;
284 
285  if (!_smlXmlAssemblerEndNode(assm, error))
286  goto error;
287 
288  smlTrace(TRACE_EXIT, "%s", __func__);
289  return TRUE;
290 
291 error:
292  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
293  return FALSE;
294 }
295 
296 SmlBool smlItemAssemble(SmlItem *item, SmlXmlAssembler *assm, SmlError **error)
297 {
298  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, item, assm, error);
299  CHECK_ERROR_REF
300  smlAssert(assm);
301  smlAssert(item);
302 
303  if (assm->moreDataSet) {
304  smlErrorSet(error, SML_ERROR_GENERIC, "Trying to start a new item while last item had more data");
305  goto error;
306  }
307 
308  /* Begin of Item */
309  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_ITEM, error))
310  goto error;
311 
312  /* Target */
313  if (smlItemGetTarget(item)) {
314  if (!smlLocationAssemble(smlItemGetTarget(item), assm, SML_ELEMENT_TARGET, error))
315  goto error;
316  }
317 
318  /* Source */
319  if (smlItemGetSource(item)) {
320  if (!smlLocationAssemble(smlItemGetSource(item), assm, SML_ELEMENT_SOURCE, error))
321  goto error;
322  }
323 
324  /* SourceParent */
325  if (smlItemGetSourceParent(item)) {
326  if (!smlLocationAssemble(smlItemGetSourceParent(item), assm, SML_ELEMENT_SOURCE_PARENT, error))
327  goto error;
328  }
329 
330  /* TargetParent */
331  if (smlItemGetTargetParent(item)) {
332  if (!smlLocationAssemble(smlItemGetTargetParent(item), assm, SML_ELEMENT_TARGET_PARENT, error))
333  goto error;
334  }
335 
336  /* Data */
337  if (smlItemHasData(item)) {
338  if (item->disabled) {
339  if (!_smlXmlAssemblerAddData(assm, SML_ELEMENT_DATA, "", 0, item->raw, error))
340  goto error;
341  } else {
342  char *data = NULL;
343  unsigned int size = 0;
344  if (!smlItemGetData(item, &data, &size, error))
345  goto error;
346 
347  if (!_smlXmlAssemblerAddData(assm, SML_ELEMENT_DATA, data, size, item->raw, error))
348  goto error;
349  }
350  }
351 
352  /* MoreData */
353  if (item->moreData) {
354  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MOREDATA, "", error))
355  goto error;
356 
357  assm->moreDataSet = TRUE;
358  }
359 
360  /* End of Item */
361  if (!_smlXmlAssemblerEndNode(assm, error))
362  goto error;
363 
364  smlTrace(TRACE_EXIT, "%s", __func__);
365  return TRUE;
366 
367 error:
368  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
369  return FALSE;
370 }
371 
372 SmlBool smlCredAssemble(SmlCred *cred, SmlXmlAssembler *assm, SmlError **error)
373 {
374  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, cred, assm, error);
375  CHECK_ERROR_REF
376  smlAssert(assm);
377  smlAssert(cred);
378 
379  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CRED, error))
380  goto error;
381 
382  //Meta
383  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
384  goto error;
385 
386  switch (cred->format) {
387  case SML_FORMAT_TYPE_BASE64:
388  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_FORMAT, SML_NAMESPACE_METINF, SML_BASE64, error))
389  goto error;
390  break;
391  default:
392  smlErrorSet(error, SML_ERROR_GENERIC, "SyncML credential: Unknown format %d.", cred->format);
393  goto error;
394  }
395 
396  switch (cred->type) {
397  case SML_AUTH_TYPE_BASIC:
398  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_BASIC, error))
399  goto error;
400  break;
401  case SML_AUTH_TYPE_MD5:
402  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_MD5, error))
403  goto error;
404  break;
405  default:
406  smlErrorSet(error, SML_ERROR_GENERIC, "Unknown format");
407  goto error;
408  }
409 
410  //META
411  if (!_smlXmlAssemblerEndNode(assm, error))
412  goto error;
413 
414  if (!_smlXmlAssemblerAddData(assm, SML_ELEMENT_DATA, cred->data, strlen(cred->data), TRUE, error))
415  goto error;
416 
417  if (!_smlXmlAssemblerEndNode(assm, error))
418  goto error;
419 
420  smlTrace(TRACE_EXIT, "%s", __func__);
421  return TRUE;
422 
423 error:
424  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
425  return FALSE;
426 }
427 
428 SmlBool smlAccessAssemble(SmlXmlAssembler *assm, SmlCommand *change, SmlError **error)
429 {
430  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, change, error);
431  CHECK_ERROR_REF
432  smlAssert(change);
433  smlAssert(assm);
434 
435  if (!change->private.access.item) {
436  smlErrorSet(error, SML_ERROR_GENERIC, "Missing item");
437  goto error;
438  }
439 
440  if (!change->private.access.item->contenttype) {
441  smlErrorSet(error, SML_ERROR_GENERIC, "Missing contenttype");
442  goto error;
443  }
444 
445  //Meta
446  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
447  goto error;
448 
449  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, change->private.access.item->contenttype, error))
450  goto error;
451 
452  //META
453  if (!_smlXmlAssemblerEndNode(assm, error))
454  goto error;
455 
456  if (!smlItemAssemble(change->private.access.item, assm, error))
457  goto error;
458 
459  smlTrace(TRACE_EXIT, "%s", __func__);
460  return TRUE;
461 
462 error:
463  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
464  return FALSE;
465 }
466 
467 SmlBool smlChangeAssemble(SmlXmlAssembler *assm, SmlCommand *change, SmlError **error)
468 {
469  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, change, error);
470  CHECK_ERROR_REF
471  smlAssert(change);
472  smlAssert(assm);
473 
474  if (!change->private.change.items ||
475  !g_list_length(change->private.change.items)) {
476  smlErrorSet(error, SML_ERROR_GENERIC, "Missing items");
477  goto error;
478  }
479 
480  /* libsyncml only supports one item per change command */
481  smlAssert(g_list_length(change->private.change.items) == 1);
482  SmlItem *item = g_list_nth_data(change->private.change.items, 0);
483 
484  if (!item) {
485  smlErrorSet(error, SML_ERROR_GENERIC, "One item of the item list is NULL.");
486  goto error;
487  }
488  if (!item->contenttype) {
489  smlErrorSet(error, SML_ERROR_GENERIC, "Missing contenttype");
490  goto error;
491  }
492 
493  //Meta
494  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
495  goto error;
496 
497  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, item->contenttype, error))
498  goto error;
499 
500  /* We will add the max obj size node, if USE_LARGEOBJECTS is true or not set at all.
501  * And the remote side must have set a maxobjsize if we are a server */
502  const char *opt = smlAssemblerGetOption(assm->assembler, "USE_LARGEOBJECTS");
503  SmlBool supportsLargeObjects = (opt && !atoi(opt)) ? FALSE : TRUE;
504 
505  smlTrace(TRACE_INTERNAL, "%s: Large object: use %i, server %i, requestedSize %i", __func__, supportsLargeObjects, assm->session->sessionType == SML_SESSION_TYPE_SERVER ? 1 : 0, smlAssemblerGetRemoteMaxObjSize(assm->assembler));
506 
507  if (supportsLargeObjects && change->size) {
508  if (!_smlXmlAssemblerAddIDNS(assm, SML_ELEMENT_SIZE, SML_NAMESPACE_METINF, change->size, error))
509  goto error;
510  }
511 
512  //META
513  if (!_smlXmlAssemblerEndNode(assm, error))
514  goto error;
515 
516  //Item(s)
517 
518  /* Item has to deal with four different situations:
519  * 1. If this is a new item but the remote peer only supports
520  * the Replace command and the local peer is an OMA DS
521  * server then the ID/location must be send as target.
522  * If the local peer is an OMA DS client then nothing
523  * changes. FYI an OMA DS server must support the Add
524  * command.
525  * => Target must be set (Target is the former Source)
526  * 2. If this is a new item then the local peer only knows
527  * the ID of the item.
528  * => Source must be set
529  * 3. If this is a changed or deleted item and the local peer
530  * is an OMA DS server then the item must reference the ID
531  * of the remote peer because of the submitted map. Please
532  * not that the remote peer as an OMA DS client must only
533  * know its own ID of the item. The mapping of locations
534  * (IDs) is the exclusive job of the OMA DS server.
535  * => Target must be set (Source is optional)
536  * 4. If this is a changed or deleted item and the local peer
537  * is an OMA DS client then the item must reference the ID
538  * of the local peer only because the mapping of locations
539  * (IDs) must be done by the OMA DS server.
540  * => Source must be set
541  */
542  SmlSessionType sessionType = assm->session->sessionType;
543  opt = smlAssemblerGetOption(assm->assembler, "ONLY_REPLACE");
544  if (change->type == SML_COMMAND_TYPE_ADD)
545  {
546  if (opt && atoi(opt) && /* remote peer supports ONLY_REPLACE */
547  sessionType == SML_SESSION_TYPE_SERVER) {
548  /* only Target must be set */
549  if (item->target)
550  smlLocationUnref(item->target);
551  item->target = item->source;
552  item->source = NULL;
553  } else {
554  /* only Source must be set */
555  if (item->target)
556  smlLocationUnref(item->target);
557  item->target = NULL;
558  }
559  } else {
560  if (sessionType == SML_SESSION_TYPE_SERVER) {
561  /* Target must be set */
562  if (item->source)
563  smlLocationUnref(item->source);
564  item->source = NULL;
565  } else {
566  /* only Source must be set */
567  if (item->target)
568  smlLocationUnref(item->target);
569  item->target = NULL;
570  }
571  }
572 
573  if (!smlItemAssemble(item, assm, error))
574  goto error;
575 
576  smlTrace(TRACE_EXIT, "%s", __func__);
577  return TRUE;
578 
579 error:
580  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
581  return FALSE;
582 }
583 
584 SmlBool smlSyncAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
585 {
586  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
587  CHECK_ERROR_REF
588  smlAssert(cmd);
589  smlAssert(assm);
590 
591  if (!cmd->target) {
592  smlErrorSet(error, SML_ERROR_GENERIC, "No target set");
593  goto error;
594  }
595 
596  /* The specification OMA DS Protocol v1.2 allows to add
597  * MaxObjsize to Alert or Sync commands but all examples show
598  * it only in Alert commands. Therefore libsyncml adds it to
599  * Alert commands only.
600  */
601 
602  if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET, error))
603  goto error;
604 
605  if (!cmd->source) {
606  smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
607  goto error;
608  }
609 
610  if (!smlLocationAssemble(cmd->source, assm, SML_ELEMENT_SOURCE, error))
611  goto error;
612 
613  if (_smlAssembleUseMaxObjSize(assm))
614  {
615  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
616  goto error;
617 
618  if (!_smlAssembleMaxObjSize(assm, error))
619  goto error;
620 
621  if (!_smlXmlAssemblerEndNode(assm, error))
622  goto error;
623  }
624 
625  const char *opt = smlAssemblerGetOption(assm->assembler, "USE_NUMBEROFCHANGES");
626  SmlBool supportsNumberOfChanges = (opt && !atoi(opt)) ? FALSE : TRUE;
627  if (supportsNumberOfChanges && assm->session->version != SML_VERSION_10) {
628  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_NUMBEROFCHANGES, cmd->private.sync.numChanged, error))
629  goto error;
630  }
631 
632  smlTrace(TRACE_EXIT, "%s", __func__);
633  return TRUE;
634 
635 error:
636  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
637  return FALSE;
638 }
639 
640 SmlBool smlMapItemAssemble(SmlXmlAssembler *assm, SmlMapItem *item, SmlError **error)
641 {
642  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, item, error);
643  CHECK_ERROR_REF
644  smlAssert(assm);
645  smlAssert(item);
646 
647  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_MAPITEM, error))
648  goto error;
649 
650  if (item->source) {
651  if (!smlLocationAssemble(item->source, assm, SML_ELEMENT_SOURCE, error))
652  goto error;
653  }
654 
655  if (item->target) {
656  if (!smlLocationAssemble(item->target, assm, SML_ELEMENT_TARGET, error))
657  goto error;
658  }
659 
660  //MapItem
661  if (!_smlXmlAssemblerEndNode(assm, error))
662  goto error;
663 
664  smlTrace(TRACE_EXIT, "%s", __func__);
665  return TRUE;
666 
667 error:
668  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
669  return FALSE;
670 }
671 
672 SmlBool smlMapAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
673 {
674  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
675  CHECK_ERROR_REF
676  smlAssert(cmd);
677  smlAssert(assm);
678 
679  if (!cmd->target) {
680  smlErrorSet(error, SML_ERROR_GENERIC, "No target set");
681  goto error;
682  }
683 
684  if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET, error))
685  goto error;
686 
687  if (!cmd->source) {
688  smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
689  goto error;
690  }
691 
692  if (!smlLocationAssemble(cmd->source, assm, SML_ELEMENT_SOURCE, error))
693  goto error;
694 
695  GList *m = NULL;
696  for (m = cmd->private.map.items; m; m = m->next) {
697  SmlMapItem *item = m->data;
698  if (!smlMapItemAssemble(assm, item, error))
699  goto error;
700  }
701 
702  smlTrace(TRACE_EXIT, "%s", __func__);
703  return TRUE;
704 
705 error:
706  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
707  return FALSE;
708 }
709 
710 SmlBool smlAlertAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
711 {
712  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
713  CHECK_ERROR_REF
714  smlAssert(cmd);
715  smlAssert(assm);
716 
717  /* Results itself and CmdID must be set by the caller.
718  * This function only add the Results specific content.
719  */
720 
721  /* No Resp */
722 
723  /* Cred */
724 
725  /* Data */
726  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_DATA, cmd->private.alert.type, error))
727  goto error;
728 
729  /* Correlator */
730 
731  /* Begin of Item */
732  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_ITEM, error))
733  goto error;
734 
735  /* Target */
736  if (cmd->target) {
737  if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET, error))
738  goto error;
739  }
740 
741  /* Source */
742  if (cmd->source) {
743  if (!smlLocationAssemble(cmd->source, assm, SML_ELEMENT_SOURCE, error))
744  goto error;
745  } else {
746  // NEXT MESSAGE alerts does not need a source/target
747  if (cmd->private.alert.type != SML_ALERT_NEXT_MESSAGE) {
748  smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
749  goto error;
750  }
751  }
752 
753  /* SourceParent */
754 
755  /* TargetParent */
756 
757  /* Begin of META */
758 
759  SmlBool meta = FALSE;
760  if (cmd->private.alert.contentType)
761  meta = TRUE;
762  if (cmd->private.alert.anchor)
763  meta = TRUE;
764  if (_smlAssembleUseMaxObjSize(assm))
765  meta = TRUE;
766 
767  if (meta && !_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
768  goto error;
769 
770  if (cmd->private.alert.contentType &&
771  !_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF,
772  cmd->private.alert.contentType, error))
773  goto error;
774 
775  if (cmd->private.alert.anchor &&
776  !smlAnchorAssemble(cmd->private.alert.anchor, assm, error))
777  goto error;
778 
779  if (_smlAssembleUseMaxObjSize(assm) &&
780  !_smlAssembleMaxObjSize(assm, error))
781  goto error;
782 
783  if (meta && !_smlXmlAssemblerEndNode(assm, error))
784  goto error;
785 
786  /* End of META */
787 
788  /* End of Item */
789  if (!_smlXmlAssemblerEndNode(assm, error))
790  goto error;
791 
792  smlTrace(TRACE_EXIT, "%s", __func__);
793  return TRUE;
794 
795 error:
796  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
797  return FALSE;
798 }
799 
800 SmlBool smlResultsAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
801 {
802  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
803  CHECK_ERROR_REF
804  smlAssert(cmd);
805  smlAssert(assm);
806 
807  /* Results itself and CmdID must be set by the caller.
808  * This function only add the Results specific content.
809  */
810 
811  /* MsgRef */
812  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MSGREF, cmd->private.results.status->msgRef, error))
813  goto error;
814 
815  /* CmdRef */
816  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDREF, cmd->private.results.status->cmdRef, error))
817  goto error;
818 
819  /* Meta */
820  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
821  goto error;
822  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, cmd->private.results.status->item->contenttype, error))
823  goto error;
824  if (!_smlXmlAssemblerEndNode(assm, error))
825  goto error;
826 
827  /* TargetRef */
828  if (cmd->private.results.status->targetRef) {
829  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_TARGETREF, cmd->private.results.status->targetRef->locURI, error))
830  goto error;
831  }
832 
833  /* SourceRef */
834  if (cmd->private.results.status->sourceRef) {
835  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, cmd->private.results.status->sourceRef->locURI, error))
836  goto error;
837  }
838 
839  /* Item */
840  if (!smlItemAssemble(cmd->private.results.status->item, assm, error))
841  goto error;
842 
843  smlTrace(TRACE_EXIT, "%s", __func__);
844  return TRUE;
845 
846 error:
847  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
848  return FALSE;
849 }
850 
851 SmlBool smlXmlAssemblerAddHeader(SmlXmlAssembler *assm, SmlSession *session, SmlError **error)
852 {
853  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, session, error);
854  CHECK_ERROR_REF
855  smlAssert(assm);
856  smlAssert(g_mutex_trylock(assm->mutex));
857  smlAssert(session);
858 
859  /* Lets see if there already was a header before */
860  if (assm->header_buffer) {
861  xmlBufferFree(assm->header_buffer);
862  assm->header_buffer = NULL;
863  }
864 
865  /* We first start a new writer that will write our header into a buffer */
866  assm->header_buffer = xmlBufferCreateSize(BUFFER_SIZE);
867  if (!assm->header_buffer) {
868  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
869  goto error;
870  }
871 
872  assm->writer = xmlNewTextWriterMemory(assm->header_buffer, 0);
873  if (!assm->writer) {
874  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
875  goto error;
876  }
877 
878  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCHDR, error))
879  goto error;
880 
881  if (!session->protocol) {
882  smlErrorSet(error, SML_ERROR_GENERIC, "No version set");
883  goto error;
884  }
885 
886  if (!session->version) {
887  smlErrorSet(error, SML_ERROR_GENERIC, "No dtd set");
888  goto error;
889  }
890 
891  switch (session->protocol) {
892  case SML_PROTOCOL_SYNCML:
893  switch (session->version) {
894  case SML_VERSION_10:
895  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.0", error))
896  goto error;
897 
898  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERPROTO, SML_VERSION_STRING_10, error))
899  goto error;
900  break;
901  case SML_VERSION_11:
902  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.1", error))
903  goto error;
904 
905  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERPROTO, SML_VERSION_STRING_11, error))
906  goto error;
907  break;
908  case SML_VERSION_12:
909  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.2", error))
910  goto error;
911 
912  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERPROTO, SML_VERSION_STRING_12, error))
913  goto error;
914  break;
915  default:
916  smlErrorSet(error, SML_ERROR_GENERIC, "Unknown version");
917  goto error;
918  }
919  break;
920  default:
921  smlErrorSet(error, SML_ERROR_GENERIC, "Unknown protocol");
922  goto error;
923  }
924 
925  if (session->sessionID) {
926  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SESSIONID, session->sessionID, error))
927  goto error;
928  }
929 
930  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MSGID, session->lastMessageID, error))
931  goto error;
932 
933  if (!smlLocationAssemble(session->target, assm, SML_ELEMENT_TARGET, error))
934  goto error;
935 
936  if (!smlLocationAssemble(session->source, assm, SML_ELEMENT_SOURCE, error))
937  goto error;
938 
939  if (session->responseURI &&
940  !_smlXmlAssemblerAddString(assm, SML_ELEMENT_RESPURI, session->responseURI, error))
941  goto error;
942 
943  /* The credentials must be present and calculated for the correct
944  * authentication schema (e.g. syncml:auth-basic or syncml:auth-md5).
945  */
946  if (session->cred != NULL && session->cred->data != NULL) {
947  if (!smlCredAssemble(session->cred, assm, error))
948  goto error;
949  }
950 
951  /* What do we have to check here?
952  *
953  * SyncML 1.0 does not require any checks because large object
954  * handling was first specified for SyncML 1.1.
955  *
956  * SyncML 1.1 and 1.2 require some checks.
957  *
958  * 1. OMA DS server MUST support large objects.
959  * This means that a local MaxMsgSize and a local MaxObjSize
960  * MUST be specified.
961  * 2. If an OMA DS client suppors large objects then it MUST
962  * match the same requirements like the OMA DS server.
963  */
964  if (session->protocol == SML_PROTOCOL_SYNCML &&
965  session->version > SML_VERSION_10)
966  {
967  /* This is a SyncML protocol version which supports
968  * large objects.
969  */
970  if (session->sessionType == SML_SESSION_TYPE_SERVER ||
971  smlSessionGetLocalMaxObjSize(session) > 0)
972  {
973  /* Large object support is enabled. */
974  if (smlSessionGetLocalMaxMsgSize(session) < 1)
975  {
976  smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
977  "MaxMsgSize must be set.");
978  goto error;
979  }
980  if (smlSessionGetLocalMaxObjSize(session) < 1)
981  {
982  smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
983  "MaxObjSize must be set.");
984  goto error;
985  }
986  }
987  }
988 
989  /* set MaxMsgSize if available */
990  unsigned int localMaxMsgSize = smlSessionGetLocalMaxMsgSize(session);
991  if (localMaxMsgSize) {
992  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
993  goto error;
994 
995  /* Do not add MaxObjSize here.
996  * Please see OMA DS Protocol v1.2 section 6.10.1
997  */
998  if (!_smlXmlAssemblerAddIDNS(assm, SML_ELEMENT_MAXMSGSIZE, SML_NAMESPACE_METINF, localMaxMsgSize, error))
999  goto error;
1000 
1001  //META
1002  if (!_smlXmlAssemblerEndNode(assm, error))
1003  goto error;
1004  }
1005 
1006  if (!_smlXmlAssemblerEndNode(assm, error))
1007  goto error;
1008 
1009  /* Now close the buffer and get the content */
1010  if (xmlTextWriterEndDocument(assm->writer) < 0) {
1011  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
1012  goto error;
1013  }
1014 
1015  xmlFreeTextWriter(assm->writer);
1016  assm->writer = NULL;
1017 
1018  g_mutex_unlock(assm->mutex);
1019  smlTrace(TRACE_EXIT, "%s", __func__);
1020  return TRUE;
1021 
1022 error:
1023  if (assm->writer) {
1024  xmlFreeTextWriter(assm->writer);
1025  assm->writer = NULL;
1026  }
1027  if (assm->header_buffer) {
1028  xmlBufferFree(assm->header_buffer);
1029  assm->header_buffer = NULL;
1030  }
1031  g_mutex_unlock(assm->mutex);
1032  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1033  return FALSE;
1034 }
1035 
1036 SmlBool smlXmlAssemblerStartCommand(SmlXmlAssembler *assm, unsigned int parentID, SmlCommand *cmd, SmlError **error)
1037 {
1038  CHECK_ERROR_REF
1039  smlAssert(assm);
1040  smlAssert(cmd);
1041 
1042  if (cmd->type == SML_COMMAND_TYPE_UNKNOWN) {
1043  smlErrorSet(error, SML_ERROR_GENERIC, "No cmd set");
1044  goto error;
1045  }
1046 
1047  if (!cmd->cmdID) {
1048  smlErrorSet(error, SML_ERROR_GENERIC, "No cmd ID set");
1049  goto error;
1050  }
1051 
1052  /* Lets see if there already was a header before */
1053  if (!assm->header_buffer) {
1054  smlErrorSet(error, SML_ERROR_GENERIC, "Header not yet added");
1055  goto error;
1056  }
1057 
1058  SmlXmlAssemblerCommand *assmcmd = smlTryMalloc0(sizeof(SmlXmlAssemblerCommand), error);
1059  if (!assmcmd)
1060  goto error;
1061  assmcmd->nodeType = SML_ASSEMBLER_NODE_OPEN;
1062  assmcmd->cmdID = cmd->cmdID;
1063  assmcmd->cmdType = cmd->type;
1064 
1065  GList **appendto = &(assm->commands);
1066  if (parentID) {
1067  /* If we have a parent we first have to search for this command */
1068  SmlXmlAssemblerCommand *excmd = NULL;
1069  GList *b = NULL;
1070  for (b = assm->commands; b; b = b->next) {
1071  excmd = b->data;
1072  if (excmd->cmdID == parentID)
1073  appendto = &(excmd->children);
1074  }
1075  }
1076 
1077  /* We first start a new parent writer that will write our command into a buffer */
1078  assmcmd->buffer = xmlBufferCreateSize(BUFFER_SIZE);
1079  if (!assmcmd->buffer) {
1080  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
1081  goto error_free_cmd;
1082  }
1083 
1084  assm->writer = xmlNewTextWriterMemory(assmcmd->buffer, 0);
1085  if (!assm->writer) {
1086  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
1087  goto error_free_buffer;
1088  }
1089 
1090  /* We start without the root node */
1091  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDID, cmd->cmdID, error))
1092  goto error_free_writer;
1093 
1094  switch (cmd->type) {
1095  case SML_COMMAND_TYPE_ALERT:
1096  if (!smlAlertAssemble(assm, cmd, error))
1097  goto error_free_writer;
1098  break;
1099  case SML_COMMAND_TYPE_SYNC:
1100  if (!smlSyncAssemble(assm, cmd, error))
1101  goto error_free_writer;
1102  break;
1103  case SML_COMMAND_TYPE_ADD:
1104  case SML_COMMAND_TYPE_REPLACE:
1105  case SML_COMMAND_TYPE_DELETE:
1106  if (!smlChangeAssemble(assm, cmd, error))
1107  goto error_free_writer;
1108  break;
1109  case SML_COMMAND_TYPE_PUT:
1110  case SML_COMMAND_TYPE_GET:
1111  if (!smlAccessAssemble(assm, cmd, error))
1112  goto error_free_writer;
1113  break;
1114  case SML_COMMAND_TYPE_MAP:
1115  if (!smlMapAssemble(assm, cmd, error))
1116  goto error_free_writer;
1117  break;
1118  case SML_COMMAND_TYPE_RESULTS:
1119  if (!smlResultsAssemble(assm, cmd, error))
1120  goto error_free_writer;
1121  break;
1122  default:
1123  smlErrorSet(error, SML_ERROR_GENERIC, "Unknown command type");
1124  goto error_free_writer;
1125  }
1126 
1127  /* Now close the buffer */
1128  if (xmlTextWriterEndDocument(assm->writer) < 0) {
1129  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
1130  goto error_free_writer;
1131  }
1132 
1133  xmlFreeTextWriter(assm->writer);
1134  assm->writer = NULL;
1135 
1136  /* Now we can append the buffer and the parent*/
1137  *appendto = g_list_append(*appendto, assmcmd);
1138 
1139  return TRUE;
1140 
1141 error_free_writer:
1142  xmlFreeTextWriter(assm->writer);
1143  assm->writer = NULL;
1144 error_free_buffer:
1145  xmlBufferFree(assmcmd->buffer);
1146 error_free_cmd:
1147  smlSafeFree((gpointer *)&assmcmd);
1148 error:
1149  return FALSE;
1150 }
1151 
1152 SmlBool smlXmlAssemblerEndCommand(SmlXmlAssembler *assm, unsigned int parentID, SmlError **error)
1153 {
1154  CHECK_ERROR_REF
1155  smlAssert(assm);
1156 
1157  /* Lets see if there already was a header before */
1158  if (!assm->header_buffer) {
1159  smlErrorSet(error, SML_ERROR_GENERIC, "Header not yet added");
1160  goto error;
1161  }
1162 
1163  SmlXmlAssemblerCommand *assmcmd = smlTryMalloc0(sizeof(SmlXmlAssemblerCommand), error);
1164  if (!assmcmd)
1165  goto error;
1166  assmcmd->nodeType = SML_ASSEMBLER_NODE_CLOSE;
1167 
1168  GList **appendto = &(assm->commands);
1169  if (parentID) {
1170  /* If we have a parent we first have to search for this command */
1171  SmlXmlAssemblerCommand *excmd = NULL;
1172  GList *b = NULL;
1173  for (b = assm->commands; b; b = b->next) {
1174  excmd = b->data;
1175  if (excmd->cmdID == parentID)
1176  appendto = &(excmd->children);
1177  }
1178  }
1179 
1180  /* Now we can append command */
1181  *appendto = g_list_append(*appendto, assmcmd);
1182 
1183  return TRUE;
1184 
1185 error:
1186  return FALSE;
1187 }
1188 
1189 SmlBool smlXmlAssemblerRemCommand(SmlXmlAssembler *assm, unsigned int parentID, SmlError **error)
1190 {
1191  smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, assm, parentID, error);
1192  CHECK_ERROR_REF
1193  smlAssert(assm);
1194 
1195  GList **removefrom = &(assm->commands);
1196  if (parentID) {
1197  /* If we have a parent we first have to search for this command */
1198  SmlXmlAssemblerCommand *excmd = NULL;
1199  GList *b = NULL;
1200  for (b = assm->commands; b; b = b->next) {
1201  excmd = b->data;
1202  if (excmd->cmdID == parentID)
1203  removefrom = &(excmd->children);
1204  }
1205  }
1206 
1207  if (!*removefrom) {
1208  smlErrorSet(error, SML_ERROR_GENERIC, "Nothing to remove");
1209  goto error;
1210  }
1211 
1212  GList *b = g_list_last(*removefrom);
1213  SmlXmlAssemblerCommand *cmd = b->data;
1214  *removefrom = g_list_delete_link(*removefrom, b);
1215  if (cmd->nodeType != SML_ASSEMBLER_NODE_OPEN) {
1216  smlErrorSet(error, SML_ERROR_GENERIC, "Trying to remove not a starting command");
1217  goto error;
1218  }
1219 
1220  assm->moreDataSet = FALSE;
1221 
1222  xmlBufferFree(cmd->buffer);
1223  smlSafeFree((gpointer *)&cmd);
1224 
1225  smlTrace(TRACE_EXIT, "%s", __func__);
1226  return TRUE;
1227 
1228 error:
1229  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1230  return FALSE;
1231 }
1232 
1233 SmlBool smlXmlAssemblerRemStatus(SmlXmlAssembler *assm, SmlError **error)
1234 {
1235  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, assm, error);
1236  CHECK_ERROR_REF
1237  smlAssert(assm);
1238 
1239  if (!assm->statuses) {
1240  smlErrorSet(error, SML_ERROR_GENERIC, "Trying to remove status but no status available");
1241  goto error;
1242  }
1243 
1244  GList *b;
1245  SmlXmlAssemblerStatus *last = NULL;
1246  for (b = assm->statuses; b; b = b->next) {
1247  SmlXmlAssemblerStatus *status = b->data;
1248 
1249  if (!status->buffer)
1250  break;
1251  last = status;
1252  }
1253 
1254  if (last) {
1255  xmlBufferFree(last->buffer);
1256  last->buffer = NULL;
1257  }
1258 
1259  assm->added_statuses--;
1260 
1261  smlTrace(TRACE_EXIT, "%s", __func__);
1262  return TRUE;
1263 
1264 error:
1265  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1266  return FALSE;
1267 }
1268 
1269 SmlBool smlXmlAssemblerReserveStatus(SmlXmlAssembler *assm, unsigned int cmdRef, unsigned int msgRef, unsigned int cmdID, SmlError **error)
1270 {
1271  smlTrace(TRACE_ENTRY, "%s(%p, %i, %i, %i, %p)", __func__, assm, cmdRef, msgRef, cmdID, error);
1272  CHECK_ERROR_REF
1273  smlAssert(assm);
1274 
1275  /* We first start a new writer that will write our status into a buffer */
1277  if (!res)
1278  goto error;
1279  res->cmdRef = cmdRef;
1280  res->cmdID = cmdID;
1281  res->msgRef = msgRef;
1282 
1283  /* Now we can append the buffer */
1284  if (cmdRef != 0)
1285  assm->statuses = g_list_append(assm->statuses, res);
1286  else
1287  assm->statuses = g_list_prepend(assm->statuses, res);
1288  assm->reserved_statuses++;
1289 
1290  smlTrace(TRACE_EXIT, "%s", __func__);
1291  return TRUE;
1292 
1293 error:
1294  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1295  return FALSE;
1296 }
1297 
1298 SmlBool smlXmlAssemblerAddStatus(SmlXmlAssembler *assm, SmlStatus *status, SmlError **error)
1299 {
1300  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, status, error);
1301  CHECK_ERROR_REF
1302  smlAssert(assm);
1303  smlAssert(g_mutex_trylock(assm->mutex));
1304  smlAssert(status);
1305  SmlXmlAssemblerStatus *res = NULL;
1306 
1307  smlTrace(TRACE_INTERNAL, "Adding status with cmdRef %i, msgRef %i, cmd %s", status->cmdRef, status->msgRef, smlCommandTypeToString(status->type, error));
1308  /* this happens if the status is damaged */
1309  if (*error)
1310  goto error;
1311 
1312  if (!status->msgRef) {
1313  smlErrorSet(error, SML_ERROR_GENERIC, "No msgref set");
1314  goto error;
1315  }
1316 
1317  if (status->type == SML_COMMAND_TYPE_UNKNOWN) {
1318  smlErrorSet(error, SML_ERROR_GENERIC, "No cmd set");
1319  goto error;
1320  }
1321 
1322  /* Lets see if there already was a header before */
1323  if (!assm->header_buffer) {
1324  smlErrorSet(error, SML_ERROR_GENERIC, "Header not yet added");
1325  goto error;
1326  }
1327 
1328  /* Get the reserved buffer */
1329  GList *s = NULL;
1330  for (s = assm->statuses; s; s = s->next) {
1331  res = s->data;
1332  if (res->cmdRef == status->cmdRef && res->msgRef == status->msgRef)
1333  break;
1334  res = NULL;
1335  }
1336 
1337  if (!res) {
1338  smlErrorSet(error, SML_ERROR_GENERIC, "Status not reserved");
1339  goto error;
1340  }
1341 
1342  if (!res->cmdID) {
1343  smlErrorSet(error, SML_ERROR_GENERIC, "No cmd ID set");
1344  goto error;
1345  }
1346 
1347  if (res->buffer) {
1348  smlErrorSet(error, SML_ERROR_GENERIC, "Status already added");
1349  goto error;
1350  }
1351 
1352  /* We first start a new writer that will write our status into a buffer */
1353  res->buffer = xmlBufferCreateSize(BUFFER_SIZE);
1354  if (!res->buffer) {
1355  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
1356  goto error;
1357  }
1358 
1359  assm->writer = xmlNewTextWriterMemory(res->buffer, 0);
1360  if (!assm->writer) {
1361  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
1362  goto error_free_buffer;
1363  }
1364 
1365  /* Begin of Status */
1366  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_STATUS, error))
1367  goto error;
1368 
1369  /* CmdID */
1370  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDID, res->cmdID, error))
1371  goto error;
1372 
1373  /* MsgRef */
1374  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MSGREF, status->msgRef, error))
1375  goto error;
1376 
1377  /* CmdRef */
1378  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDREF, res->cmdRef, error))
1379  goto error;
1380 
1381  /* Cmd */
1382  const char *cmdname = smlCommandTypeToString(status->type, error);
1383  if (!cmdname)
1384  goto error;
1385  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_CMD, cmdname, error))
1386  goto error;
1387 
1388  /* TargetRef */
1389  if (status->targetRef) {
1390  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_TARGETREF, status->targetRef->locURI, error))
1391  goto error;
1392  }
1393 
1394  /* SourceRef */
1395  if (status->sourceRef) {
1396  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, status->sourceRef->locURI, error))
1397  goto error;
1398  }
1399 
1400  /* Cred */
1401 
1402  /* Chal */
1403  if (status->type == SML_COMMAND_TYPE_HEADER && status->chal)
1404  {
1405  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CHAL, error))
1406  goto error;
1407 
1408  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
1409  goto error;
1410 
1411  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_FORMAT, SML_NAMESPACE_METINF, SML_BASE64, error))
1412  goto error;
1413 
1414  switch (status->chal->type) {
1415  case SML_AUTH_TYPE_BASIC:
1416  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_BASIC, error))
1417  goto error;
1418  break;
1419  case SML_AUTH_TYPE_MD5:
1420  if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_MD5, error))
1421  goto error;
1422  if (status->chal->nonce_b64 &&
1423  !_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_NEXTNONCE, SML_NAMESPACE_METINF, status->chal->nonce_b64, error))
1424  goto error;
1425  break;
1426  default:
1427  smlErrorSet(error, SML_ERROR_GENERIC, "Unknown auth type");
1428  goto error;
1429  }
1430 
1431  //META
1432  if (!_smlXmlAssemblerEndNode(assm, error))
1433  goto error;
1434 
1435  //CHAL
1436  if (!_smlXmlAssemblerEndNode(assm, error))
1437  goto error;
1438  }
1439 
1440  /* Data */
1441  if (status->data) {
1442  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DATA, status->data, error))
1443  goto error;
1444  }
1445 
1446  /* Item */
1447  if (status->type == SML_COMMAND_TYPE_ALERT && status->anchor)
1448  {
1449  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_ITEM, error))
1450  goto error;
1451 
1452  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DATA, error))
1453  goto error;
1454 
1455  if (!smlAnchorAssemble(status->anchor, assm, error))
1456  goto error;
1457 
1458  //DATA
1459  if (!_smlXmlAssemblerEndNode(assm, error))
1460  goto error;
1461 
1462  //ITEM
1463  if (!_smlXmlAssemblerEndNode(assm, error))
1464  goto error;
1465  }
1466 
1467  /* End of Status */
1468  if (!_smlXmlAssemblerEndNode(assm, error))
1469  goto error;
1470 
1471  /* Now close the buffer and get the content */
1472  if (xmlTextWriterEndDocument(assm->writer) < 0) {
1473  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
1474  goto error_free_writer;
1475  }
1476 
1477  xmlFreeTextWriter(assm->writer);
1478  assm->writer = NULL;
1479 
1480  assm->added_statuses++;
1481 
1482  g_mutex_unlock(assm->mutex);
1483  smlTrace(TRACE_EXIT, "%s", __func__);
1484  return TRUE;
1485 
1486 error_free_writer:
1487  xmlFreeTextWriter(assm->writer);
1488  assm->writer = NULL;
1489 error_free_buffer:
1490  xmlBufferFree(res->buffer);
1491  res->buffer = NULL;
1492 error:
1493  g_mutex_unlock(assm->mutex);
1494  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1495  return FALSE;
1496 }
1497 
1498 SmlBool smlXmlAssemblerMissingStatus(SmlXmlAssembler *assm)
1499 {
1500  smlAssert(assm);
1501 
1502  if (assm->reserved_statuses - assm->added_statuses > 0)
1503  return TRUE;
1504 
1505  return FALSE;
1506 }
1507 
1508 static void flush_list(GList *list)
1509 {
1510  GList *l = NULL;
1511  for (l = list; l; l = l->next) {
1512  SmlXmlAssemblerCommand *cmd = l->data;
1513 
1514  if (cmd->nodeType != SML_ASSEMBLER_NODE_CLOSE && cmd->children)
1515  flush_list(cmd->children);
1516 
1517  xmlBufferFree(cmd->buffer);
1518  smlSafeFree((gpointer *)&cmd);
1519  }
1520 
1521  g_list_free(list);
1522 }
1523 
1524 void smlXmlAssemblerFree(SmlXmlAssembler *assm)
1525 {
1526  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
1527  smlAssert(assm);
1528 
1529  if (assm->header_buffer) {
1530  xmlBufferFree(assm->header_buffer);
1531  assm->header_buffer = NULL;
1532  }
1533 
1534  while (assm->statuses) {
1535  SmlXmlAssemblerStatus *status = assm->statuses->data;
1536 
1537  if (status->buffer)
1538  xmlBufferFree(status->buffer);
1539 
1540  smlSafeFree((gpointer *)&status);
1541  assm->statuses = g_list_delete_link(assm->statuses, assm->statuses);
1542  }
1543 
1544  flush_list(assm->commands);
1545  flush_list(assm->last_commands);
1546 
1547  g_mutex_free(assm->mutex);
1548  smlSafeFree((gpointer *)&assm);
1549 
1550  smlTrace(TRACE_EXIT, "%s", __func__);
1551 }
1552 
1553 /* This function returns the highest command id that is still in the
1554  * assembler after flushing which is: number of statuses + 1 */
1555 unsigned int smlXmlAssemblerFlush(SmlXmlAssembler *assm)
1556 {
1557  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
1558  smlAssert(assm);
1559 
1560  unsigned int newid = 1;
1561  SmlBool missing = FALSE;
1562 
1563  /* Remove the statuses */
1564  GList *s = NULL;
1565  GList *s2 = g_list_copy(assm->statuses);
1566  for (s = s2; s; s = s->next) {
1567  SmlXmlAssemblerStatus *status = s->data;
1568  /* Only remove statuses that were added already */
1569  if (!status->buffer) {
1570  /* If the status was not yet added, we have to reset the command id.
1571  * We start with id #2 (since #1 will be the header reply) */
1572  /* FIXME: why should we reset a command id? */
1573  newid++;
1574  // status->cmdID = newid;
1575  missing = TRUE;
1576  } else if (!missing) {
1577  xmlBufferFree(status->buffer);
1578  assm->statuses = g_list_remove(assm->statuses, status);
1579  smlSafeFree((gpointer *)&status);
1580  assm->reserved_statuses--;
1581  assm->added_statuses--;
1582  }
1583  }
1584  g_list_free(s2);
1585 
1586  /* Remove all commands that are complete
1587  * Perhaps more correct remove all commands from the last message.
1588  * The actual commands are cached to support error 407 handling.
1589  * (AUTHENTICATION_REQUIRED)
1590  */
1591  if (assm->last_commands)
1592  flush_list(assm->last_commands);
1593  assm->last_commands = assm->commands;
1594  assm->commands = NULL;
1595 
1596  /* Reset LargeObject handling */
1597  assm->moreDataSet = FALSE;
1598 
1599  smlTrace(TRACE_EXIT, "%s: %i", __func__, newid);
1600  return newid;
1601 }
1602 
1603 SmlBool smlXmlAssemblerStart(SmlXmlAssembler *assm, SmlSession *session, SmlError **error)
1604 {
1605  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, session, error);
1606  CHECK_ERROR_REF
1607  smlAssert(assm);
1608  smlAssert(g_mutex_trylock(assm->mutex));
1609  smlAssert(session);
1610 
1611  assm->session = session;
1612 
1613  g_mutex_unlock(assm->mutex);
1614  smlTrace(TRACE_EXIT, "%s", __func__);
1615  return TRUE;
1616 }
1617 
1618 
1619 SmlBool smlXmlAssemblerEnd(SmlXmlAssembler *assm, SmlError **error)
1620 {
1621  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, assm, error);
1622  CHECK_ERROR_REF
1623  smlAssert(assm);
1624  smlAssert(g_mutex_trylock(assm->mutex));
1625 
1626  //Close syncml
1627  if (!_smlXmlAssemblerEndNode(assm, error))
1628  goto error;
1629 
1630  if (_smlXmlAssemblerEndNode(assm, NULL)) {
1631  smlErrorSet(error, SML_ERROR_GENERIC, "Extra node open");
1632  goto error;
1633  }
1634 
1635  g_mutex_unlock(assm->mutex);
1636  smlTrace(TRACE_EXIT, "%s", __func__);
1637  return TRUE;
1638 
1639 error:
1640  g_mutex_unlock(assm->mutex);
1641  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1642  return FALSE;
1643 }
1644 
1645 SmlBool smlXmlAssemblerAddChildren(SmlXmlAssembler *assm, GList *b, SmlError **error)
1646 {
1647  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, b, error);
1648  CHECK_ERROR_REF
1649  smlAssert(assm);
1650  const char *opt = smlAssemblerGetOption(assm->assembler, "ONLY_REPLACE");
1651  SmlBool onlyReplace = (opt && atoi(opt)) ? TRUE : FALSE;
1652 
1653  SmlXmlAssemblerCommand *cmd = NULL;
1654  const char *cmdname = NULL;
1655 
1656  for (; b; b = b->next) {
1657  cmd = b->data;
1658  switch (cmd->nodeType) {
1659  case SML_ASSEMBLER_NODE_OPEN:
1660  /* Add the corresponding command opener */
1661 
1662  if (cmd->cmdType == SML_COMMAND_TYPE_ADD && onlyReplace)
1663  cmdname = SML_ELEMENT_REPLACE;
1664  else {
1665  cmdname = smlCommandTypeToString(cmd->cmdType, error);
1666  if (!cmdname)
1667  goto error;
1668  }
1669  smlTrace(TRACE_INTERNAL, "opening node %s", cmdname);
1670 
1671  if (!_smlXmlAssemblerStartNode(assm, cmdname, error))
1672  goto error;
1673 
1674  int rc = xmlTextWriterWriteRawLen(assm->writer, xmlBufferContent(cmd->buffer), xmlBufferLength(cmd->buffer));
1675  if (rc < 0) {
1676  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to write raw node data (%d).", rc);
1677  goto error;
1678  }
1679 
1680  if (cmd->children) {
1681  if (!smlXmlAssemblerAddChildren(assm, cmd->children, error))
1682  goto error;
1683  }
1684 
1685  smlTrace(TRACE_INTERNAL, "%s: closing node", __func__);
1686  if (!_smlXmlAssemblerEndNode(assm, error))
1687  goto error;
1688  break;
1689  case SML_ASSEMBLER_NODE_CLOSE:
1690  /*smlTrace(TRACE_INTERNAL, "%s: closing node". __func__);
1691  if (!_smlXmlAssemblerEndNode(assm, error))
1692  goto error;*/
1693  break;
1694  }
1695  }
1696 
1697  smlTrace(TRACE_EXIT, "%s", __func__);
1698  return TRUE;
1699 
1700 error:
1701  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1702  return FALSE;
1703 }
1704 
1705 SmlBool smlXmlAssemblerRunFull(SmlXmlAssembler *assm, char **data, unsigned int *size, SmlBool *end, SmlBool final, SmlBool check, unsigned int maxsize, SmlError **error)
1706 {
1707  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %i, %i, %i, %p)", __func__, assm, data, size, end, final, check, maxsize, error);
1708  CHECK_ERROR_REF
1709  smlAssert(assm);
1710  smlAssert(g_mutex_trylock(assm->mutex));
1711  smlAssert(data);
1712  smlAssert(size);
1713  smlAssert(assm->session);
1714  unsigned int buffersize = 0;
1715 
1716  /* Return an error if there is no header */
1717  if (!assm->header_buffer) {
1718  smlErrorSet(error, SML_ERROR_GENERIC, "No header available");
1719  goto error;
1720  }
1721 
1722  /* Return an error if there is no status/command. Note that if
1723  * statuses have been reserved, at least the first status
1724  * must be added */
1725  if (check && !assm->statuses && !assm->commands) {
1726  smlErrorSet(error, SML_ERROR_GENERIC, "No status/command available");
1727  goto error;
1728  }
1729 
1730  if (check && assm->statuses && final) {
1731  SmlXmlAssemblerStatus *status = assm->statuses->data;
1732  if (!status->buffer) {
1733  smlErrorSet(error, SML_ERROR_GENERIC, "Missing the first status with cmdRef %i", status->cmdRef);
1734  goto error;
1735  }
1736  }
1737 
1738  /* Create the final buffer and writer */
1739  xmlBuffer *buffer = xmlBufferCreateSize(BUFFER_SIZE);
1740  if (!buffer) {
1741  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
1742  goto error;
1743  }
1744 
1745  assm->writer = xmlNewTextWriterMemory(buffer, 0);
1746  if (!assm->writer) {
1747  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
1748  goto error_free_buffer;
1749  }
1750 
1751  /* Sync4j has a bug and cannot handle the default XML encoding.
1752  * The default XML encoding is UTF-8. So this default encoding
1753  * is explicitly added to support buggy XML parsers.
1754  */
1755  if (xmlTextWriterStartDocument(assm->writer, NULL, "UTF-8", NULL) < 0) {
1756  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
1757  goto error_free_writer;
1758  }
1759 
1760  /* Add the syncml start node */
1761  switch (assm->session->version) {
1762  case SML_VERSION_10:
1763  xmlTextWriterWriteRaw(assm->writer, (xmlChar *)"<!DOCTYPE SyncML PUBLIC \"-//SYNCML//DTD SyncML 1.0//EN\" \"http://www.syncml.org/docs/syncml_represent_v10_20001207.dtd\">");
1764  if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_SYNCML, SML_NAMESPACE_SYNCML10, error))
1765  goto error_free_writer;
1766  break;
1767  case SML_VERSION_11:
1768  xmlTextWriterWriteRaw(assm->writer, (xmlChar *)"<!DOCTYPE SyncML PUBLIC \"-//SYNCML//DTD SyncML 1.1//EN\" \"http://www.syncml.org/docs/syncml_represent_v11_20020213.dtd\">");
1769  if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_SYNCML, SML_NAMESPACE_SYNCML11, error))
1770  goto error_free_writer;
1771  break;
1772  case SML_VERSION_12:
1773  xmlTextWriterWriteRaw(assm->writer, (xmlChar *)"<!DOCTYPE SyncML PUBLIC \"-//SYNCML//DTD SyncML 1.2//EN\" \"http://www.openmobilealliance.org/tech/DTD/OMA-TS-SyncML_RepPro_DTD-V1_2.dtd\">");
1774  if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_SYNCML, SML_NAMESPACE_SYNCML12, error))
1775  goto error_free_writer;
1776  break;
1777  default:
1778  smlErrorSet(error, SML_ERROR_GENERIC, "Unknown version");
1779  goto error_free_writer;
1780  }
1781 
1782  /* Add the header */
1783  int rc = xmlTextWriterWriteRawLen(assm->writer, xmlBufferContent(assm->header_buffer), xmlBufferLength(assm->header_buffer));
1784  if (rc < 0) {
1785  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to write raw header data (%d).", rc);
1786  goto error_free_writer;
1787  }
1788 
1789  /* Start the sync body */
1790  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCBODY, error))
1791  goto error_free_writer;
1792 
1793  // FIXME: why do we need 20 extra bytes here?
1794  // FIXME: buffersize is never read !!!
1795  buffersize += 20 + xmlBufferLength(assm->header_buffer);
1796 
1797  /* Add the statuses */
1798  GList *b = NULL;
1799  SmlBool missingstatus = FALSE;
1800  smlTrace(TRACE_INTERNAL, "%s: Now adding %i statuses",
1801  __func__, g_list_length(assm->statuses));
1802  for (b = assm->statuses; b; b = b->next) {
1803  SmlXmlAssemblerStatus *status = b->data;
1804  if (!status->buffer || xmlBufferLength(status->buffer) == 0) {
1805  if (status->cmdRef == 0 && check) {
1806  smlErrorSet(error, SML_ERROR_GENERIC, "Reserved status 0 has not been added");
1807  goto error_free_writer;
1808  }
1809  smlTrace(TRACE_INTERNAL, "%s: Reserved status %i is missing",
1810  __func__, status->cmdRef);
1811  missingstatus = TRUE;
1812  break;
1813  }
1814 
1815  buffersize += xmlBufferLength(status->buffer);
1816  //if (maxsize && buffersize > maxsize)
1817  // break;
1818  rc = xmlTextWriterWriteRawLen(assm->writer, xmlBufferContent(status->buffer), xmlBufferLength(status->buffer));
1819  if (rc < 0) {
1820  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to write raw status data (%d - %d of %d: %s).",
1821  rc, xmlBufferLength(status->buffer),
1822  buffersize, xmlBufferContent(status->buffer));
1823  goto error_free_writer;
1824  }
1825  }
1826 
1827  /* We cannot have missing statuses when its final. And we cannot add commands */
1828  if (missingstatus && final && check) {
1829  smlErrorSet(error, SML_ERROR_GENERIC, "Reserved status has not been added");
1830  goto error_free_writer;
1831  }
1832 
1833  /* Add the commands. Parent commands are added in the order they were added. */
1834  if (!smlXmlAssemblerAddChildren(assm, assm->commands, error))
1835  goto error_free_writer;
1836 
1837  if (final) {
1838  smlTrace(TRACE_INTERNAL, "%s: setting final", __func__);
1839  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_FINAL, "", error))
1840  goto error_free_writer;
1841  }
1842 
1843  /* Close syncbody */
1844  if (!_smlXmlAssemblerEndNode(assm, error))
1845  goto error_free_writer;
1846 
1847  /* Close syncml */
1848  if (!_smlXmlAssemblerEndNode(assm, error))
1849  goto error_free_writer;
1850 
1851  if (xmlTextWriterEndDocument(assm->writer) < 0) {
1852  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
1853  goto error;
1854  }
1855 
1856 
1857  xmlFreeTextWriter(assm->writer);
1858  assm->writer = NULL;
1859 
1860  // the buffer was created with xmlBufferCreateSize
1861  // so we must free it via the libxml API and not via smlSafeCFree
1862  *size = xmlBufferLength(buffer);
1863  *data = g_strndup((const char *) xmlBufferContent(buffer), *size);
1864  xmlBufferFree(buffer);
1865  if (end) {
1866  if (final && !assm->commands)
1867  *end = TRUE;
1868  else
1869  *end = FALSE;
1870  }
1871  smlTrace(TRACE_INTERNAL, "%s: Message Assembled: %s",
1872  __func__, VA_STRING(*data));
1873 
1874  g_mutex_unlock(assm->mutex);
1875  smlTrace(TRACE_EXIT, "%s", __func__);
1876  return TRUE;
1877 
1878 error_free_writer:
1879  xmlFreeTextWriter(assm->writer);
1880  assm->writer = NULL;
1881 error_free_buffer:
1882  xmlBufferFree(buffer);
1883 error:
1884  g_mutex_unlock(assm->mutex);
1885  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1886  return FALSE;
1887 }
1888 
1889 static unsigned int calc_list(GList *list)
1890 {
1891  unsigned int size = 0;
1892 
1893  for (; list; list = list->next) {
1894  SmlXmlAssemblerCommand *cmd = list->data;
1895  size += 15;
1896  if (cmd->nodeType != SML_ASSEMBLER_NODE_CLOSE) {
1897  size += xmlBufferLength(cmd->buffer);
1898  if (cmd->children)
1899  size += calc_list(cmd->children);
1900  }
1901  }
1902 
1903  return size;
1904 }
1905 
1906 SmlBool smlXmlAssemblerRun(SmlXmlAssembler *assm, char **data, unsigned int *size, SmlBool *end, SmlBool final, unsigned int maxsize, SmlError **error)
1907 {
1908  CHECK_ERROR_REF
1909  SmlBool ans = smlXmlAssemblerRunFull(assm, data, size, end, final, TRUE, maxsize, error);
1910 
1911  if (ans)
1912  smlLog("sent-%i.xml", *data, *size);
1913  else
1914  smlTrace(TRACE_ERROR, "%s - %s", __func__, smlErrorPrint(error));
1915 
1916  return ans;
1917 }
1918 
1919 unsigned int smlXmlAssemblerCheckSize(SmlXmlAssembler *assm, SmlBool headeronly, SmlError **error)
1920 {
1921  CHECK_ERROR_REF
1922  smlAssert(assm);
1923  SmlXmlAssemblerStatus *status = NULL;
1924  unsigned int size = 0;
1925 
1926  /* Add the size if the syncml tags etc */
1927  size += 20;
1928 
1929  /* Add the size of the header */
1930  if (assm->header_buffer)
1931  size += xmlBufferLength(assm->header_buffer);
1932 
1933  if (!headeronly) {
1934  /* Add the size of the status */
1935  GList *b = NULL;
1936  for (b = assm->statuses; b; b = b->next) {
1937  status = b->data;
1938  if (!status->buffer)
1939  break;
1940 
1941  size += xmlBufferLength(status->buffer);
1942  }
1943 
1944  /* Add the size of the commands */
1945  size += calc_list(assm->commands);
1946  }
1947 
1948  return size;
1949 }
1950 
1951 SmlBool smlXmlAssemblerNextCmdRef(SmlXmlAssembler *assm, unsigned int *cmdRef, unsigned int *msgRef)
1952 {
1953  smlAssert(assm);
1954  smlAssert(cmdRef);
1955  smlAssert(msgRef);
1956  SmlXmlAssemblerStatus *status = NULL;
1957 
1958  GList *b = NULL;
1959  for (b = assm->statuses; b; b = b->next) {
1960  status = b->data;
1961  if (!status->buffer) {
1962  *cmdRef = status->cmdRef;
1963  *msgRef = status->msgRef;
1964  return TRUE;
1965  }
1966  }
1967 
1968  return FALSE;
1969 }
1970 
1979 {
1980  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assembler, functions, error);
1981  CHECK_ERROR_REF
1982 
1983  SmlXmlAssembler *assm = smlTryMalloc0(sizeof(SmlXmlAssembler), error);
1984  if (!assm)
1985  goto error;
1986  assm->assembler = assembler;
1987  assm->mutex = g_mutex_new();
1988  if (!assm->mutex)
1989  goto error_mutex;
1990 
1991  functions->start = (SmlAssemblerStartFunction)smlXmlAssemblerStart;
1992  functions->free = (SmlAssemblerFreeFunction)smlXmlAssemblerFree;
1993  functions->run = (SmlAssemblerRunFunction)smlXmlAssemblerRun;
1994  functions->end = (SmlAssemblerEndFunction)smlXmlAssemblerEnd;
1995  functions->add_header = (SmlAssemblerHeaderFunction)smlXmlAssemblerAddHeader;
1996  functions->start_cmd = (SmlAssemblerStartCommandFunction)smlXmlAssemblerStartCommand;
1997  functions->end_cmd = (SmlAssemblerEndCommandFunction)smlXmlAssemblerEndCommand;
1998  functions->rem_cmd = (SmlAssemblerRemCommandFunction)smlXmlAssemblerRemCommand;
1999  functions->add_status = (SmlAssemblerStatusFunction)smlXmlAssemblerAddStatus;
2000  functions->rem_status = (SmlAssemblerRemStatusFunction)smlXmlAssemblerRemStatus;
2001  functions->reserve_status = (SmlAssemblerReserveStatusFunction)smlXmlAssemblerReserveStatus;
2002  functions->missing_status = (SmlAssemblerStatusMissingFunction)smlXmlAssemblerMissingStatus;
2003  functions->check_size = (SmlAssemblerCheckFunction)smlXmlAssemblerCheckSize;
2004  functions->next_cmdref = (SmlAssemblerNextCmdRefFunction)smlXmlAssemblerNextCmdRef;
2005  functions->flush = (SmlAssemblerFlushFunction)smlXmlAssemblerFlush;
2006  functions->restore_cmds = (SmlAssemblerRestoreCommandsFunction)smlXmlAssemblerRestoreCommands;
2007 
2008  smlTrace(TRACE_EXIT, "%s: %p", __func__, assm);
2009  return assm;
2010 
2011 error_mutex:
2012  smlSafeFree((gpointer *)&assm);
2013  smlErrorSet(error, SML_ERROR_GENERIC, "%s - Cannot create new mutex.", __func__);
2014 error:
2015  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2016  return NULL;
2017 }
2018 
2019 static SmlBool _smlXmlDevInfDataStoreAssembleRxTx(SmlXmlAssembler *assm, const char *element, const char *cttype, const char *version, SmlError **error)
2020 {
2021  smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %s, %p)", __func__, assm, VA_STRING(element), VA_STRING(cttype), VA_STRING(version), error);
2022  CHECK_ERROR_REF
2023  smlAssert(assm);
2024  smlAssert(element);
2025  smlAssert(cttype);
2026 
2027  if (!_smlXmlAssemblerStartNode(assm, element, error))
2028  goto error;
2029 
2030  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_CTTYPE, cttype, error))
2031  goto error;
2032 
2033  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERCT, version, error))
2034  goto error;
2035 
2036  if (!_smlXmlAssemblerEndNode(assm, error))
2037  goto error;
2038 
2039  smlTrace(TRACE_EXIT, "%s", __func__);
2040  return TRUE;
2041 
2042 error:
2043  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2044  return FALSE;
2045 }
2046 
2047 static SmlBool _smlXmlDevInfDataStoreAssembleCTCap(
2048  SmlXmlAssembler *assm,
2049  const SmlDevInfCTCap *ctcap,
2050  SmlBool flat,
2051  SmlError **error)
2052 {
2053  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, assm, ctcap, error);
2054  CHECK_ERROR_REF
2055  smlAssert(assm);
2056  smlAssert(ctcap);
2057 
2058  if (!flat)
2059  {
2060  // start CTCap
2061  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CTCAP, error))
2062  goto error;
2063  }
2064 
2065  // add CTType
2066  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_CTTYPE, ctcap->ct->cttype, error))
2067  goto error;
2068 
2069  // add VerCT
2070  // SyncML 1.0 and 1.1 does not support VerCT in CTCap
2071  // because this info is in the VERSION property.
2072  if (!flat)
2073  {
2074  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERCT, ctcap->ct->verct, error))
2075  goto error;
2076  }
2077 
2078  // add properties
2079  GList *hprop = NULL;
2080  for (hprop = ctcap->properties; hprop; hprop = hprop->next)
2081  {
2082  SmlDevInfProperty *property = hprop->data;
2083 
2084  if (!flat)
2085  {
2086  // add Property
2087  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_PROPERTY, error))
2088  goto error;
2089  }
2090 
2091  // add PropName
2092  if (property->propName != NULL &&
2093  !_smlXmlAssemblerAddString(assm, SML_ELEMENT_PROPNAME, property->propName, error))
2094  goto error;
2095 
2096  // add DataType
2097  if (property->dataType != NULL &&
2098  !_smlXmlAssemblerAddString(assm, SML_ELEMENT_DATATYPE, property->dataType, error))
2099  goto error;
2100 
2101  // add MaxOccur
2102  if (property->maxOccur > 0 &&
2103  !_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXOCCUR, property->maxOccur, error))
2104  goto error;
2105 
2106  // add MaxSize
2107  if (property->maxSize > 0) {
2108  /* There was a change of the element name in OMA DS DevInf 1.2. */
2109  if (flat) {
2110  if (!_smlXmlAssemblerAddID(
2111  assm,
2112  SML_ELEMENT_SIZE,
2113  property->maxSize,
2114  error))
2115  goto error;
2116  } else {
2117  if (!_smlXmlAssemblerAddID(
2118  assm,
2119  SML_ELEMENT_MAXSIZE,
2120  property->maxSize,
2121  error))
2122  goto error;
2123  }
2124  }
2125 
2126  // add NoTruncate
2127  if (property->noTruncate)
2128  {
2129  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_NOTRUNCATE, error))
2130  goto error;
2131  if (!_smlXmlAssemblerEndNode(assm, error))
2132  goto error;
2133  }
2134 
2135  // add DisplayName
2136  if (property->displayName != NULL &&
2137  !_smlXmlAssemblerAddString(assm, SML_ELEMENT_DISPLAYNAME, property->displayName, error))
2138  goto error;
2139 
2140  // add values
2141  GList *hvalue = NULL;
2142  for (hvalue = property->valEnums; hvalue; hvalue = hvalue->next)
2143  {
2144  char *valEnum = hvalue->data;
2145 
2146  // add ValEnum
2147  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VALENUM, valEnum, error))
2148  goto error;
2149 
2150  } // end values for loop
2151 
2152  // add parameters
2153  GList *hparam = NULL;
2154  for (hparam = property->propParams; hparam; hparam = hparam->next)
2155  {
2156  SmlDevInfPropParam *param = hparam->data;
2157 
2158  if (!flat)
2159  {
2160  // add PropParam
2161  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_PROPPARAM, error))
2162  goto error;
2163  }
2164 
2165  // add ParamName
2166  if (param->paramName != NULL &&
2167  !_smlXmlAssemblerAddString(assm, SML_ELEMENT_PARAMNAME, param->paramName, error))
2168  goto error;
2169 
2170  // add DataType
2171  if (param->dataType != NULL &&
2172  !_smlXmlAssemblerAddString(assm, SML_ELEMENT_DATATYPE, param->dataType, error))
2173  goto error;
2174 
2175  // add DisplayName
2176  if (param->displayName != NULL &&
2177  !_smlXmlAssemblerAddString(assm, SML_ELEMENT_DISPLAYNAME, param->displayName, error))
2178  goto error;
2179 
2180  // add values
2181  hvalue = NULL;
2182  for (hvalue = param->valEnums; hvalue; hvalue = hvalue->next)
2183  {
2184  char *valEnum = hvalue->data;
2185 
2186  // add ValEnum
2187  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VALENUM, valEnum, error))
2188  goto error;
2189 
2190  } // end values for loop
2191 
2192  if (!flat)
2193  {
2194  // end PropParam
2195  if (!_smlXmlAssemblerEndNode(assm, error))
2196  goto error;
2197  }
2198 
2199  } // end params for loop
2200 
2201  if (!flat)
2202  {
2203  // end Property
2204  if (!_smlXmlAssemblerEndNode(assm, error))
2205  goto error;
2206  }
2207 
2208  } //end of ctcap->properties for loop
2209 
2210  if (!flat)
2211  {
2212  // end CTCap
2213  if (!_smlXmlAssemblerEndNode(assm, error))
2214  goto error;
2215  }
2216 
2217  smlTrace(TRACE_EXIT, "%s", __func__);
2218  return TRUE;
2219 
2220 error:
2221  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2222  return FALSE;
2223 }
2224 
2225 static SmlBool _smlXmlDevInfDataStoreAssemble(
2226  SmlXmlAssembler *assm,
2227  SmlDevInfDataStore *datastore,
2228  SmlDevInf *devinf,
2229  SmlError **error)
2230 {
2231  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, assm, datastore, devinf, error);
2232  CHECK_ERROR_REF
2233  smlAssert(datastore);
2234  smlAssert(assm);
2235 
2236  GList *contentTypes = NULL;
2237 
2238  if (!datastore->sourceref) {
2239  smlErrorSet(error, SML_ERROR_GENERIC, "Missing sourceref");
2240  goto error;
2241  }
2242 
2243  if (!datastore->rxPrefContentType) {
2244  smlErrorSet(error, SML_ERROR_GENERIC, "Missing rx-pref");
2245  goto error;
2246  }
2247 
2248  if (!datastore->txPrefContentType) {
2249  smlErrorSet(error, SML_ERROR_GENERIC, "Missing tx-pref");
2250  goto error;
2251  }
2252 
2253  if (!datastore->synccap) {
2254  smlErrorSet(error, SML_ERROR_GENERIC, "No sync capabilities");
2255  goto error;
2256  }
2257 
2258  // Datastore
2259  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DATASTORE, error))
2260  goto error;
2261 
2262  //SourceRef
2263  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, datastore->sourceref, error))
2264  goto error;
2265 
2266  //displayname
2267  if (datastore->displayname) {
2268  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DISPLAYNAME, datastore->displayname, error))
2269  goto error;
2270  }
2271 
2272  //maxguidsize
2273  if (datastore->maxGUIDSize) {
2274  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXGUIDSIZE, datastore->maxGUIDSize, error))
2275  goto error;
2276  }
2277 
2278  //rx-pref
2279  if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_RXPREF, datastore->rxPrefContentType, datastore->rxPrefVersion, error))
2280  goto error;
2281  contentTypes = g_list_append(
2282  contentTypes,
2283  smlDevInfNewContentType(
2284  datastore->rxPrefContentType,
2285  datastore->rxPrefVersion, error));
2286 
2287  //rx
2288  if (datastore->rx) {
2289  GList *hl;
2290  for (hl = datastore->rx; hl; hl = hl->next)
2291  {
2292  SmlDevInfContentType *ct = hl->data;
2293  if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_RX, ct->cttype, ct->verct, error))
2294  goto error;
2295  contentTypes = g_list_append(
2296  contentTypes,
2297  smlDevInfNewContentType(ct->cttype, ct->verct, error));
2298  }
2299  }
2300 
2301  //tx-pref
2302  if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_TXPREF, datastore->txPrefContentType, datastore->txPrefVersion, error))
2303  goto error;
2304  contentTypes = g_list_append(
2305  contentTypes,
2306  smlDevInfNewContentType(
2307  datastore->txPrefContentType,
2308  datastore->txPrefVersion, error));
2309 
2310  //tx
2311  if (datastore->tx) {
2312  GList *hl;
2313  for (hl = datastore->tx; hl; hl = hl->next)
2314  {
2315  SmlDevInfContentType *ct = hl->data;
2316  if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_TX, ct->cttype, ct->verct, error))
2317  goto error;
2318  contentTypes = g_list_append(
2319  contentTypes,
2320  smlDevInfNewContentType(ct->cttype, ct->verct, error));
2321  }
2322  }
2323 
2324  // CTCap (if SyncML version 1.2 device info)
2325  if (devinf->version >= SML_DEVINF_VERSION_12)
2326  {
2327  GList *hct = NULL;
2328  for (hct = contentTypes; hct; hct = hct->next) {
2329  SmlDevInfContentType *ct = hct->data;
2330  const SmlDevInfCTCap *ctcap = smlDevInfGetCTCap(devinf, ct);
2331  if (ctcap != NULL)
2332  {
2333  // we found a matching CTCap
2334  // so let's dump it
2335  if (!_smlXmlDevInfDataStoreAssembleCTCap(assm, ctcap, FALSE, error))
2336  goto error;
2337  } else {
2338  // we have a content type without CTCap
2339  // this is a real source of trouble
2340  // WARNING: should we fail on this issue?
2341  smlTrace(TRACE_INTERNAL, "%s: found a content type (%s %d) without CTCap",
2342  __func__, VA_STRING(ct->cttype), ct->verct);
2343  }
2344  } // end of contentTypes for loop
2345  } // end of SML_DEVINF_VERSION_12
2346 
2347  // cleanup list of content types
2348  while(contentTypes)
2349  {
2350  SmlDevInfContentType *ct = contentTypes->data;
2351  contentTypes = g_list_remove(contentTypes, ct);
2352  smlDevInfFreeContentType(ct);
2353  }
2354 
2355  //Dsmem
2356  if (datastore->maxmem || datastore->maxid) {
2357  //Dsmem
2358  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DSMEM, error))
2359  goto error;
2360 
2361  //shared
2362  if (datastore->sharedMem) {
2363  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SHAREDMEM, "", error))
2364  goto error;
2365  }
2366 
2367  //maxid
2368  if (datastore->maxid) {
2369  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXID, datastore->maxid, error))
2370  goto error;
2371  }
2372 
2373  //maxmem
2374  if (datastore->maxmem) {
2375  if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXMEM, datastore->maxmem, error))
2376  goto error;
2377  }
2378 
2379  //DsMem
2380  if (!_smlXmlAssemblerEndNode(assm, error))
2381  goto error;
2382  }
2383 
2384  //SyncCap
2385  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCCAP, error))
2386  goto error;
2387 
2388  //SyncTypes
2389  if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY)) {
2390  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "1", error))
2391  goto error;
2392  }
2393  if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC)) {
2394  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "2", error))
2395  goto error;
2396  }
2397  if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_CLIENT)) {
2398  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "3", error))
2399  goto error;
2400  }
2401  if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_REFRESH_FROM_CLIENT)) {
2402  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "4", error))
2403  goto error;
2404  }
2405  if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_SERVER)) {
2406  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "5", error))
2407  goto error;
2408  }
2409  if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_REFRESH_FROM_SERVER)) {
2410  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "6", error))
2411  goto error;
2412  }
2413  if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC)) {
2414  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "7", error))
2415  goto error;
2416  }
2417 
2418  //SyncCap
2419  if (!_smlXmlAssemblerEndNode(assm, error))
2420  goto error;
2421 
2422  //DataStore
2423  if (!_smlXmlAssemblerEndNode(assm, error))
2424  goto error;
2425 
2426  smlTrace(TRACE_EXIT, "%s", __func__);
2427  return TRUE;
2428 
2429 error:
2430  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2431  return FALSE;
2432 }
2433 
2434 SmlBool smlXmlDevInfAssemble(SmlDevInf *devinf, SmlDevInfVersion version, char **data, unsigned int *size, SmlError **error)
2435 {
2436  smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p)", __func__, devinf, version, data, size, error);
2437  CHECK_ERROR_REF
2438  smlAssert(devinf);
2439  smlAssert(data);
2440  smlAssert(size);
2441 
2442  // sometime devinf->version is empty
2443  // both version fields should be identical
2444  smlTrace(TRACE_INTERNAL, "%s: devinf version: %i, version: %i", __func__, devinf->version, version);
2445  if (devinf->version == SML_DEVINF_VERSION_UNKNOWN)
2446  devinf->version = version;
2447  if (version == SML_DEVINF_VERSION_UNKNOWN)
2448  version = devinf->version;
2449  smlAssert(devinf->version == version);
2450 
2451  SmlXmlAssembler *assm = smlTryMalloc0(sizeof(SmlXmlAssembler), error);
2452  if (!assm)
2453  goto error;
2454 
2455  /* We first start a new parent writer that will write our command into a buffer */
2456  xmlBuffer *buffer = xmlBufferCreateSize(BUFFER_SIZE);
2457  if (!buffer) {
2458  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
2459  goto error_free_assm;
2460  }
2461 
2462  assm->writer = xmlNewTextWriterMemory(buffer, 0);
2463  if (!assm->writer) {
2464  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
2465  goto error_free_buffer;
2466  }
2467 
2468  //Devinf
2469  if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_DEVINF, SML_NAMESPACE_DEVINF, error))
2470  goto error_free_writer;
2471 
2472  //Verdtd
2473  switch (version) {
2474  case SML_DEVINF_VERSION_12:
2475  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.2", error))
2476  goto error_free_writer;
2477  break;
2478  case SML_DEVINF_VERSION_11:
2479  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.1", error))
2480  goto error_free_writer;
2481  break;
2482  case SML_DEVINF_VERSION_10:
2483  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.0", error))
2484  goto error_free_writer;
2485  break;
2486  case SML_DEVINF_VERSION_UNKNOWN:
2487  smlErrorSet(error, SML_ERROR_GENERIC, "Unknown devinf version");
2488  goto error_free_writer;
2489  break;
2490  }
2491 
2492  //Man
2493  if (devinf->manufacturer) {
2494  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MAN, devinf->manufacturer, error))
2495  goto error_free_writer;
2496  }
2497 
2498  //Mod
2499  if (devinf->model) {
2500  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MOD, devinf->model, error))
2501  goto error_free_writer;
2502  }
2503 
2504  //OEM
2505  if (devinf->oem) {
2506  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_OEM, devinf->oem, error))
2507  goto error_free_writer;
2508  }
2509 
2510  //FwV
2511  if (devinf->firmwareVersion) {
2512  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_FWV, devinf->firmwareVersion, error))
2513  goto error_free_writer;
2514  }
2515 
2516  //SwV
2517  if (devinf->softwareVersion) {
2518  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SWV, devinf->softwareVersion, error))
2519  goto error_free_writer;
2520  }
2521 
2522  //HwV
2523  if (devinf->hardwareVersion) {
2524  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_HWV, devinf->hardwareVersion, error))
2525  goto error_free_writer;
2526  }
2527 
2528  //DevID
2529  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DEVID, devinf->devid, error))
2530  goto error_free_writer;
2531 
2532  //Devtyp
2533  const char *devtype = smlDevInfDevTypeToString(devinf->devtyp, error);
2534  if (!devtype)
2535  goto error_free_writer;
2536 
2537  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DEVTYPE, devtype, error))
2538  goto error_free_writer;
2539 
2540  //UTC (>= 1.1)
2541  if (devinf->supportsUTC && version != SML_DEVINF_VERSION_10) {
2542  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_UTC, "", error))
2543  goto error_free_writer;
2544  }
2545 
2546  //Large objs (>= 1.1)
2547  if (devinf->supportsLargeObjs && version != SML_DEVINF_VERSION_10) {
2548  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SUPPORTLARGEOBJS, "", error))
2549  goto error_free_writer;
2550  }
2551 
2552  //Number of changes (>= 1.1)
2553  if (devinf->supportsNumberOfChanges && version != SML_DEVINF_VERSION_10) {
2554  if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SUPPORTNUMBEROFCHANGES, "", error))
2555  goto error_free_writer;
2556  }
2557 
2558  //Add the datastores
2559  GList *d = NULL;
2560  for (d = devinf->datastores; d; d = d->next) {
2561  SmlDevInfDataStore *datastore = d->data;
2562  if (!_smlXmlDevInfDataStoreAssemble(assm, datastore, devinf, error))
2563  goto error_free_writer;
2564  }
2565 
2566  // CTCap (only for SyncML 1.0 and 1.1)
2567  if (
2568  devinf->version < SML_DEVINF_VERSION_12 &&
2569  devinf->ctcaps) {
2570  if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CTCAP, error))
2571  goto error_free_writer;
2572 
2573  for (d = devinf->ctcaps; d; d = d->next) {
2574  SmlDevInfCTCap *ctcap = d->data;
2575 
2576  if (!_smlXmlDevInfDataStoreAssembleCTCap(assm, ctcap, TRUE, error))
2577  goto error_free_writer;
2578  }
2579 
2580  if (!_smlXmlAssemblerEndNode(assm, error))
2581  goto error_free_writer;
2582  }
2583 
2584  //Devinf
2585  if (!_smlXmlAssemblerEndNode(assm, error))
2586  goto error_free_writer;
2587 
2588  /* Now close the buffer */
2589  if (xmlTextWriterEndDocument(assm->writer) < 0) {
2590  smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
2591  goto error_free_writer;
2592  }
2593 
2594  xmlFreeTextWriter(assm->writer);
2595  assm->writer = NULL;
2596 
2597  *size = xmlBufferLength(buffer);
2598  *data = g_strndup((const char *) xmlBufferContent(buffer), *size);
2599 
2600  xmlBufferFree(buffer);
2601 
2602  smlSafeFree((gpointer *)&assm);
2603 
2604  smlTrace(TRACE_INTERNAL, "Message Assembled: %s", *data);
2605 
2606  smlTrace(TRACE_EXIT, "%s", __func__);
2607  return TRUE;
2608 
2609 error_free_writer:
2610  xmlFreeTextWriter(assm->writer);
2611  assm->writer = NULL;
2612 error_free_buffer:
2613  xmlBufferFree(buffer);
2614 error_free_assm:
2615  smlSafeFree((gpointer *)&assm);
2616 error:
2617  g_warning("%s: %s", __func__, smlErrorPrint(error));
2618  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2619  return FALSE;
2620 }
2621 
2622 void smlXmlAssemblerRestoreCommands(SmlXmlAssembler *assm)
2623 {
2624  smlTrace(TRACE_ENTRY, "%s", __func__);
2625  smlAssert(assm->commands == NULL);
2626 
2627  assm->commands = assm->last_commands;
2628  assm->last_commands = NULL;
2629 
2630  smlTrace(TRACE_EXIT, "%s", __func__);
2631 }
2632 
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
const char * smlAssemblerGetOption(SmlAssembler *assm, const char *optionname)
Gets a option from the assembler.
Definition: sml_parse.c:914
SmlXmlAssemblerNodeType nodeType
SmlBool smlItemGetData(SmlItem *item, char **data, unsigned int *size, SmlError **error)
Definition: sml_elements.c:495
SmlXmlAssembler * smlXmlAssemblerNew(SmlAssembler *assembler, SmlAssemblerFunctions *functions, SmlError **error)
Creates a new XML assembler.
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
void * smlTryMalloc0(long n_bytes, SmlError **error)
Safely mallocs.
Definition: sml_support.c:335
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
Represent an error.