sml_xml_assm.c
/*
* libsyncml - A syncml protocol implementation
* Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
* Copyright (C) 2007-2009 Michael Bell <michael.bell@opensync.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <libsyncml/syncml.h>
#include <libsyncml/syncml_internals.h>
#include <libsyncml/sml_elements_internals.h>
#include <libsyncml/sml_command_internals.h>
#include <libsyncml/sml_devinf_internals.h>
#include <libsyncml/sml_session_internals.h>
#include "libsyncml/sml_error_internals.h"
#include "sml_xml_assm.h"
#include "sml_xml_assm_internals.h"
#define BUFFER_SIZE 500
static SmlBool _smlXmlAssemblerStartNodeNS(SmlXmlAssembler *assm, const char *name, const char *uri, SmlError **error)
{
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(assm->writer);
smlAssert(name != NULL);
smlAssert(strlen(name) > 0);
smlTrace(TRACE_INTERNAL, "%s: Starting \"%s\"", __func__, VA_STRING(name));
int rc = xmlTextWriterStartElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri);
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to start node");
return FALSE;
}
return TRUE;
}
static SmlBool _smlXmlAssemblerStartNode(SmlXmlAssembler *assm, const char *name, SmlError **error)
{
CHECK_ERROR_REF
return _smlXmlAssemblerStartNodeNS(assm, name, NULL, error);
}
static SmlBool _smlXmlAssemblerEndNode(SmlXmlAssembler *assm, SmlError **error)
{
CHECK_ERROR_REF
smlTrace(TRACE_INTERNAL, "%s: Ending node", __func__);
int rc = xmlTextWriterEndElement(assm->writer);
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end node (%d).", rc);
return FALSE;
}
return TRUE;
}
static SmlBool _smlXmlAssemblerAddStringNS(SmlXmlAssembler *assm, const char *name, const char *uri, const char *value, SmlError **error)
{
CHECK_ERROR_REF
int rc = xmlTextWriterWriteElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri, (xmlChar *)value);
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add string (%d - %s:%s -> %s).", rc, VA_STRING(uri), VA_STRING(name), VA_STRING(value));
return FALSE;
}
return TRUE;
}
static SmlBool _smlXmlAssemblerAddString(SmlXmlAssembler *assm, const char *name, const char *value, SmlError **error)
{
CHECK_ERROR_REF
int rc = xmlTextWriterWriteElement(assm->writer, (xmlChar *)name, (xmlChar *)value);
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add pure string (%d - %s -> %s).", rc, VA_STRING(name), VA_STRING(value));
return FALSE;
}
return TRUE;
}
static SmlBool _smlXmlAssemblerAddData(SmlXmlAssembler *assm, const char *name, const char *value, unsigned int size, SmlBool raw, SmlError **error)
{
CHECK_ERROR_REF
int rc = 0;
if (!_smlXmlAssemblerStartNode(assm, name, error))
return FALSE;
if (raw)
rc = xmlTextWriterWriteRawLen(assm->writer, (xmlChar *)value, size);
else
rc = xmlTextWriterWriteFormatCDATA(assm->writer, "%*s", size, (xmlChar *)value);
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add data (%d).", rc);
return FALSE;
}
if (!_smlXmlAssemblerEndNode(assm, error))
return FALSE;
return TRUE;
}
static SmlBool _smlXmlAssemblerAddID(SmlXmlAssembler *assm, const char *name, unsigned int id, SmlError **error)
{
CHECK_ERROR_REF
int rc = xmlTextWriterWriteFormatElement(assm->writer, (xmlChar *)name, "%i", id);
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add id");
return FALSE;
}
return TRUE;
}
static SmlBool _smlXmlAssemblerAddIDNS(SmlXmlAssembler *assm, const char *name, const char *uri, unsigned int id, SmlError **error)
{
CHECK_ERROR_REF
int rc = xmlTextWriterWriteFormatElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri, "%i", id);
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add id");
return FALSE;
}
return TRUE;
}
static SmlBool _smlAssembleUseMaxObjSize(SmlXmlAssembler *assm)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
/* check the configuration */
const char *opt = smlAssemblerGetOption(assm->assembler, "USE_LARGEOBJECTS");
SmlBool supportsLargeObjects = (opt && !atoi(opt)) ? FALSE : TRUE;
/* server should react only the client behavior */
if (assm->session->sessionType == SML_SESSION_TYPE_SERVER)
{
if (smlAssemblerGetRemoteMaxObjSize(assm->assembler) <= 0)
supportsLargeObjects = FALSE;
if (smlAssemblerGetRemoteMaxMsgSize(assm->assembler) <= 0)
supportsLargeObjects = FALSE;
}
/* SyncML 1.0 does not support large objects */
if (assm->session->version == SML_VERSION_10)
supportsLargeObjects = FALSE;
/* If MaxObjsize must really be sent
* then check if it is configured
*/
if (smlSessionGetLocalMaxObjSize(assm->session) <= 0)
supportsLargeObjects = FALSE;
smlTrace(TRACE_EXIT, "%s - %d", __func__, supportsLargeObjects);
return supportsLargeObjects;
}
static SmlBool _smlAssembleMaxObjSize(SmlXmlAssembler *assm, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
CHECK_ERROR_REF
/* It is allowed to add MaxObjSize to the Alert or Sync
* command and libsyncml adds the MaxObjSize to the Sync
* command. Explanation from OMA DS Protocol v1.2:
*
* 6 Protocol Fundamentals
* 6.10 Large Object Handling
* 6.10.1 Conformance statements
* Citation: If a device supports receiving Large
* Objects it MUST declare the maximum size of
* object (<MaxObjSize> tag) it is capable of
* receiving as Meta information within the
* Alert or Sync command, as specified in
* [META].
*
* SyncML 1.0 does not support large objects. Therefore
* MaxObjSize must not be sent because otherwise this
* could result in errors (bad request/message).
*/
if (!_smlXmlAssemblerAddIDNS(assm, SML_ELEMENT_MAXOBJSIZE, SML_NAMESPACE_METINF, smlSessionGetLocalMaxObjSize(assm->session), error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlLocationAssemble(SmlLocation *location, SmlXmlAssembler *assm, const char *name, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, location, assm, VA_STRING(name), error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(location);
if (name) {
if (!_smlXmlAssemblerStartNode(assm, name, error))
goto error;
}
if (!location->locURI) {
smlErrorSet(error, SML_ERROR_GENERIC, "No locURI set");
goto error;
}
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_LOCURI, location->locURI, error))
goto error;
if (location->locName) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_LOCNAME, location->locName, error))
goto error;
}
if (name) {
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlAnchorAssemble(SmlAnchor *anchor, SmlXmlAssembler *assm, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, anchor, assm, error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(anchor);
/* Start the anchor and switch to syncml:metinf namespace.
* It is not possible to remove the namespace from Last and
* Next because otherwise the Status commands could be longer
* then the Alert commands. If this would happen then the
* Status commands alone could be longer than MaxMsgSize.
*
* It is not sure how a mobile would react in this situation.
*/
if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_ANCHOR, SML_NAMESPACE_METINF, error))
goto error;
if (!anchor->next) {
smlErrorSet(error, SML_ERROR_GENERIC, "No next set");
goto error;
}
/* SyncML Meta-Information DTD 1.1, Page 6, 5.1 Anchor - Content Model: (Last?, Next)
Last is optional, don't write a empty Last node.
*/
if (anchor->last) {
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_LAST, SML_NAMESPACE_METINF, anchor->last, error))
goto error;
}
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_NEXT, SML_NAMESPACE_METINF, anchor->next, error))
goto error;
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlItemAssemble(SmlItem *item, SmlXmlAssembler *assm, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, item, assm, error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(item);
if (assm->moreDataSet) {
smlErrorSet(error, SML_ERROR_GENERIC, "Trying to start a new item while last item had more data");
goto error;
}
/* Begin of Item */
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_ITEM, error))
goto error;
/* Target */
if (smlItemGetTarget(item)) {
if (!smlLocationAssemble(smlItemGetTarget(item), assm, SML_ELEMENT_TARGET, error))
goto error;
}
/* Source */
if (smlItemGetSource(item)) {
if (!smlLocationAssemble(smlItemGetSource(item), assm, SML_ELEMENT_SOURCE, error))
goto error;
}
/* SourceParent */
if (smlItemGetSourceParent(item)) {
if (!smlLocationAssemble(smlItemGetSourceParent(item), assm, SML_ELEMENT_SOURCE_PARENT, error))
goto error;
}
/* TargetParent */
if (smlItemGetTargetParent(item)) {
if (!smlLocationAssemble(smlItemGetTargetParent(item), assm, SML_ELEMENT_TARGET_PARENT, error))
goto error;
}
/* Data */
if (smlItemHasData(item)) {
if (item->disabled) {
if (!_smlXmlAssemblerAddData(assm, SML_ELEMENT_DATA, "", 0, item->raw, error))
goto error;
} else {
char *data = NULL;
unsigned int size = 0;
if (!smlItemGetData(item, &data, &size, error))
goto error;
if (!_smlXmlAssemblerAddData(assm, SML_ELEMENT_DATA, data, size, item->raw, error))
goto error;
}
}
/* MoreData */
if (item->moreData) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MOREDATA, "", error))
goto error;
assm->moreDataSet = TRUE;
}
/* End of Item */
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlCredAssemble(SmlCred *cred, SmlXmlAssembler *assm, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, cred, assm, error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(cred);
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CRED, error))
goto error;
//Meta
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
goto error;
switch (cred->format) {
case SML_FORMAT_TYPE_BASE64:
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_FORMAT, SML_NAMESPACE_METINF, SML_BASE64, error))
goto error;
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "SyncML credential: Unknown format %d.", cred->format);
goto error;
}
switch (cred->type) {
case SML_AUTH_TYPE_BASIC:
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_BASIC, error))
goto error;
break;
case SML_AUTH_TYPE_MD5:
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_MD5, error))
goto error;
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown format");
goto error;
}
//META
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
if (!_smlXmlAssemblerAddData(assm, SML_ELEMENT_DATA, cred->data, strlen(cred->data), TRUE, error))
goto error;
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlAccessAssemble(SmlXmlAssembler *assm, SmlCommand *change, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, change, error);
CHECK_ERROR_REF
smlAssert(change);
smlAssert(assm);
if (!change->private.access.item) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing item");
goto error;
}
if (!change->private.access.item->contenttype) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing contenttype");
goto error;
}
//Meta
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
goto error;
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, change->private.access.item->contenttype, error))
goto error;
//META
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
if (!smlItemAssemble(change->private.access.item, assm, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlChangeAssemble(SmlXmlAssembler *assm, SmlCommand *change, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, change, error);
CHECK_ERROR_REF
smlAssert(change);
smlAssert(assm);
if (!change->private.change.items ||
!g_list_length(change->private.change.items)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing items");
goto error;
}
/* libsyncml only supports one item per change command */
smlAssert(g_list_length(change->private.change.items) == 1);
SmlItem *item = g_list_nth_data(change->private.change.items, 0);
if (!item) {
smlErrorSet(error, SML_ERROR_GENERIC, "One item of the item list is NULL.");
goto error;
}
if (!item->contenttype) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing contenttype");
goto error;
}
//Meta
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
goto error;
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, item->contenttype, error))
goto error;
/* We will add the max obj size node, if USE_LARGEOBJECTS is true or not set at all.
* And the remote side must have set a maxobjsize if we are a server */
const char *opt = smlAssemblerGetOption(assm->assembler, "USE_LARGEOBJECTS");
SmlBool supportsLargeObjects = (opt && !atoi(opt)) ? FALSE : TRUE;
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));
if (supportsLargeObjects && change->size) {
if (!_smlXmlAssemblerAddIDNS(assm, SML_ELEMENT_SIZE, SML_NAMESPACE_METINF, change->size, error))
goto error;
}
//META
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
//Item(s)
/* Item has to deal with four different situations:
* 1. If this is a new item but the remote peer only supports
* the Replace command and the local peer is an OMA DS
* server then the ID/location must be send as target.
* If the local peer is an OMA DS client then nothing
* changes. FYI an OMA DS server must support the Add
* command.
* => Target must be set (Target is the former Source)
* 2. If this is a new item then the local peer only knows
* the ID of the item.
* => Source must be set
* 3. If this is a changed or deleted item and the local peer
* is an OMA DS server then the item must reference the ID
* of the remote peer because of the submitted map. Please
* not that the remote peer as an OMA DS client must only
* know its own ID of the item. The mapping of locations
* (IDs) is the exclusive job of the OMA DS server.
* => Target must be set (Source is optional)
* 4. If this is a changed or deleted item and the local peer
* is an OMA DS client then the item must reference the ID
* of the local peer only because the mapping of locations
* (IDs) must be done by the OMA DS server.
* => Source must be set
*/
SmlSessionType sessionType = assm->session->sessionType;
opt = smlAssemblerGetOption(assm->assembler, "ONLY_REPLACE");
if (change->type == SML_COMMAND_TYPE_ADD)
{
if (opt && atoi(opt) && /* remote peer supports ONLY_REPLACE */
sessionType == SML_SESSION_TYPE_SERVER) {
/* only Target must be set */
if (item->target)
smlLocationUnref(item->target);
item->target = item->source;
item->source = NULL;
} else {
/* only Source must be set */
if (item->target)
smlLocationUnref(item->target);
item->target = NULL;
}
} else {
if (sessionType == SML_SESSION_TYPE_SERVER) {
/* Target must be set */
if (item->source)
smlLocationUnref(item->source);
item->source = NULL;
} else {
/* only Source must be set */
if (item->target)
smlLocationUnref(item->target);
item->target = NULL;
}
}
if (!smlItemAssemble(item, assm, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlSyncAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
CHECK_ERROR_REF
smlAssert(cmd);
smlAssert(assm);
if (!cmd->target) {
smlErrorSet(error, SML_ERROR_GENERIC, "No target set");
goto error;
}
/* The specification OMA DS Protocol v1.2 allows to add
* MaxObjsize to Alert or Sync commands but all examples show
* it only in Alert commands. Therefore libsyncml adds it to
* Alert commands only.
*/
if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET, error))
goto error;
if (!cmd->source) {
smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
goto error;
}
if (!smlLocationAssemble(cmd->source, assm, SML_ELEMENT_SOURCE, error))
goto error;
if (_smlAssembleUseMaxObjSize(assm))
{
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
goto error;
if (!_smlAssembleMaxObjSize(assm, error))
goto error;
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
}
const char *opt = smlAssemblerGetOption(assm->assembler, "USE_NUMBEROFCHANGES");
SmlBool supportsNumberOfChanges = (opt && !atoi(opt)) ? FALSE : TRUE;
if (supportsNumberOfChanges && assm->session->version != SML_VERSION_10) {
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_NUMBEROFCHANGES, cmd->private.sync.numChanged, error))
goto error;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlMapItemAssemble(SmlXmlAssembler *assm, SmlMapItem *item, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, item, error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(item);
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_MAPITEM, error))
goto error;
if (item->source) {
if (!smlLocationAssemble(item->source, assm, SML_ELEMENT_SOURCE, error))
goto error;
}
if (item->target) {
if (!smlLocationAssemble(item->target, assm, SML_ELEMENT_TARGET, error))
goto error;
}
//MapItem
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlMapAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
CHECK_ERROR_REF
smlAssert(cmd);
smlAssert(assm);
if (!cmd->target) {
smlErrorSet(error, SML_ERROR_GENERIC, "No target set");
goto error;
}
if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET, error))
goto error;
if (!cmd->source) {
smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
goto error;
}
if (!smlLocationAssemble(cmd->source, assm, SML_ELEMENT_SOURCE, error))
goto error;
GList *m = NULL;
for (m = cmd->private.map.items; m; m = m->next) {
SmlMapItem *item = m->data;
if (!smlMapItemAssemble(assm, item, error))
goto error;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlAlertAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
CHECK_ERROR_REF
smlAssert(cmd);
smlAssert(assm);
/* Results itself and CmdID must be set by the caller.
* This function only add the Results specific content.
*/
/* No Resp */
/* Cred */
/* Data */
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_DATA, cmd->private.alert.type, error))
goto error;
/* Correlator */
/* Begin of Item */
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_ITEM, error))
goto error;
/* Target */
if (cmd->target) {
if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET, error))
goto error;
}
/* Source */
if (cmd->source) {
if (!smlLocationAssemble(cmd->source, assm, SML_ELEMENT_SOURCE, error))
goto error;
} else {
// NEXT MESSAGE alerts does not need a source/target
if (cmd->private.alert.type != SML_ALERT_NEXT_MESSAGE) {
smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
goto error;
}
}
/* SourceParent */
/* TargetParent */
/* Begin of META */
SmlBool meta = FALSE;
if (cmd->private.alert.contentType)
meta = TRUE;
if (cmd->private.alert.anchor)
meta = TRUE;
if (_smlAssembleUseMaxObjSize(assm))
meta = TRUE;
if (meta && !_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
goto error;
if (cmd->private.alert.contentType &&
!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF,
cmd->private.alert.contentType, error))
goto error;
if (cmd->private.alert.anchor &&
!smlAnchorAssemble(cmd->private.alert.anchor, assm, error))
goto error;
if (_smlAssembleUseMaxObjSize(assm) &&
!_smlAssembleMaxObjSize(assm, error))
goto error;
if (meta && !_smlXmlAssemblerEndNode(assm, error))
goto error;
/* End of META */
/* End of Item */
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlResultsAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
CHECK_ERROR_REF
smlAssert(cmd);
smlAssert(assm);
/* Results itself and CmdID must be set by the caller.
* This function only add the Results specific content.
*/
/* MsgRef */
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MSGREF, cmd->private.results.status->msgRef, error))
goto error;
/* CmdRef */
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDREF, cmd->private.results.status->cmdRef, error))
goto error;
/* Meta */
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
goto error;
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, cmd->private.results.status->item->contenttype, error))
goto error;
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
/* TargetRef */
if (cmd->private.results.status->targetRef) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_TARGETREF, cmd->private.results.status->targetRef->locURI, error))
goto error;
}
/* SourceRef */
if (cmd->private.results.status->sourceRef) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, cmd->private.results.status->sourceRef->locURI, error))
goto error;
}
/* Item */
if (!smlItemAssemble(cmd->private.results.status->item, assm, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlAssemblerAddHeader(SmlXmlAssembler *assm, SmlSession *session, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, session, error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(g_mutex_trylock(assm->mutex));
smlAssert(session);
/* Lets see if there already was a header before */
if (assm->header_buffer) {
xmlBufferFree(assm->header_buffer);
assm->header_buffer = NULL;
}
/* We first start a new writer that will write our header into a buffer */
assm->header_buffer = xmlBufferCreateSize(BUFFER_SIZE);
if (!assm->header_buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
goto error;
}
assm->writer = xmlNewTextWriterMemory(assm->header_buffer, 0);
if (!assm->writer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
goto error;
}
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCHDR, error))
goto error;
if (!session->protocol) {
smlErrorSet(error, SML_ERROR_GENERIC, "No version set");
goto error;
}
if (!session->version) {
smlErrorSet(error, SML_ERROR_GENERIC, "No dtd set");
goto error;
}
switch (session->protocol) {
case SML_PROTOCOL_SYNCML:
switch (session->version) {
case SML_VERSION_10:
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.0", error))
goto error;
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERPROTO, SML_VERSION_STRING_10, error))
goto error;
break;
case SML_VERSION_11:
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.1", error))
goto error;
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERPROTO, SML_VERSION_STRING_11, error))
goto error;
break;
case SML_VERSION_12:
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.2", error))
goto error;
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERPROTO, SML_VERSION_STRING_12, error))
goto error;
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown version");
goto error;
}
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown protocol");
goto error;
}
if (session->sessionID) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SESSIONID, session->sessionID, error))
goto error;
}
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MSGID, session->lastMessageID, error))
goto error;
if (!smlLocationAssemble(session->target, assm, SML_ELEMENT_TARGET, error))
goto error;
if (!smlLocationAssemble(session->source, assm, SML_ELEMENT_SOURCE, error))
goto error;
if (session->responseURI &&
!_smlXmlAssemblerAddString(assm, SML_ELEMENT_RESPURI, session->responseURI, error))
goto error;
/* The credentials must be present and calculated for the correct
* authentication schema (e.g. syncml:auth-basic or syncml:auth-md5).
*/
if (session->cred != NULL && session->cred->data != NULL) {
if (!smlCredAssemble(session->cred, assm, error))
goto error;
}
/* What do we have to check here?
*
* SyncML 1.0 does not require any checks because large object
* handling was first specified for SyncML 1.1.
*
* SyncML 1.1 and 1.2 require some checks.
*
* 1. OMA DS server MUST support large objects.
* This means that a local MaxMsgSize and a local MaxObjSize
* MUST be specified.
* 2. If an OMA DS client suppors large objects then it MUST
* match the same requirements like the OMA DS server.
*/
if (session->protocol == SML_PROTOCOL_SYNCML &&
session->version > SML_VERSION_10)
{
/* This is a SyncML protocol version which supports
* large objects.
*/
if (session->sessionType == SML_SESSION_TYPE_SERVER ||
smlSessionGetLocalMaxObjSize(session) > 0)
{
/* Large object support is enabled. */
if (smlSessionGetLocalMaxMsgSize(session) < 1)
{
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
"MaxMsgSize must be set.");
goto error;
}
if (smlSessionGetLocalMaxObjSize(session) < 1)
{
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
"MaxObjSize must be set.");
goto error;
}
}
}
/* set MaxMsgSize if available */
unsigned int localMaxMsgSize = smlSessionGetLocalMaxMsgSize(session);
if (localMaxMsgSize) {
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
goto error;
/* Do not add MaxObjSize here.
* Please see OMA DS Protocol v1.2 section 6.10.1
*/
if (!_smlXmlAssemblerAddIDNS(assm, SML_ELEMENT_MAXMSGSIZE, SML_NAMESPACE_METINF, localMaxMsgSize, error))
goto error;
//META
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
}
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
/* Now close the buffer and get the content */
if (xmlTextWriterEndDocument(assm->writer) < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
goto error;
}
xmlFreeTextWriter(assm->writer);
assm->writer = NULL;
g_mutex_unlock(assm->mutex);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
if (assm->writer) {
xmlFreeTextWriter(assm->writer);
assm->writer = NULL;
}
if (assm->header_buffer) {
xmlBufferFree(assm->header_buffer);
assm->header_buffer = NULL;
}
g_mutex_unlock(assm->mutex);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlAssemblerStartCommand(SmlXmlAssembler *assm, unsigned int parentID, SmlCommand *cmd, SmlError **error)
{
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(cmd);
if (cmd->type == SML_COMMAND_TYPE_UNKNOWN) {
smlErrorSet(error, SML_ERROR_GENERIC, "No cmd set");
goto error;
}
if (!cmd->cmdID) {
smlErrorSet(error, SML_ERROR_GENERIC, "No cmd ID set");
goto error;
}
/* Lets see if there already was a header before */
if (!assm->header_buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Header not yet added");
goto error;
}
SmlXmlAssemblerCommand *assmcmd = smlTryMalloc0(sizeof(SmlXmlAssemblerCommand), error);
if (!assmcmd)
goto error;
assmcmd->nodeType = SML_ASSEMBLER_NODE_OPEN;
assmcmd->cmdID = cmd->cmdID;
assmcmd->cmdType = cmd->type;
GList **appendto = &(assm->commands);
if (parentID) {
/* If we have a parent we first have to search for this command */
SmlXmlAssemblerCommand *excmd = NULL;
GList *b = NULL;
for (b = assm->commands; b; b = b->next) {
excmd = b->data;
if (excmd->cmdID == parentID)
appendto = &(excmd->children);
}
}
/* We first start a new parent writer that will write our command into a buffer */
assmcmd->buffer = xmlBufferCreateSize(BUFFER_SIZE);
if (!assmcmd->buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
goto error_free_cmd;
}
assm->writer = xmlNewTextWriterMemory(assmcmd->buffer, 0);
if (!assm->writer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
goto error_free_buffer;
}
/* We start without the root node */
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDID, cmd->cmdID, error))
goto error_free_writer;
switch (cmd->type) {
case SML_COMMAND_TYPE_ALERT:
if (!smlAlertAssemble(assm, cmd, error))
goto error_free_writer;
break;
case SML_COMMAND_TYPE_SYNC:
if (!smlSyncAssemble(assm, cmd, error))
goto error_free_writer;
break;
case SML_COMMAND_TYPE_ADD:
case SML_COMMAND_TYPE_REPLACE:
case SML_COMMAND_TYPE_DELETE:
if (!smlChangeAssemble(assm, cmd, error))
goto error_free_writer;
break;
case SML_COMMAND_TYPE_PUT:
case SML_COMMAND_TYPE_GET:
if (!smlAccessAssemble(assm, cmd, error))
goto error_free_writer;
break;
case SML_COMMAND_TYPE_MAP:
if (!smlMapAssemble(assm, cmd, error))
goto error_free_writer;
break;
case SML_COMMAND_TYPE_RESULTS:
if (!smlResultsAssemble(assm, cmd, error))
goto error_free_writer;
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown command type");
goto error_free_writer;
}
/* Now close the buffer */
if (xmlTextWriterEndDocument(assm->writer) < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
goto error_free_writer;
}
xmlFreeTextWriter(assm->writer);
assm->writer = NULL;
/* Now we can append the buffer and the parent*/
*appendto = g_list_append(*appendto, assmcmd);
return TRUE;
error_free_writer:
xmlFreeTextWriter(assm->writer);
assm->writer = NULL;
error_free_buffer:
xmlBufferFree(assmcmd->buffer);
error_free_cmd:
smlSafeFree((gpointer *)&assmcmd);
error:
return FALSE;
}
SmlBool smlXmlAssemblerEndCommand(SmlXmlAssembler *assm, unsigned int parentID, SmlError **error)
{
CHECK_ERROR_REF
smlAssert(assm);
/* Lets see if there already was a header before */
if (!assm->header_buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Header not yet added");
goto error;
}
SmlXmlAssemblerCommand *assmcmd = smlTryMalloc0(sizeof(SmlXmlAssemblerCommand), error);
if (!assmcmd)
goto error;
assmcmd->nodeType = SML_ASSEMBLER_NODE_CLOSE;
GList **appendto = &(assm->commands);
if (parentID) {
/* If we have a parent we first have to search for this command */
SmlXmlAssemblerCommand *excmd = NULL;
GList *b = NULL;
for (b = assm->commands; b; b = b->next) {
excmd = b->data;
if (excmd->cmdID == parentID)
appendto = &(excmd->children);
}
}
/* Now we can append command */
*appendto = g_list_append(*appendto, assmcmd);
return TRUE;
error:
return FALSE;
}
SmlBool smlXmlAssemblerRemCommand(SmlXmlAssembler *assm, unsigned int parentID, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, assm, parentID, error);
CHECK_ERROR_REF
smlAssert(assm);
GList **removefrom = &(assm->commands);
if (parentID) {
/* If we have a parent we first have to search for this command */
SmlXmlAssemblerCommand *excmd = NULL;
GList *b = NULL;
for (b = assm->commands; b; b = b->next) {
excmd = b->data;
if (excmd->cmdID == parentID)
removefrom = &(excmd->children);
}
}
if (!*removefrom) {
smlErrorSet(error, SML_ERROR_GENERIC, "Nothing to remove");
goto error;
}
GList *b = g_list_last(*removefrom);
SmlXmlAssemblerCommand *cmd = b->data;
*removefrom = g_list_delete_link(*removefrom, b);
if (cmd->nodeType != SML_ASSEMBLER_NODE_OPEN) {
smlErrorSet(error, SML_ERROR_GENERIC, "Trying to remove not a starting command");
goto error;
}
assm->moreDataSet = FALSE;
xmlBufferFree(cmd->buffer);
smlSafeFree((gpointer *)&cmd);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlAssemblerRemStatus(SmlXmlAssembler *assm, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, assm, error);
CHECK_ERROR_REF
smlAssert(assm);
if (!assm->statuses) {
smlErrorSet(error, SML_ERROR_GENERIC, "Trying to remove status but no status available");
goto error;
}
GList *b;
SmlXmlAssemblerStatus *last = NULL;
for (b = assm->statuses; b; b = b->next) {
SmlXmlAssemblerStatus *status = b->data;
if (!status->buffer)
break;
last = status;
}
if (last) {
xmlBufferFree(last->buffer);
last->buffer = NULL;
}
assm->added_statuses--;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlAssemblerReserveStatus(SmlXmlAssembler *assm, unsigned int cmdRef, unsigned int msgRef, unsigned int cmdID, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %i, %i, %p)", __func__, assm, cmdRef, msgRef, cmdID, error);
CHECK_ERROR_REF
smlAssert(assm);
/* We first start a new writer that will write our status into a buffer */
SmlXmlAssemblerStatus *res = smlTryMalloc0(sizeof(SmlXmlAssemblerStatus), error);
if (!res)
goto error;
res->cmdRef = cmdRef;
res->cmdID = cmdID;
res->msgRef = msgRef;
/* Now we can append the buffer */
if (cmdRef != 0)
assm->statuses = g_list_append(assm->statuses, res);
else
assm->statuses = g_list_prepend(assm->statuses, res);
assm->reserved_statuses++;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlAssemblerAddStatus(SmlXmlAssembler *assm, SmlStatus *status, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, status, error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(g_mutex_trylock(assm->mutex));
smlAssert(status);
SmlXmlAssemblerStatus *res = NULL;
smlTrace(TRACE_INTERNAL, "Adding status with cmdRef %i, msgRef %i, cmd %s", status->cmdRef, status->msgRef, smlCommandTypeToString(status->type, error));
/* this happens if the status is damaged */
if (*error)
goto error;
if (!status->msgRef) {
smlErrorSet(error, SML_ERROR_GENERIC, "No msgref set");
goto error;
}
if (status->type == SML_COMMAND_TYPE_UNKNOWN) {
smlErrorSet(error, SML_ERROR_GENERIC, "No cmd set");
goto error;
}
/* Lets see if there already was a header before */
if (!assm->header_buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Header not yet added");
goto error;
}
/* Get the reserved buffer */
GList *s = NULL;
for (s = assm->statuses; s; s = s->next) {
res = s->data;
if (res->cmdRef == status->cmdRef && res->msgRef == status->msgRef)
break;
res = NULL;
}
if (!res) {
smlErrorSet(error, SML_ERROR_GENERIC, "Status not reserved");
goto error;
}
if (!res->cmdID) {
smlErrorSet(error, SML_ERROR_GENERIC, "No cmd ID set");
goto error;
}
if (res->buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Status already added");
goto error;
}
/* We first start a new writer that will write our status into a buffer */
res->buffer = xmlBufferCreateSize(BUFFER_SIZE);
if (!res->buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
goto error;
}
assm->writer = xmlNewTextWriterMemory(res->buffer, 0);
if (!assm->writer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
goto error_free_buffer;
}
/* Begin of Status */
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_STATUS, error))
goto error;
/* CmdID */
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDID, res->cmdID, error))
goto error;
/* MsgRef */
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MSGREF, status->msgRef, error))
goto error;
/* CmdRef */
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDREF, res->cmdRef, error))
goto error;
/* Cmd */
const char *cmdname = smlCommandTypeToString(status->type, error);
if (!cmdname)
goto error;
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_CMD, cmdname, error))
goto error;
/* TargetRef */
if (status->targetRef) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_TARGETREF, status->targetRef->locURI, error))
goto error;
}
/* SourceRef */
if (status->sourceRef) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, status->sourceRef->locURI, error))
goto error;
}
/* Cred */
/* Chal */
if (status->type == SML_COMMAND_TYPE_HEADER && status->chal)
{
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CHAL, error))
goto error;
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
goto error;
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_FORMAT, SML_NAMESPACE_METINF, SML_BASE64, error))
goto error;
switch (status->chal->type) {
case SML_AUTH_TYPE_BASIC:
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_BASIC, error))
goto error;
break;
case SML_AUTH_TYPE_MD5:
if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_MD5, error))
goto error;
if (status->chal->nonce_b64 &&
!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_NEXTNONCE, SML_NAMESPACE_METINF, status->chal->nonce_b64, error))
goto error;
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown auth type");
goto error;
}
//META
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
//CHAL
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
}
/* Data */
if (status->data) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DATA, status->data, error))
goto error;
}
/* Item */
if (status->type == SML_COMMAND_TYPE_ALERT && status->anchor)
{
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_ITEM, error))
goto error;
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DATA, error))
goto error;
if (!smlAnchorAssemble(status->anchor, assm, error))
goto error;
//DATA
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
//ITEM
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
}
/* End of Status */
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
/* Now close the buffer and get the content */
if (xmlTextWriterEndDocument(assm->writer) < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
goto error_free_writer;
}
xmlFreeTextWriter(assm->writer);
assm->writer = NULL;
assm->added_statuses++;
g_mutex_unlock(assm->mutex);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_writer:
xmlFreeTextWriter(assm->writer);
assm->writer = NULL;
error_free_buffer:
xmlBufferFree(res->buffer);
res->buffer = NULL;
error:
g_mutex_unlock(assm->mutex);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlAssemblerMissingStatus(SmlXmlAssembler *assm)
{
smlAssert(assm);
if (assm->reserved_statuses - assm->added_statuses > 0)
return TRUE;
return FALSE;
}
static void flush_list(GList *list)
{
GList *l = NULL;
for (l = list; l; l = l->next) {
SmlXmlAssemblerCommand *cmd = l->data;
if (cmd->nodeType != SML_ASSEMBLER_NODE_CLOSE && cmd->children)
flush_list(cmd->children);
xmlBufferFree(cmd->buffer);
smlSafeFree((gpointer *)&cmd);
}
g_list_free(list);
}
void smlXmlAssemblerFree(SmlXmlAssembler *assm)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
smlAssert(assm);
if (assm->header_buffer) {
xmlBufferFree(assm->header_buffer);
assm->header_buffer = NULL;
}
while (assm->statuses) {
SmlXmlAssemblerStatus *status = assm->statuses->data;
if (status->buffer)
xmlBufferFree(status->buffer);
smlSafeFree((gpointer *)&status);
assm->statuses = g_list_delete_link(assm->statuses, assm->statuses);
}
flush_list(assm->commands);
flush_list(assm->last_commands);
g_mutex_free(assm->mutex);
smlSafeFree((gpointer *)&assm);
smlTrace(TRACE_EXIT, "%s", __func__);
}
/* This function returns the highest command id that is still in the
* assembler after flushing which is: number of statuses + 1 */
unsigned int smlXmlAssemblerFlush(SmlXmlAssembler *assm)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
smlAssert(assm);
unsigned int newid = 1;
SmlBool missing = FALSE;
/* Remove the statuses */
GList *s = NULL;
GList *s2 = g_list_copy(assm->statuses);
for (s = s2; s; s = s->next) {
SmlXmlAssemblerStatus *status = s->data;
/* Only remove statuses that were added already */
if (!status->buffer) {
/* If the status was not yet added, we have to reset the command id.
* We start with id #2 (since #1 will be the header reply) */
/* FIXME: why should we reset a command id? */
newid++;
// status->cmdID = newid;
missing = TRUE;
} else if (!missing) {
xmlBufferFree(status->buffer);
assm->statuses = g_list_remove(assm->statuses, status);
smlSafeFree((gpointer *)&status);
assm->reserved_statuses--;
assm->added_statuses--;
}
}
g_list_free(s2);
/* Remove all commands that are complete
* Perhaps more correct remove all commands from the last message.
* The actual commands are cached to support error 407 handling.
* (AUTHENTICATION_REQUIRED)
*/
if (assm->last_commands)
flush_list(assm->last_commands);
assm->last_commands = assm->commands;
assm->commands = NULL;
/* Reset LargeObject handling */
assm->moreDataSet = FALSE;
smlTrace(TRACE_EXIT, "%s: %i", __func__, newid);
return newid;
}
SmlBool smlXmlAssemblerStart(SmlXmlAssembler *assm, SmlSession *session, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, session, error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(g_mutex_trylock(assm->mutex));
smlAssert(session);
assm->session = session;
g_mutex_unlock(assm->mutex);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
SmlBool smlXmlAssemblerEnd(SmlXmlAssembler *assm, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, assm, error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(g_mutex_trylock(assm->mutex));
//Close syncml
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
if (_smlXmlAssemblerEndNode(assm, NULL)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Extra node open");
goto error;
}
g_mutex_unlock(assm->mutex);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
g_mutex_unlock(assm->mutex);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlAssemblerAddChildren(SmlXmlAssembler *assm, GList *b, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, b, error);
CHECK_ERROR_REF
smlAssert(assm);
const char *opt = smlAssemblerGetOption(assm->assembler, "ONLY_REPLACE");
SmlBool onlyReplace = (opt && atoi(opt)) ? TRUE : FALSE;
SmlXmlAssemblerCommand *cmd = NULL;
const char *cmdname = NULL;
for (; b; b = b->next) {
cmd = b->data;
switch (cmd->nodeType) {
case SML_ASSEMBLER_NODE_OPEN:
/* Add the corresponding command opener */
if (cmd->cmdType == SML_COMMAND_TYPE_ADD && onlyReplace)
cmdname = SML_ELEMENT_REPLACE;
else {
cmdname = smlCommandTypeToString(cmd->cmdType, error);
if (!cmdname)
goto error;
}
smlTrace(TRACE_INTERNAL, "opening node %s", cmdname);
if (!_smlXmlAssemblerStartNode(assm, cmdname, error))
goto error;
int rc = xmlTextWriterWriteRawLen(assm->writer, xmlBufferContent(cmd->buffer), xmlBufferLength(cmd->buffer));
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to write raw node data (%d).", rc);
goto error;
}
if (cmd->children) {
if (!smlXmlAssemblerAddChildren(assm, cmd->children, error))
goto error;
}
smlTrace(TRACE_INTERNAL, "%s: closing node", __func__);
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
break;
case SML_ASSEMBLER_NODE_CLOSE:
/*smlTrace(TRACE_INTERNAL, "%s: closing node". __func__);
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;*/
break;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlAssemblerRunFull(SmlXmlAssembler *assm, char **data, unsigned int *size, SmlBool *end, SmlBool final, SmlBool check, unsigned int maxsize, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %i, %i, %i, %p)", __func__, assm, data, size, end, final, check, maxsize, error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(g_mutex_trylock(assm->mutex));
smlAssert(data);
smlAssert(size);
smlAssert(assm->session);
unsigned int buffersize = 0;
/* Return an error if there is no header */
if (!assm->header_buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "No header available");
goto error;
}
/* Return an error if there is no status/command. Note that if
* statuses have been reserved, at least the first status
* must be added */
if (check && !assm->statuses && !assm->commands) {
smlErrorSet(error, SML_ERROR_GENERIC, "No status/command available");
goto error;
}
if (check && assm->statuses && final) {
SmlXmlAssemblerStatus *status = assm->statuses->data;
if (!status->buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing the first status with cmdRef %i", status->cmdRef);
goto error;
}
}
/* Create the final buffer and writer */
xmlBuffer *buffer = xmlBufferCreateSize(BUFFER_SIZE);
if (!buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
goto error;
}
assm->writer = xmlNewTextWriterMemory(buffer, 0);
if (!assm->writer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
goto error_free_buffer;
}
/* Sync4j has a bug and cannot handle the default XML encoding.
* The default XML encoding is UTF-8. So this default encoding
* is explicitly added to support buggy XML parsers.
*/
if (xmlTextWriterStartDocument(assm->writer, NULL, "UTF-8", NULL) < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
goto error_free_writer;
}
/* Add the syncml start node */
switch (assm->session->version) {
case SML_VERSION_10:
xmlTextWriterWriteRaw(assm->writer, (xmlChar *)"<!DOCTYPE SyncML PUBLIC \"-//SYNCML//DTD SyncML 1.0//EN\" \"http://www.syncml.org/docs/syncml_represent_v10_20001207.dtd\">");
if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_SYNCML, SML_NAMESPACE_SYNCML10, error))
goto error_free_writer;
break;
case SML_VERSION_11:
xmlTextWriterWriteRaw(assm->writer, (xmlChar *)"<!DOCTYPE SyncML PUBLIC \"-//SYNCML//DTD SyncML 1.1//EN\" \"http://www.syncml.org/docs/syncml_represent_v11_20020213.dtd\">");
if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_SYNCML, SML_NAMESPACE_SYNCML11, error))
goto error_free_writer;
break;
case SML_VERSION_12:
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\">");
if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_SYNCML, SML_NAMESPACE_SYNCML12, error))
goto error_free_writer;
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown version");
goto error_free_writer;
}
/* Add the header */
int rc = xmlTextWriterWriteRawLen(assm->writer, xmlBufferContent(assm->header_buffer), xmlBufferLength(assm->header_buffer));
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to write raw header data (%d).", rc);
goto error_free_writer;
}
/* Start the sync body */
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCBODY, error))
goto error_free_writer;
// FIXME: why do we need 20 extra bytes here?
// FIXME: buffersize is never read !!!
buffersize += 20 + xmlBufferLength(assm->header_buffer);
/* Add the statuses */
GList *b = NULL;
SmlBool missingstatus = FALSE;
smlTrace(TRACE_INTERNAL, "%s: Now adding %i statuses",
__func__, g_list_length(assm->statuses));
for (b = assm->statuses; b; b = b->next) {
SmlXmlAssemblerStatus *status = b->data;
if (!status->buffer || xmlBufferLength(status->buffer) == 0) {
if (status->cmdRef == 0 && check) {
smlErrorSet(error, SML_ERROR_GENERIC, "Reserved status 0 has not been added");
goto error_free_writer;
}
smlTrace(TRACE_INTERNAL, "%s: Reserved status %i is missing",
__func__, status->cmdRef);
missingstatus = TRUE;
break;
}
buffersize += xmlBufferLength(status->buffer);
//if (maxsize && buffersize > maxsize)
// break;
rc = xmlTextWriterWriteRawLen(assm->writer, xmlBufferContent(status->buffer), xmlBufferLength(status->buffer));
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to write raw status data (%d - %d of %d: %s).",
rc, xmlBufferLength(status->buffer),
buffersize, xmlBufferContent(status->buffer));
goto error_free_writer;
}
}
/* We cannot have missing statuses when its final. And we cannot add commands */
if (missingstatus && final && check) {
smlErrorSet(error, SML_ERROR_GENERIC, "Reserved status has not been added");
goto error_free_writer;
}
/* Add the commands. Parent commands are added in the order they were added. */
if (!smlXmlAssemblerAddChildren(assm, assm->commands, error))
goto error_free_writer;
if (final) {
smlTrace(TRACE_INTERNAL, "%s: setting final", __func__);
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_FINAL, "", error))
goto error_free_writer;
}
/* Close syncbody */
if (!_smlXmlAssemblerEndNode(assm, error))
goto error_free_writer;
/* Close syncml */
if (!_smlXmlAssemblerEndNode(assm, error))
goto error_free_writer;
if (xmlTextWriterEndDocument(assm->writer) < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
goto error;
}
xmlFreeTextWriter(assm->writer);
assm->writer = NULL;
// the buffer was created with xmlBufferCreateSize
// so we must free it via the libxml API and not via smlSafeCFree
*size = xmlBufferLength(buffer);
*data = g_strndup((const char *) xmlBufferContent(buffer), *size);
xmlBufferFree(buffer);
if (end) {
if (final && !assm->commands)
*end = TRUE;
else
*end = FALSE;
}
smlTrace(TRACE_INTERNAL, "%s: Message Assembled: %s",
__func__, VA_STRING(*data));
g_mutex_unlock(assm->mutex);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_writer:
xmlFreeTextWriter(assm->writer);
assm->writer = NULL;
error_free_buffer:
xmlBufferFree(buffer);
error:
g_mutex_unlock(assm->mutex);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static unsigned int calc_list(GList *list)
{
unsigned int size = 0;
for (; list; list = list->next) {
SmlXmlAssemblerCommand *cmd = list->data;
size += 15;
if (cmd->nodeType != SML_ASSEMBLER_NODE_CLOSE) {
size += xmlBufferLength(cmd->buffer);
if (cmd->children)
size += calc_list(cmd->children);
}
}
return size;
}
SmlBool smlXmlAssemblerRun(SmlXmlAssembler *assm, char **data, unsigned int *size, SmlBool *end, SmlBool final, unsigned int maxsize, SmlError **error)
{
CHECK_ERROR_REF
SmlBool ans = smlXmlAssemblerRunFull(assm, data, size, end, final, TRUE, maxsize, error);
if (ans)
smlLog("sent-%i.xml", *data, *size);
else
smlTrace(TRACE_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return ans;
}
unsigned int smlXmlAssemblerCheckSize(SmlXmlAssembler *assm, SmlBool headeronly, SmlError **error)
{
CHECK_ERROR_REF
smlAssert(assm);
SmlXmlAssemblerStatus *status = NULL;
unsigned int size = 0;
/* Add the size if the syncml tags etc */
size += 20;
/* Add the size of the header */
if (assm->header_buffer)
size += xmlBufferLength(assm->header_buffer);
if (!headeronly) {
/* Add the size of the status */
GList *b = NULL;
for (b = assm->statuses; b; b = b->next) {
status = b->data;
if (!status->buffer)
break;
size += xmlBufferLength(status->buffer);
}
/* Add the size of the commands */
size += calc_list(assm->commands);
}
return size;
}
SmlBool smlXmlAssemblerNextCmdRef(SmlXmlAssembler *assm, unsigned int *cmdRef, unsigned int *msgRef)
{
smlAssert(assm);
smlAssert(cmdRef);
smlAssert(msgRef);
SmlXmlAssemblerStatus *status = NULL;
GList *b = NULL;
for (b = assm->statuses; b; b = b->next) {
status = b->data;
if (!status->buffer) {
*cmdRef = status->cmdRef;
*msgRef = status->msgRef;
return TRUE;
}
}
return FALSE;
}
SmlXmlAssembler *smlXmlAssemblerNew(SmlAssembler *assembler, SmlAssemblerFunctions *functions, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assembler, functions, error);
CHECK_ERROR_REF
SmlXmlAssembler *assm = smlTryMalloc0(sizeof(SmlXmlAssembler), error);
if (!assm)
goto error;
assm->assembler = assembler;
assm->mutex = g_mutex_new();
if (!assm->mutex)
goto error_mutex;
functions->start = (SmlAssemblerStartFunction)smlXmlAssemblerStart;
functions->free = (SmlAssemblerFreeFunction)smlXmlAssemblerFree;
functions->run = (SmlAssemblerRunFunction)smlXmlAssemblerRun;
functions->end = (SmlAssemblerEndFunction)smlXmlAssemblerEnd;
functions->add_header = (SmlAssemblerHeaderFunction)smlXmlAssemblerAddHeader;
functions->start_cmd = (SmlAssemblerStartCommandFunction)smlXmlAssemblerStartCommand;
functions->end_cmd = (SmlAssemblerEndCommandFunction)smlXmlAssemblerEndCommand;
functions->rem_cmd = (SmlAssemblerRemCommandFunction)smlXmlAssemblerRemCommand;
functions->add_status = (SmlAssemblerStatusFunction)smlXmlAssemblerAddStatus;
functions->rem_status = (SmlAssemblerRemStatusFunction)smlXmlAssemblerRemStatus;
functions->reserve_status = (SmlAssemblerReserveStatusFunction)smlXmlAssemblerReserveStatus;
functions->missing_status = (SmlAssemblerStatusMissingFunction)smlXmlAssemblerMissingStatus;
functions->check_size = (SmlAssemblerCheckFunction)smlXmlAssemblerCheckSize;
functions->next_cmdref = (SmlAssemblerNextCmdRefFunction)smlXmlAssemblerNextCmdRef;
functions->flush = (SmlAssemblerFlushFunction)smlXmlAssemblerFlush;
functions->restore_cmds = (SmlAssemblerRestoreCommandsFunction)smlXmlAssemblerRestoreCommands;
smlTrace(TRACE_EXIT, "%s: %p", __func__, assm);
return assm;
error_mutex:
smlSafeFree((gpointer *)&assm);
smlErrorSet(error, SML_ERROR_GENERIC, "%s - Cannot create new mutex.", __func__);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
static SmlBool _smlXmlDevInfDataStoreAssembleRxTx(SmlXmlAssembler *assm, const char *element, const char *cttype, const char *version, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %s, %p)", __func__, assm, VA_STRING(element), VA_STRING(cttype), VA_STRING(version), error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(element);
smlAssert(cttype);
if (!_smlXmlAssemblerStartNode(assm, element, error))
goto error;
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_CTTYPE, cttype, error))
goto error;
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERCT, version, error))
goto error;
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlXmlDevInfDataStoreAssembleCTCap(
SmlXmlAssembler *assm,
const SmlDevInfCTCap *ctcap,
SmlBool flat,
SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, assm, ctcap, error);
CHECK_ERROR_REF
smlAssert(assm);
smlAssert(ctcap);
if (!flat)
{
// start CTCap
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CTCAP, error))
goto error;
}
// add CTType
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_CTTYPE, ctcap->ct->cttype, error))
goto error;
// add VerCT
// SyncML 1.0 and 1.1 does not support VerCT in CTCap
// because this info is in the VERSION property.
if (!flat)
{
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERCT, ctcap->ct->verct, error))
goto error;
}
// add properties
GList *hprop = NULL;
for (hprop = ctcap->properties; hprop; hprop = hprop->next)
{
SmlDevInfProperty *property = hprop->data;
if (!flat)
{
// add Property
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_PROPERTY, error))
goto error;
}
// add PropName
if (property->propName != NULL &&
!_smlXmlAssemblerAddString(assm, SML_ELEMENT_PROPNAME, property->propName, error))
goto error;
// add DataType
if (property->dataType != NULL &&
!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DATATYPE, property->dataType, error))
goto error;
// add MaxOccur
if (property->maxOccur > 0 &&
!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXOCCUR, property->maxOccur, error))
goto error;
// add MaxSize
if (property->maxSize > 0) {
/* There was a change of the element name in OMA DS DevInf 1.2. */
if (flat) {
if (!_smlXmlAssemblerAddID(
assm,
SML_ELEMENT_SIZE,
property->maxSize,
error))
goto error;
} else {
if (!_smlXmlAssemblerAddID(
assm,
SML_ELEMENT_MAXSIZE,
property->maxSize,
error))
goto error;
}
}
// add NoTruncate
if (property->noTruncate)
{
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_NOTRUNCATE, error))
goto error;
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
}
// add DisplayName
if (property->displayName != NULL &&
!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DISPLAYNAME, property->displayName, error))
goto error;
// add values
GList *hvalue = NULL;
for (hvalue = property->valEnums; hvalue; hvalue = hvalue->next)
{
char *valEnum = hvalue->data;
// add ValEnum
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VALENUM, valEnum, error))
goto error;
} // end values for loop
// add parameters
GList *hparam = NULL;
for (hparam = property->propParams; hparam; hparam = hparam->next)
{
SmlDevInfPropParam *param = hparam->data;
if (!flat)
{
// add PropParam
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_PROPPARAM, error))
goto error;
}
// add ParamName
if (param->paramName != NULL &&
!_smlXmlAssemblerAddString(assm, SML_ELEMENT_PARAMNAME, param->paramName, error))
goto error;
// add DataType
if (param->dataType != NULL &&
!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DATATYPE, param->dataType, error))
goto error;
// add DisplayName
if (param->displayName != NULL &&
!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DISPLAYNAME, param->displayName, error))
goto error;
// add values
hvalue = NULL;
for (hvalue = param->valEnums; hvalue; hvalue = hvalue->next)
{
char *valEnum = hvalue->data;
// add ValEnum
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VALENUM, valEnum, error))
goto error;
} // end values for loop
if (!flat)
{
// end PropParam
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
}
} // end params for loop
if (!flat)
{
// end Property
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
}
} //end of ctcap->properties for loop
if (!flat)
{
// end CTCap
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlXmlDevInfDataStoreAssemble(
SmlXmlAssembler *assm,
SmlDevInfDataStore *datastore,
SmlDevInf *devinf,
SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, assm, datastore, devinf, error);
CHECK_ERROR_REF
smlAssert(datastore);
smlAssert(assm);
GList *contentTypes = NULL;
if (!datastore->sourceref) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing sourceref");
goto error;
}
if (!datastore->rxPrefContentType) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing rx-pref");
goto error;
}
if (!datastore->txPrefContentType) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing tx-pref");
goto error;
}
if (!datastore->synccap) {
smlErrorSet(error, SML_ERROR_GENERIC, "No sync capabilities");
goto error;
}
// Datastore
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DATASTORE, error))
goto error;
//SourceRef
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, datastore->sourceref, error))
goto error;
//displayname
if (datastore->displayname) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DISPLAYNAME, datastore->displayname, error))
goto error;
}
//maxguidsize
if (datastore->maxGUIDSize) {
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXGUIDSIZE, datastore->maxGUIDSize, error))
goto error;
}
//rx-pref
if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_RXPREF, datastore->rxPrefContentType, datastore->rxPrefVersion, error))
goto error;
contentTypes = g_list_append(
contentTypes,
smlDevInfNewContentType(
datastore->rxPrefContentType,
datastore->rxPrefVersion, error));
//rx
if (datastore->rx) {
GList *hl;
for (hl = datastore->rx; hl; hl = hl->next)
{
SmlDevInfContentType *ct = hl->data;
if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_RX, ct->cttype, ct->verct, error))
goto error;
contentTypes = g_list_append(
contentTypes,
smlDevInfNewContentType(ct->cttype, ct->verct, error));
}
}
//tx-pref
if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_TXPREF, datastore->txPrefContentType, datastore->txPrefVersion, error))
goto error;
contentTypes = g_list_append(
contentTypes,
smlDevInfNewContentType(
datastore->txPrefContentType,
datastore->txPrefVersion, error));
//tx
if (datastore->tx) {
GList *hl;
for (hl = datastore->tx; hl; hl = hl->next)
{
SmlDevInfContentType *ct = hl->data;
if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_TX, ct->cttype, ct->verct, error))
goto error;
contentTypes = g_list_append(
contentTypes,
smlDevInfNewContentType(ct->cttype, ct->verct, error));
}
}
// CTCap (if SyncML version 1.2 device info)
if (devinf->version >= SML_DEVINF_VERSION_12)
{
GList *hct = NULL;
for (hct = contentTypes; hct; hct = hct->next) {
SmlDevInfContentType *ct = hct->data;
const SmlDevInfCTCap *ctcap = smlDevInfGetCTCap(devinf, ct);
if (ctcap != NULL)
{
// we found a matching CTCap
// so let's dump it
if (!_smlXmlDevInfDataStoreAssembleCTCap(assm, ctcap, FALSE, error))
goto error;
} else {
// we have a content type without CTCap
// this is a real source of trouble
// WARNING: should we fail on this issue?
smlTrace(TRACE_INTERNAL, "%s: found a content type (%s %d) without CTCap",
__func__, VA_STRING(ct->cttype), ct->verct);
}
} // end of contentTypes for loop
} // end of SML_DEVINF_VERSION_12
// cleanup list of content types
while(contentTypes)
{
SmlDevInfContentType *ct = contentTypes->data;
contentTypes = g_list_remove(contentTypes, ct);
smlDevInfFreeContentType(ct);
}
//Dsmem
if (datastore->maxmem || datastore->maxid) {
//Dsmem
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DSMEM, error))
goto error;
//shared
if (datastore->sharedMem) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SHAREDMEM, "", error))
goto error;
}
//maxid
if (datastore->maxid) {
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXID, datastore->maxid, error))
goto error;
}
//maxmem
if (datastore->maxmem) {
if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXMEM, datastore->maxmem, error))
goto error;
}
//DsMem
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
}
//SyncCap
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCCAP, error))
goto error;
//SyncTypes
if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY)) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "1", error))
goto error;
}
if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC)) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "2", error))
goto error;
}
if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_CLIENT)) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "3", error))
goto error;
}
if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_REFRESH_FROM_CLIENT)) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "4", error))
goto error;
}
if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_SERVER)) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "5", error))
goto error;
}
if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_REFRESH_FROM_SERVER)) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "6", error))
goto error;
}
if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC)) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "7", error))
goto error;
}
//SyncCap
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
//DataStore
if (!_smlXmlAssemblerEndNode(assm, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlDevInfAssemble(SmlDevInf *devinf, SmlDevInfVersion version, char **data, unsigned int *size, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p)", __func__, devinf, version, data, size, error);
CHECK_ERROR_REF
smlAssert(devinf);
smlAssert(data);
smlAssert(size);
// sometime devinf->version is empty
// both version fields should be identical
smlTrace(TRACE_INTERNAL, "%s: devinf version: %i, version: %i", __func__, devinf->version, version);
if (devinf->version == SML_DEVINF_VERSION_UNKNOWN)
devinf->version = version;
if (version == SML_DEVINF_VERSION_UNKNOWN)
version = devinf->version;
smlAssert(devinf->version == version);
SmlXmlAssembler *assm = smlTryMalloc0(sizeof(SmlXmlAssembler), error);
if (!assm)
goto error;
/* We first start a new parent writer that will write our command into a buffer */
xmlBuffer *buffer = xmlBufferCreateSize(BUFFER_SIZE);
if (!buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
goto error_free_assm;
}
assm->writer = xmlNewTextWriterMemory(buffer, 0);
if (!assm->writer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
goto error_free_buffer;
}
//Devinf
if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_DEVINF, SML_NAMESPACE_DEVINF, error))
goto error_free_writer;
//Verdtd
switch (version) {
case SML_DEVINF_VERSION_12:
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.2", error))
goto error_free_writer;
break;
case SML_DEVINF_VERSION_11:
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.1", error))
goto error_free_writer;
break;
case SML_DEVINF_VERSION_10:
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.0", error))
goto error_free_writer;
break;
case SML_DEVINF_VERSION_UNKNOWN:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown devinf version");
goto error_free_writer;
break;
}
//Man
if (devinf->manufacturer) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MAN, devinf->manufacturer, error))
goto error_free_writer;
}
//Mod
if (devinf->model) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MOD, devinf->model, error))
goto error_free_writer;
}
//OEM
if (devinf->oem) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_OEM, devinf->oem, error))
goto error_free_writer;
}
//FwV
if (devinf->firmwareVersion) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_FWV, devinf->firmwareVersion, error))
goto error_free_writer;
}
//SwV
if (devinf->softwareVersion) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SWV, devinf->softwareVersion, error))
goto error_free_writer;
}
//HwV
if (devinf->hardwareVersion) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_HWV, devinf->hardwareVersion, error))
goto error_free_writer;
}
//DevID
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DEVID, devinf->devid, error))
goto error_free_writer;
//Devtyp
const char *devtype = smlDevInfDevTypeToString(devinf->devtyp, error);
if (!devtype)
goto error_free_writer;
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DEVTYPE, devtype, error))
goto error_free_writer;
//UTC (>= 1.1)
if (devinf->supportsUTC && version != SML_DEVINF_VERSION_10) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_UTC, "", error))
goto error_free_writer;
}
//Large objs (>= 1.1)
if (devinf->supportsLargeObjs && version != SML_DEVINF_VERSION_10) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SUPPORTLARGEOBJS, "", error))
goto error_free_writer;
}
//Number of changes (>= 1.1)
if (devinf->supportsNumberOfChanges && version != SML_DEVINF_VERSION_10) {
if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SUPPORTNUMBEROFCHANGES, "", error))
goto error_free_writer;
}
//Add the datastores
GList *d = NULL;
for (d = devinf->datastores; d; d = d->next) {
SmlDevInfDataStore *datastore = d->data;
if (!_smlXmlDevInfDataStoreAssemble(assm, datastore, devinf, error))
goto error_free_writer;
}
// CTCap (only for SyncML 1.0 and 1.1)
if (
devinf->version < SML_DEVINF_VERSION_12 &&
devinf->ctcaps) {
if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CTCAP, error))
goto error_free_writer;
for (d = devinf->ctcaps; d; d = d->next) {
SmlDevInfCTCap *ctcap = d->data;
if (!_smlXmlDevInfDataStoreAssembleCTCap(assm, ctcap, TRUE, error))
goto error_free_writer;
}
if (!_smlXmlAssemblerEndNode(assm, error))
goto error_free_writer;
}
//Devinf
if (!_smlXmlAssemblerEndNode(assm, error))
goto error_free_writer;
/* Now close the buffer */
if (xmlTextWriterEndDocument(assm->writer) < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
goto error_free_writer;
}
xmlFreeTextWriter(assm->writer);
assm->writer = NULL;
*size = xmlBufferLength(buffer);
*data = g_strndup((const char *) xmlBufferContent(buffer), *size);
xmlBufferFree(buffer);
smlSafeFree((gpointer *)&assm);
smlTrace(TRACE_INTERNAL, "Message Assembled: %s", *data);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_writer:
xmlFreeTextWriter(assm->writer);
assm->writer = NULL;
error_free_buffer:
xmlBufferFree(buffer);
error_free_assm:
smlSafeFree((gpointer *)&assm);
error:
g_warning("%s: %s", __func__, smlErrorPrint(error));
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
void smlXmlAssemblerRestoreCommands(SmlXmlAssembler *assm)
{
smlTrace(TRACE_ENTRY, "%s", __func__);
smlAssert(assm->commands == NULL);
assm->commands = assm->last_commands;
assm->last_commands = NULL;
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlXmlAssemblerStatus
Definition: sml_xml_assm_internals.h:57
SmlCred
Definition: sml_elements_internals.h:87
smlErrorPrint
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
smlAssemblerGetOption
const char * smlAssemblerGetOption(SmlAssembler *assm, const char *optionname)
Gets a option from the assembler.
Definition: sml_parse.c:914
SmlXmlAssemblerCommand::nodeType
SmlXmlAssemblerNodeType nodeType
Definition: sml_xml_assm_internals.h:51
SmlAssembler
Definition: sml_parse_internals.h:33
SmlDevInfCTCap
Definition: sml_devinf_internals.h:102
SmlAnchor
Definition: sml_elements_internals.h:35
SmlStatus
Definition: sml_command_internals.h:94
SmlAssemblerFunctions
Definition: sml_parse.h:67
SmlXmlAssemblerStatus::buffer
xmlBuffer * buffer
Definition: sml_xml_assm_internals.h:63
SmlItem::raw
SmlBool raw
Definition: sml_elements_internals.h:64
SmlItem
Definition: sml_elements_internals.h:46
SmlXmlAssemblerCommand::cmdType
SmlCommandType cmdType
Definition: sml_xml_assm_internals.h:47
SmlXmlAssembler
Definition: sml_xml_assm_internals.h:68
SmlMapItem
Definition: sml_elements_internals.h:67
SmlSession
Definition: sml_session_internals.h:27
SmlCommand
Definition: sml_command_internals.h:73
SmlDevInfContentType
Definition: sml_devinf_internals.h:107
smlItemGetData
SmlBool smlItemGetData(SmlItem *item, char **data, unsigned int *size, SmlError **error)
Definition: sml_elements.c:495
smlXmlAssemblerNew
SmlXmlAssembler * smlXmlAssemblerNew(SmlAssembler *assembler, SmlAssemblerFunctions *functions, SmlError **error)
Creates a new XML assembler.
Definition: sml_xml_assm.c:1978
SmlXmlAssemblerStatus::cmdRef
unsigned int cmdRef
Definition: sml_xml_assm_internals.h:59
smlTrace
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
SmlDevInf
Definition: sml_devinf_internals.h:33
SmlDevInfDataStore
Definition: sml_devinf_internals.h:55
SmlLocation
Definition: sml_elements_internals.h:40
SmlXmlAssemblerCommand::cmdID
unsigned int cmdID
Definition: sml_xml_assm_internals.h:53
SmlXmlAssemblerCommand::buffer
xmlBuffer * buffer
Definition: sml_xml_assm_internals.h:49
smlTryMalloc0
void * smlTryMalloc0(long n_bytes, SmlError **error)
Safely mallocs.
Definition: sml_support.c:335
SmlDevInfProperty
Definition: sml_devinf_internals.h:90
smlErrorSet
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
SmlXmlAssemblerCommand
Definition: sml_xml_assm_internals.h:45
SmlError
Represent an error.
Definition: sml_error_internals.h:29
SmlDevInfPropParam
Definition: sml_devinf_internals.h:83
SmlXmlAssemblerStatus::cmdID
unsigned int cmdID
Definition: sml_xml_assm_internals.h:65