sml_xml_parse.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_parse.h"
#include "sml_xml_parse_internals.h"
#define BUFFER_SIZE 500
static SmlBool _smlXmlParserStep(SmlXmlParser *parser)
{
SmlBool ret = FALSE;
do {
ret = (xmlTextReaderRead(parser->reader) == 1) ? TRUE : FALSE;
} while (ret && (xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_DOCUMENT_TYPE || \
xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_WHITESPACE || \
xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_SIGNIFICANT_WHITESPACE));
return ret;
}
static SmlBool _smlXmlParserStepData(SmlXmlParser *parser)
{
SmlBool ret = FALSE;
do {
ret = (xmlTextReaderRead(parser->reader) == 1) ? TRUE : FALSE;
} while (ret && (xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_DOCUMENT_TYPE || \
xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_WHITESPACE));
return ret;
}
static SmlBool _smlXmlSkipNode(SmlXmlParser *parser)
{
int node_type;
if (xmlTextReaderNext(parser->reader) != 1)
return FALSE;
node_type = xmlTextReaderNodeType(parser->reader);
while (xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_DOCUMENT_TYPE ||
xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_WHITESPACE ||
xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_SIGNIFICANT_WHITESPACE)
{
if (!_smlXmlParserStep (parser))
return FALSE;
}
return TRUE;
}
static SmlBool _smlXmlParserExpectNode(SmlXmlParser *parser, int type, SmlBool empty, const char *name, SmlError **error)
{
CHECK_ERROR_REF
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "No node at all");
return FALSE;
}
if (xmlTextReaderNodeType(parser->reader) != type) {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong node type");
return FALSE;
}
switch (type) {
case XML_NODE_START:
case XML_NODE_CLOSE:
if (name) {
if (!xmlTextReaderConstName(parser->reader)) {
smlErrorSet(error, SML_ERROR_GENERIC, "no name");
return FALSE;
}
if (strcmp((char *)xmlTextReaderConstName(parser->reader), name)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Wrong name");
return FALSE;
}
}
break;
case XML_NODE_TEXT:
if (!empty && !xmlTextReaderConstName(parser->reader)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Empty.");
return FALSE;
}
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown node type");
return FALSE;
}
return TRUE;
}
static SmlBool _smlXmlParserGetID(SmlXmlParser *parser, unsigned int *id, const char *name, SmlError **error)
{
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(id);
if (*id) {
smlErrorSet(error, SML_ERROR_GENERIC, "Id already set");
goto error;
}
if (!_smlXmlParserExpectNode(parser, XML_NODE_TEXT, FALSE, NULL, error))
goto error;
*id = atoi(g_strstrip((char *)xmlTextReaderConstValue(parser->reader)));
if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, name, error))
goto error;
return TRUE;
error:
return FALSE;
}
static SmlBool _smlXmlParserGetString(SmlXmlParser *parser, char **string, const char *name, SmlError **error)
{
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(string);
if (*string) {
smlErrorSet(error, SML_ERROR_GENERIC, "string already set");
goto error;
}
/* required for empty elements like <FwV/> */
if (xmlTextReaderIsEmptyElement(parser->reader)) {
*string = g_strdup("");
return TRUE;
}
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "No node at all");
goto error_clear;
}
if (xmlTextReaderNodeType(parser->reader) == XML_NODE_TEXT) {
*string = g_strstrip(g_strdup((char *)xmlTextReaderConstValue(parser->reader)));
if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, name, error))
goto error_clear;
} else if (xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
*string = g_strdup("");
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong node type");
goto error_clear;
}
return TRUE;
error_clear:
*string = NULL;
error:
return FALSE;
}
static SmlBool _smlXmlParserGetData(SmlXmlParser *parser, char **string, unsigned int *size, const char *name, SmlError **error)
{
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(string);
int rc = 0;
if (*string) {
smlErrorSet(error, SML_ERROR_GENERIC, "string already set");
return FALSE;
}
xmlBuffer *buffer = xmlBufferCreateSize(BUFFER_SIZE);
if (!buffer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
goto error;
}
xmlTextWriter *writer = xmlNewTextWriterMemory(buffer, 0);
if (!writer) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
goto error_free_buffer;
}
/* SyncML 1.2 allows empty data elements <Data /> */
if (xmlTextReaderIsEmptyElement(parser->reader))
goto out;
while (1) {
if (!_smlXmlParserStepData(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "There is a missing node during the data parsing of %s.", name);
goto error_free_writer;
}
switch (xmlTextReaderNodeType(parser->reader)) {
case XML_NODE_CLOSE:
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), name))
goto out;
if (!xmlTextReaderIsEmptyElement(parser->reader))
rc = xmlTextWriterEndElement(writer);
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end node");
goto error_free_writer;
}
break;
case XML_NODE_START:
if (!xmlTextReaderIsEmptyElement(parser->reader))
rc = xmlTextWriterStartElementNS(writer, NULL, xmlTextReaderConstName(parser->reader), NULL);
else
rc = xmlTextWriterWriteElementNS(writer, NULL, xmlTextReaderConstName(parser->reader), NULL, (const xmlChar*)"");
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to start node");
goto error_free_writer;
}
break;
case XML_NODE_CDATA:
case XML_NODE_TEXT:
rc = xmlTextWriterWriteRaw(writer, xmlTextReaderConstValue(parser->reader));
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add string");
goto error_free_writer;
}
break;
case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
rc = xmlTextWriterWriteRaw(writer, xmlTextReaderConstValue(parser->reader));
if (rc < 0) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add string");
goto error_free_writer;
}
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown node type");
goto error_free_writer;
}
}
out:
// do not call xmlTextWriterEndDocument here
// the writer was not started with xmlTextWriterStartDocument
// the function adds always a newline
// if you load only a CDATA field then a newline is additional data
// additional data invalidates size information of the remote peer
// additional data crashs by this way all length checks if smlItemCheck !!!
xmlFreeTextWriter(writer);
*string = g_strndup((const char *) xmlBufferContent(buffer), xmlBufferLength(buffer));
if (size)
*size = xmlBufferLength(buffer);
xmlBufferFree(buffer);
return TRUE;
error_free_writer:
xmlFreeTextWriter(writer);
error_free_buffer:
xmlBufferFree(buffer);
error:
*string = NULL;
if (size) *size = 0;
return FALSE;
}
static SmlBool _smlSyncHeaderParseDTD(SmlProtocolVersion *version, SmlXmlParser *parser, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, version, parser, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(version);
if (*version) {
smlErrorSet(error, SML_ERROR_GENERIC, "dtd already set");
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
char *dtd = NULL;
if (!_smlXmlParserGetString(parser, &dtd, SML_ELEMENT_VERDTD, error))
goto error;
if (!strcmp(dtd, "1.0"))
*version = SML_VERSION_10;
else if (!strcmp(dtd, "1.1"))
*version = SML_VERSION_11;
else if (!strcmp(dtd, "1.2"))
*version = SML_VERSION_12;
else {
smlSafeCFree(&dtd);
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown VerDTD");
goto error;
}
smlSafeCFree(&dtd);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
*version = SML_VERSION_UNKNOWN;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlSyncHeaderParseProto(SmlProtocolType *type, SmlXmlParser *parser, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, type, parser, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(type);
if (*type) {
smlErrorSet(error, SML_ERROR_GENERIC, "protocol already set");
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
char *proto = NULL;
if (!_smlXmlParserGetString(parser, &proto, SML_ELEMENT_VERPROTO, error))
goto error;
if (!strcmp(proto, SML_VERSION_STRING_10) || !strcmp(proto, SML_VERSION_STRING_11) || !strcmp(proto, SML_VERSION_STRING_12))
*type = SML_PROTOCOL_SYNCML;
else {
smlSafeCFree(&proto);
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown VerProto");
goto error;
}
smlSafeCFree(&proto);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
*type = SML_PROTOCOL_UNKNOWN;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlLocationParse(SmlLocation **location, SmlXmlParser *parser, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, location, parser, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(location);
if (*location) {
smlErrorSet(error, SML_ERROR_GENERIC, "Location already set");
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
*location = smlTryMalloc0(sizeof(SmlLocation), error);
if (!location)
goto error;
(*location)->refCount = 1;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_location;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE_PARENT) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET_PARENT) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in locations (Target, Source, SourceParent or TargetParent).",
xmlTextReaderConstName(parser->reader));
goto error_free_location;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_LOCURI)) {
if (!_smlXmlParserGetString(parser, &((*location)->locURI), SML_ELEMENT_LOCURI, error))
goto error_free_location;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_LOCNAME)) {
if (!_smlXmlParserGetString(parser, &((*location)->locName), SML_ELEMENT_LOCNAME, error))
goto error_free_location;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node. expected SyncHdr");
goto error_free_location;
}
}
if (!(*location)->locURI) {
smlErrorSet(error, SML_ERROR_GENERIC, "No locURI set");
goto error_free_location;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_location:
smlLocationUnref(*location);
error:
*location = NULL;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlAnchorParse(SmlAnchor **anchor, SmlXmlParser *parser, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, anchor, parser, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(anchor);
if (*anchor) {
smlErrorSet(error, SML_ERROR_GENERIC, "anchor already set");
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
*anchor = smlTryMalloc0(sizeof(SmlAnchor), error);
if (!anchor)
goto error;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_anchor;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ANCHOR) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Anchor.",
xmlTextReaderConstName(parser->reader));
goto error_free_anchor;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_NEXT)) {
if (!_smlXmlParserGetString(parser, &((*anchor)->next), SML_ELEMENT_NEXT, error))
goto error_free_anchor;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_LAST)) {
if (!_smlXmlParserGetString(parser, &((*anchor)->last), SML_ELEMENT_LAST, error))
goto error_free_anchor;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
goto error_free_anchor;
}
}
if (!(*anchor)->next) {
smlErrorSet(error, SML_ERROR_GENERIC, "No next set");
goto error_free_anchor;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_anchor:
smlAnchorFree(*anchor);
error:
*anchor = NULL;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlChalMetaParse(SmlXmlParser *parser, char **format, char **type, char **nonce, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p, %p, %p)", __func__, parser, format, type, nonce, error);
CHECK_ERROR_REF
smlAssert(parser);
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Chal/Meta.",
xmlTextReaderConstName(parser->reader));
goto error;
}
if (type && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TYPE)) {
if (!_smlXmlParserGetString(parser, type, SML_ELEMENT_TYPE, error))
goto error;
} else if (format && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FORMAT)) {
if (!_smlXmlParserGetString(parser, format, SML_ELEMENT_FORMAT, error))
goto error;
} else if (format && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_NEXTNONCE)) {
if (!_smlXmlParserGetString(parser, nonce, SML_ELEMENT_NEXTNONCE, error))
goto error;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node: %s", xmlTextReaderConstName(parser->reader));
goto error;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
if (format)
*format = NULL;
if (nonce)
*nonce = NULL;
if (type)
*type = NULL;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlCommandMetaParse(SmlXmlParser *parser, char **format, char **type, SmlAnchor **anchor, unsigned int *size, int *maxobjsize, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p, %p, %p)", __func__, parser, format, type, anchor, size, maxobjsize, error);
CHECK_ERROR_REF
smlAssert(parser);
if (maxobjsize)
*maxobjsize = -1;
if (xmlTextReaderIsEmptyElement(parser->reader))
{
/* empty meta element <meta/> */
smlTrace(TRACE_EXIT, "%s - empty meta element", __func__);
return TRUE;
}
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MEM)) {
/* Ignored for now */
smlTrace(TRACE_INTERNAL, "%s: Skipping mem node");
if (!_smlXmlSkipNode(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to skip mem node");
goto error;
}
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Command/Meta.",
xmlTextReaderConstName(parser->reader));
goto error;
}
if (type && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TYPE)) {
if (!_smlXmlParserGetString(parser, type, SML_ELEMENT_TYPE, error))
goto error;
} else if (anchor && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ANCHOR)) {
if (!_smlAnchorParse(anchor, parser, error))
goto error;
} else if (format && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FORMAT)) {
if (!_smlXmlParserGetString(parser, format, SML_ELEMENT_FORMAT, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MARK)) {
/* Ignore the mark for now */
char *mark = NULL;
if (!_smlXmlParserGetString(parser, &mark, SML_ELEMENT_MARK, error))
goto error;
smlSafeCFree(&mark);
} else if (size && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SIZE)) {
if (!_smlXmlParserGetID(parser, size, SML_ELEMENT_SIZE, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXOBJSIZE)) {
/* MaxObjSize must be parsed even if there is no maxobjsize pointer */
unsigned int loc_maxobjsize = 0;
if (!_smlXmlParserGetID(parser, &loc_maxobjsize, SML_ELEMENT_MAXOBJSIZE, error))
goto error;
if (maxobjsize)
*maxobjsize = loc_maxobjsize;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERSION)) {
/* Ignore the version for now */
char *version = NULL;
if (!_smlXmlParserGetString(parser, &version, SML_ELEMENT_VERSION, error))
goto error;
smlSafeCFree(&version);
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node for meta information: %s", xmlTextReaderConstName(parser->reader));
goto error;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
if (format)
*format = NULL;
if (anchor)
*anchor = NULL;
if (type)
*type = NULL;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlItem *_smlItemParse(SmlXmlParser *parser, SmlCommand *cmd, SmlCommandType type, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p)", __func__, parser, cmd, type, error);
CHECK_ERROR_REF
smlAssert(parser);
if (parser->gotMoreData) {
smlErrorSet(error, SML_ERROR_GENERIC, "Last item already had more data set");
goto error;
}
SmlItem *item = smlItemNew(0, error);
if (!item)
goto error;
/* If the item is an empty element then return immediately. */
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM) &&
xmlTextReaderIsEmptyElement(parser->reader)) {
smlTrace(TRACE_EXIT, "%s - empty item", __func__);
return item;
}
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "The first element inside of an Item structure cannot be parsed.");
goto error_free_item;
}
while (1) {
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Item.",
xmlTextReaderConstName(parser->reader));
goto error_free_item;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
SmlLocation *source = NULL;
if (!_smlLocationParse(&source, parser, error))
goto error_free_item;
smlItemSetSource(item, source);
smlLocationUnref(source);
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
SmlLocation *target = NULL;
if (!_smlLocationParse(&target, parser, error))
goto error_free_item;
smlItemSetTarget(item, target);
smlLocationUnref(target);
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE_PARENT)) {
SmlLocation *source = NULL;
if (!_smlLocationParse(&source, parser, error))
goto error_free_item;
smlItemSetSourceParent(item, source);
smlLocationUnref(source);
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET_PARENT)) {
SmlLocation *target = NULL;
if (!_smlLocationParse(&target, parser, error))
goto error_free_item;
smlItemSetTargetParent(item, target);
smlLocationUnref(target);
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
switch (type) {
case SML_COMMAND_TYPE_ALERT:
if (!cmd) {
/* This is an item inside a status.
* We must parse the meta data
* but we ignore the content completely.
* FIXME: Can we ignore the content?
*/
if (!_smlCommandMetaParse(parser, NULL, NULL, NULL, NULL, NULL, error))
goto error_free_item;
} else {
/* Some phones send the maxobjsize in the alert */
if (!_smlCommandMetaParse(parser, NULL, &(cmd->private.alert.contentType), &cmd->private.alert.anchor, NULL, &(cmd->private.alert.maxObjSize), error))
goto error_free_item;
}
break;
case SML_COMMAND_TYPE_ADD:
case SML_COMMAND_TYPE_DELETE:
case SML_COMMAND_TYPE_REPLACE:
if (!_smlCommandMetaParse(parser, NULL, &(item->contenttype), NULL, &cmd->size, NULL, error))
goto error_free_item;
break;
case SML_COMMAND_TYPE_RESULTS:
case SML_COMMAND_TYPE_PUT:;
/* Some phones send the type information for the devinf put
* not in the put itself but in the item */
if (!_smlCommandMetaParse(parser, NULL, &(item->contenttype), NULL, NULL, NULL, error))
goto error_free_item;
if (!item->contenttype)
goto error_free_item;
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown command type");
goto error_free_item;
}
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA)) {
/* Do nothing if the Data element is empty.
* The first emptyness test only covers <Data/>.
*
* Example: Funambol with WBXML on scheduleworld.com
*/
if (!xmlTextReaderIsEmptyElement(parser->reader)) {
switch (type) {
case SML_COMMAND_TYPE_ALERT:
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "The next element after the starting Data element in an Item is missing.");
goto error_free_item;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA) &&
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
/* Do nothing if the Data element is empty.
* This only covers <Data></Data>.
*
* Example: Funambol with XML on scheduleworld.com
*/
smlTrace(TRACE_INTERNAL, "%s: detected empty data element", __func__);
break;
}
SmlAnchor *anchor = NULL;
if (xmlTextReaderNodeType(parser->reader) == XML_NODE_TEXT) {
/* Google just copies the element Next from the Anchor
* Please see ticket #230 for more details.
*/
anchor = smlTryMalloc0(sizeof(SmlAnchor), error);
if (!anchor)
goto error_free_item;
anchor->next = g_strdup((char *)xmlTextReaderConstValue(parser->reader));
} else {
/* normal behaviour with anchor copy */
if (!_smlAnchorParse(&anchor, parser, error))
goto error_free_item;
}
item->anchor = anchor;
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "The closing Data element in an Item is missing.");
goto error_free_item;
}
break;
default:;
char *data = NULL;
unsigned int size = 0;
if (!_smlXmlParserGetData(parser, &data, &size, SML_ELEMENT_DATA, error))
goto error_free_item;
if (!smlItemAddData(item, data, size, error)) {
smlSafeCFree(&data);
goto error_free_item;
}
smlSafeCFree(&data);
}
}
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MOREDATA)) {
if (parser->version == SML_VERSION_10) {
smlErrorSet(error, SML_ERROR_GENERIC, "SyncML 1.0 does not allow MoreData");
goto error_free_item;
}
item->moreData = TRUE;
parser->gotMoreData = TRUE;
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes2");
goto error_free_item;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MOREDATA) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes3");
goto error_free_item;
}
}
continue;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "The element Item does not support the child element %s.", xmlTextReaderConstName(parser->reader));
goto error_free_item;
}
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "The next element in an Item structure is missing.");
goto error_free_item;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return item;
error_free_item:
smlItemUnref(item);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
static SmlCred *_smlCredParse(SmlXmlParser *parser, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, parser, error);
CHECK_ERROR_REF
smlAssert(parser);
SmlCred *cred = NULL;
char *format = NULL;
char *type = NULL;
char *data = NULL;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CRED) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Cred.",
xmlTextReaderConstName(parser->reader));
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
if (!_smlCommandMetaParse(parser, &format, &type, NULL, NULL, NULL, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA)) {
if (!_smlXmlParserGetString(parser, &data, SML_ELEMENT_DATA, error))
goto error;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node %s", xmlTextReaderConstName(parser->reader));
goto error;
}
}
cred = smlCredNewFromString(type, format, data, error);
error:
if (data)
smlSafeCFree(&data);
if (format)
smlSafeCFree(&format);
if (type)
smlSafeCFree(&type);
if (!smlErrorIsSet(error))
{
smlTrace(TRACE_EXIT, "%s: %p", __func__, cred);
return cred;
} else {
if (cred)
smlCredUnref(cred);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
}
static SmlChal *_smlChalParse(SmlXmlParser *parser, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, parser, error);
CHECK_ERROR_REF
smlAssert(parser);
char *format = NULL;
char *type = NULL;
char *nonce = NULL;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CHAL) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Chal.",
xmlTextReaderConstName(parser->reader));
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
if (!_smlChalMetaParse(parser, &format, &type, &nonce, error))
goto error;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node %s", xmlTextReaderConstName(parser->reader));
goto error;
}
}
if (format && strcmp(format, SML_BASE64)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown format");
goto error;
}
SmlAuthType auth;
if (!type || !strcmp(type, SML_AUTH_BASIC)) {
auth = SML_AUTH_TYPE_BASIC;
} else if (!strcmp(type, SML_AUTH_MD5)) {
auth = SML_AUTH_TYPE_MD5;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown type");
goto error;
}
if (format)
smlSafeCFree(&format);
if (type)
smlSafeCFree(&type);
SmlChal *chal = NULL;
if (nonce || auth == SML_AUTH_TYPE_MD5)
chal = smlChalNewFromBase64(auth, nonce, error);
else
chal = smlChalNew(auth, error);
if (!chal)
goto error;
if (nonce)
smlSafeCFree(&nonce);
smlTrace(TRACE_EXIT, "%s: %p", __func__, chal);
return chal;
error:
if (format)
smlSafeCFree(&format);
if (type)
smlSafeCFree(&type);
if (nonce)
smlSafeCFree(&nonce);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
static SmlBool _smlChangeParse(SmlXmlParser *parser, SmlCommand **cmd, SmlCommandType type, const char *name, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %p)", __func__, parser, type, VA_STRING(name), error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(name);
char *contenttype = NULL;
*cmd = smlCommandNew(type, error);
if (!*cmd)
goto error;
(*cmd)->refCount = 1;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), name) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in %s.",
xmlTextReaderConstName(parser->reader), name);
goto error_free_cmd;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
SmlItem *item = _smlItemParse(parser, *cmd, type, error);
if (!item)
goto error_free_cmd;
(*cmd)->private.change.items = g_list_append((*cmd)->private.change.items, item);
if (!(*cmd)->private.change.items)
{
smlItemUnref(item);
smlErrorSet(error, SML_ERROR_GENERIC, "g_list_append for item list of change command failed.");
goto error_free_cmd;
}
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
if (!_smlCommandMetaParse(parser, NULL, &contenttype, NULL, &(*cmd)->size, NULL, error))
goto error_free_cmd;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
goto error_free_cmd;
}
}
if (!(*cmd)->cmdID) {
smlErrorSet(error, SML_ERROR_GENERIC, "No cmdid set");
goto error_free_cmd;
}
if (!(*cmd)->private.change.items ||
!g_list_length((*cmd)->private.change.items)) {
smlErrorSet(error, SML_ERROR_GENERIC, "No items set");
goto error_free_cmd;
}
/* some clients set the contentype in the item, don't overwrite it with NULL */
guint i;
for (i=0; i < g_list_length((*cmd)->private.change.items); i++)
{
SmlItem *item = g_list_nth_data((*cmd)->private.change.items, i);
if (item->contenttype == NULL && contenttype) {
item->contenttype = g_strdup(contenttype);
}
}
if (contenttype)
smlSafeCFree(&contenttype);
for (i=0; i < g_list_length((*cmd)->private.change.items); i++)
{
SmlItem *item = g_list_nth_data((*cmd)->private.change.items, i);
if ((*cmd)->size && !item->size)
item->size = (*cmd)->size;
}
switch (type) {
case SML_COMMAND_TYPE_ADD:
(*cmd)->private.change.type = SML_CHANGE_ADD;
break;
case SML_COMMAND_TYPE_REPLACE:
(*cmd)->private.change.type = SML_CHANGE_REPLACE;
break;
case SML_COMMAND_TYPE_DELETE:
(*cmd)->private.change.type = SML_CHANGE_DELETE;
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown change type set");
goto error_free_cmd;
}
if (!(*cmd)->source)
{
SmlItem *item = g_list_nth_data((*cmd)->private.change.items, 0);
if (item->source) {
(*cmd)->source = smlLocationClone(item->source, error);
if (!(*cmd)->source)
goto error_free_cmd;
}
}
if (!(*cmd)->target)
{
SmlItem *item = g_list_nth_data((*cmd)->private.change.items, 0);
if (item->target) {
(*cmd)->target = smlLocationClone(item->target, error);
if (!(*cmd)->target)
goto error_free_cmd;
}
}
/* Step once more */
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
smlTrace(TRACE_EXIT, "%s: %p", __func__, *cmd);
return TRUE;
error_free_cmd:
smlCommandUnref(*cmd);
error:
if (contenttype)
smlSafeCFree(&contenttype);
*cmd = NULL;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlMessageParseSynchdrMeta(SmlXmlParser *parser, unsigned int *maxmsgsize, unsigned int *maxobjsize, char **emi, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, parser, maxmsgsize, maxobjsize, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(maxmsgsize);
smlAssert(maxobjsize);
smlAssert(emi);
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in SyncHdr/Meta.",
xmlTextReaderConstName(parser->reader));
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXMSGSIZE)) {
if (!_smlXmlParserGetID(parser, maxmsgsize, SML_ELEMENT_MAXMSGSIZE, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXOBJSIZE)) {
if (!_smlXmlParserGetID(parser, maxobjsize, SML_ELEMENT_MAXOBJSIZE, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_EMI)) {
if (!_smlXmlParserGetString(parser, emi, SML_ELEMENT_EMI, error))
goto error;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node. expected MaxMsgSize, MaxObjSize or EMI. Instead of that: %s", xmlTextReaderConstName(parser->reader));
goto error;
}
}
if (!(*maxmsgsize) && !(*maxobjsize)) {
smlErrorSet(error, SML_ERROR_GENERIC, "No maxmsgsize set");
goto error;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
*maxmsgsize = 0;
*maxobjsize = 0;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlAlertParse(SmlXmlParser *parser, SmlCommand **cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, cmd, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(cmd);
*cmd = smlCommandNew(SML_COMMAND_TYPE_ALERT, error);
if (!*cmd)
goto error;
(*cmd)->refCount = 1;
(*cmd)->private.alert.maxObjSize = -1;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ALERT) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Alert.",
xmlTextReaderConstName(parser->reader));
goto error_free_cmd;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
SmlItem *item = _smlItemParse(parser, (*cmd), SML_COMMAND_TYPE_ALERT, error);
if (!item)
goto error_free_cmd;
(*cmd)->target = item->target;
item->target = NULL;
(*cmd)->source = item->source;
item->source = NULL;
smlItemUnref(item);
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA)) {
unsigned int id = 0;
if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_DATA, error))
goto error_free_cmd;
(*cmd)->private.alert.type = smlAlertTypeConvert(id, error);
if ((*cmd)->private.alert.type == SML_ALERT_UNKNOWN &&
*error != NULL)
goto error_free_cmd;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node %s", xmlTextReaderConstName(parser->reader));
goto error_free_cmd;
}
}
if (!(*cmd)->private.alert.type) {
smlErrorSet(error, SML_ERROR_GENERIC, "No alert type set");
goto error_free_cmd;
}
/* Step once more */
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_cmd:
smlCommandUnref(*cmd);
error:
*cmd = NULL;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlParserResult _smlCommandSyncParse(SmlXmlParser *parser, SmlCommand **cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, cmd, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(cmd);
*cmd = smlCommandNew(SML_COMMAND_TYPE_SYNC, error);
if (!*cmd)
goto error;
(*cmd)->private.sync.maxObjSize = -1;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
if (xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNC))
break;
else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Sync.",
xmlTextReaderConstName(parser->reader));
goto error_free_cmd;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ADD) || \
!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_REPLACE) || \
!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DELETE))
break;
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
SmlItem *item = _smlItemParse(parser, (*cmd), SML_COMMAND_TYPE_SYNC, error);
if (!item)
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
if (!_smlCommandMetaParse(parser, NULL, NULL, NULL, NULL, &((*cmd)->private.sync.maxObjSize), error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
if (!_smlLocationParse(&(*cmd)->target, parser, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
if (!_smlLocationParse(&(*cmd)->source, parser, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_NUMBEROFCHANGES)) {
(*cmd)->private.sync.hasNumChanged = TRUE;
if (!_smlXmlParserGetID(parser, &((*cmd)->private.sync.numChanged), SML_ELEMENT_NUMBEROFCHANGES, error))
goto error_free_cmd;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node: %s", xmlTextReaderConstName(parser->reader));
goto error_free_cmd;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_cmd:
smlCommandUnref(*cmd);
error:
*cmd = NULL;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlMapItem *_smlMapItemParse(SmlXmlParser *parser, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, parser, error);
CHECK_ERROR_REF
smlAssert(parser);
SmlMapItem *item = smlTryMalloc0(sizeof(SmlMapItem), error);
if (!item)
goto error;
item->refCount = 1;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_item;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAPITEM) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in MapItem.",
xmlTextReaderConstName(parser->reader));
goto error_free_item;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
if (!_smlLocationParse(&item->source, parser, error))
goto error_free_item;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
if (!_smlLocationParse(&item->target, parser, error))
goto error_free_item;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node %s", xmlTextReaderConstName(parser->reader));
goto error_free_item;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return item;
error_free_item:
smlMapItemUnref(item);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
static SmlBool _smlCommandMapParse(SmlXmlParser *parser, SmlCommand **cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, cmd, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(cmd);
*cmd = smlCommandNew(SML_COMMAND_TYPE_MAP, error);
if (!*cmd)
goto error;
(*cmd)->refCount = 1;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAP) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Map.",
xmlTextReaderConstName(parser->reader));
goto error_free_cmd;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAPITEM)) {
SmlMapItem *item = _smlMapItemParse(parser, error);
if (!item)
goto error_free_cmd;
(*cmd)->private.map.items = g_list_append((*cmd)->private.map.items, item);
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
if (!_smlLocationParse(&(*cmd)->target, parser, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
if (!_smlLocationParse(&(*cmd)->source, parser, error))
goto error_free_cmd;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
goto error_free_cmd;
}
}
/* Step once more */
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_cmd:
smlCommandUnref(*cmd);
error:
*cmd = NULL;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlCommandAccessParse(SmlXmlParser *parser, SmlCommand **cmd, SmlCommandType type, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p)", __func__, parser, cmd, type, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(cmd);
char *contenttype = NULL;
*cmd = smlCommandNew(type, error);
if (!*cmd)
goto error;
(*cmd)->refCount = 1;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
if ((*cmd)->type == SML_COMMAND_TYPE_PUT &&!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_PUT) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if ((*cmd)->type == SML_COMMAND_TYPE_GET &&!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_GET) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Put or Get.",
xmlTextReaderConstName(parser->reader));
goto error_free_cmd;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
(*cmd)->private.access.item = _smlItemParse(parser, (*cmd), (*cmd)->type, error);
if (!(*cmd)->private.access.item)
goto error_free_cmd;
(*cmd)->target = (*cmd)->private.access.item->target;
(*cmd)->private.access.item->target = NULL;
(*cmd)->source = (*cmd)->private.access.item->source;
(*cmd)->private.access.item->source = NULL;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
char *format = NULL;
if (!_smlCommandMetaParse(parser, &format, &contenttype, NULL, NULL, NULL, error))
goto error_free_cmd;
if (format) smlSafeCFree(&format);
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
goto error_free_cmd;
}
}
if (!(*cmd)->private.access.item) {
smlErrorSet(error, SML_ERROR_GENERIC, "Put/Get is missing item");
goto error_free_cmd;
}
/* We only use the content type of the put command if the item itself did not have
* a content type set */
if (!(*cmd)->private.access.item->contenttype && contenttype)
(*cmd)->private.access.item->contenttype = g_strdup(contenttype);
if (contenttype)
smlSafeCFree(&contenttype);
/* Step once more */
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_cmd:
smlCommandUnref(*cmd);
error:
*cmd = NULL;
if (contenttype)
smlSafeCFree(&contenttype);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlResultsParse(SmlXmlParser *parser, SmlCommand **cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, cmd, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(cmd);
char *contenttype = NULL;
char *locURI = NULL;
*cmd = smlCommandNew(SML_COMMAND_TYPE_RESULTS, error);
if (!*cmd)
goto error;
(*cmd)->refCount = 1;
(*cmd)->private.results.status = smlTryMalloc0(sizeof(SmlStatus), error);
if (!(*cmd)->private.results.status)
goto error;
(*cmd)->private.results.status->refCount = 1;
(*cmd)->private.results.status->result = (*cmd);
(*cmd)->private.results.status->type = SML_COMMAND_TYPE_RESULTS;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RESULTS) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Results.",
xmlTextReaderConstName(parser->reader));
goto error_free_cmd;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MSGREF)) {
if (!_smlXmlParserGetID(parser, &((*cmd)->private.results.status->msgRef), SML_ELEMENT_MSGREF, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDREF)) {
if (!_smlXmlParserGetID(parser, &((*cmd)->private.results.status->cmdRef), SML_ELEMENT_CMDREF, error))
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCEREF)) {
if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_SOURCEREF, error))
goto error_free_cmd;
(*cmd)->private.results.status->sourceRef = smlLocationNew(locURI, NULL, error);
smlSafeCFree(&locURI);
if (!(*cmd)->private.results.status->sourceRef)
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGETREF)) {
if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_TARGETREF, error))
goto error_free_cmd;
(*cmd)->private.results.status->targetRef = smlLocationNew(locURI, NULL, error);
smlSafeCFree(&locURI);
if (!(*cmd)->private.results.status->targetRef)
goto error_free_cmd;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
(*cmd)->private.results.status->item = _smlItemParse(parser, (*cmd), (*cmd)->type, error);
if (!(*cmd)->private.results.status->item)
goto error_free_cmd;
(*cmd)->target = (*cmd)->private.results.status->item->target;
(*cmd)->private.results.status->item->target = NULL;
(*cmd)->source = (*cmd)->private.results.status->item->source;
(*cmd)->private.results.status->item->source = NULL;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
/* ignore format */
char *format = NULL;
if (!_smlCommandMetaParse(parser, &format, &contenttype, NULL, NULL, NULL, error))
goto error_free_cmd;
if (format) smlSafeCFree(&format);
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
goto error_free_cmd;
}
}
if (!(*cmd)->private.results.status->item) {
smlErrorSet(error, SML_ERROR_GENERIC, "Result is missing item");
goto error_free_cmd;
}
/* We only use the content type of the put command if the item itself did not have
* a content type set */
if (!(*cmd)->private.results.status->item->contenttype) {
if (!contenttype) {
smlErrorSet(error, SML_ERROR_GENERIC, "Result is missing content type");
goto error_free_cmd;
}
(*cmd)->private.results.status->item->contenttype = g_strdup(contenttype);
}
if (contenttype)
smlSafeCFree(&contenttype);
/* Step once more */
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_cmd:
smlCommandUnref(*cmd);
error:
*cmd = NULL;
if (contenttype)
smlSafeCFree(&contenttype);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlXmlParserFixBrokenItemData(
const char *data, unsigned int size,
char **fixed_data, unsigned int *fixed_size,
SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %d, %p, %p, %p)", __func__, data, size, fixed_data, fixed_size, error);
CHECK_ERROR_REF
smlAssert(data);
smlAssert(size);
smlAssert(fixed_data);
smlAssert(fixed_size);
/* *******************
* fix wrong enconding
* *******************
*/
/* This fix was implemented for text/x-vMessage.
* Some mobiles encode SMS always as UTF-16
* even if the XML document is encoded in UTF-8.
* Example: Nokia E71
*/
*fixed_size = size;
*fixed_data = smlTryMalloc0(size + 1, error);
if (!*fixed_data)
{
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
memcpy(*fixed_data, data, size);
const char *position = *fixed_data;
while (position + 1 < *fixed_data + *fixed_size)
{
/* check if the next character is a NULL byte */
const char *byte = position + 1;
if (*byte != 0) {
/* the next byte is not NULL */
position++;
continue;
}
smlTrace(TRACE_INTERNAL, "%s: Found NULL byte in XML document at %p.", __func__, position);
/* the next character is a NULL byte
* so let's check how long the UTF-16 string is
*/
const char *last_utf16 = position;
while (last_utf16 + 1 < *fixed_data + *fixed_size &&
*((char *)(last_utf16 + 1)) == 0)
{
last_utf16 += 2;
}
/* go to the last NULL byte */
last_utf16--;
/* convert the whole strong to UTF-8 */
smlTrace(TRACE_INTERNAL, "%s: Converting %d bytes ...", __func__, last_utf16 - position + 1);
GError *gerror = NULL;
size_t read_bytes = 0;
size_t written_bytes = 0;
gchar *conv_string = g_convert(
position, (last_utf16 - position + 1),
"UTF-8", "UTF-16",
&read_bytes, &written_bytes,
&gerror);
if (gerror != NULL)
{
smlErrorSet(
error, SML_ERROR_GENERIC,
"Character conversion from UTF-16 to UTF-8 failed. %s",
gerror->message);
g_error_free(gerror);
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
smlTrace(TRACE_INTERNAL, "%s: read %d --> written %d --> %d ::= %s", __func__, read_bytes, written_bytes, strlen(conv_string), conv_string);
/* replace the embedded string */
char *new_data = smlTryMalloc0(*fixed_size - read_bytes + written_bytes + 1, error);
if (!new_data)
{
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
memcpy(new_data, *fixed_data, (size_t) position - (size_t) *fixed_data);
memcpy(new_data + (size_t) position - (size_t) *fixed_data, conv_string, written_bytes);
memcpy(new_data + (size_t) position - (size_t) *fixed_data + written_bytes,
position + read_bytes, *fixed_size - ( (size_t) position - (size_t) *fixed_data ) - read_bytes );
/* fix pointers */
*fixed_size = *fixed_size - read_bytes + written_bytes;
position = new_data + (position - *fixed_data) + written_bytes;
smlSafeCFree(fixed_data);
*fixed_data = new_data;
new_data = NULL;
smlSafeCFree(&conv_string);
smlTrace(TRACE_INTERNAL, "%s: Converted UTF-16 string to UTF-8", __func__);
}
smlTrace(TRACE_INTERNAL, "%s: Correctly encoded: %s", __func__, *fixed_data);
/* *****************
* add missing CDATA
* *****************
*/
position = *fixed_data;
while (position + 1 < *fixed_data + *fixed_size)
{
/* find item */
const char *limit_item_start = strstr(position, "<Item>");
if (! limit_item_start)
{
smlTrace(TRACE_EXIT, "%s - no (more) Item found", __func__);
return TRUE;
} else {
smlTrace(TRACE_INTERNAL, "%s - Item found", __func__);
}
const char *limit_item_end = strstr(limit_item_start, "</Item>");
if (! limit_item_end)
{
g_warning("%s - no end of Item found.", __func__);
smlTrace(TRACE_EXIT, "%s - no end of Item found", __func__);
return TRUE;
} else {
smlTrace(TRACE_INTERNAL, "%s - end of Item found", __func__);
}
if (limit_item_start >= limit_item_end)
{
/* </Item> is before <Item> ?! */
position = limit_item_end + strlen("</Item>");
continue;
}
/* find data */
const char *limit_data_start = strstr(limit_item_start, "<Data>");
if (! limit_data_start)
{
smlTrace(TRACE_EXIT, "%s - no Data found", __func__);
return TRUE;
} else {
smlTrace(TRACE_INTERNAL, "%s - Data found", __func__);
}
const char *limit_data_end = strstr(limit_data_start, "</Data>");
if (! limit_data_end)
{
g_warning("%s - no end of Data found.", __func__);
smlTrace(TRACE_EXIT, "%s - no end of Data found", __func__);
return TRUE;
} else {
smlTrace(TRACE_INTERNAL, "%s - end of Data found", __func__);
}
if (limit_data_start >= limit_data_end)
{
/* </Data> is before <Data> ?! */
position = limit_data_end + strlen("</Data>");
continue;
}
/* check limits */
if (limit_item_start >= limit_data_start ||
limit_data_start >= limit_data_end ||
limit_data_end >= limit_item_end)
{
/* There is something wrong with the encapsulation.
* Expected: <Item>...<Data>...</Data>...</Item>
*/
position = limit_item_end + strlen("</Item>");
continue;
}
/* check for CDATA */
const char *cdata_start = strstr(limit_data_start, "<![CDATA[");
const char *cdata_end = NULL;
if (cdata_start)
cdata_end = strstr(cdata_start, "]]");
if (cdata_start && cdata_end &&
limit_data_start < cdata_start &&
cdata_start < cdata_end &&
cdata_end < limit_data_end)
{
/* Anything is fine. */
position = limit_item_end + strlen("</Item>");
continue;
}
/* check that there is no tag inside Data */
if (g_strstr_len(
limit_data_start + strlen("<Data>"),
limit_data_end - limit_data_start - strlen("<Data>"),
"<"))
{
/* There is a tag in the Data area. */
position = limit_item_end + strlen("</Item>");
continue;
}
/* There is data in an item element and it is not CDATA.
* - allocate new memory
* - copy before data
* - add <![CDATA[
* - copy data
* - add ]]
* - copy after data
* - fix position
* - cleanup
*/
smlTrace(TRACE_INTERNAL, "%s: There is data which is not CDATA.", __func__);
/* 1 is added to the real length * because of the
* trailing NULL byte. This NULL byte is required
* because XML documents can be represented as strings
* and a string must be terminated by a NULL byte.
*/
char *new_data = smlTryMalloc0(*fixed_size + strlen("<![CDATA[]]>") + 1, error);
if (!new_data)
{
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
size_t before_size = limit_data_start + strlen("<Data>") - *fixed_data;
size_t data_size = limit_data_end - limit_data_start - strlen("<Data>");
size_t after_size = *fixed_size - before_size - data_size;
smlTrace(TRACE_INTERNAL, "%s: %i = %i + %i +%i", __func__,
*fixed_size, before_size, data_size, after_size);
memcpy(new_data, *fixed_data, before_size);
memcpy(new_data + before_size, "<![CDATA[", strlen ("<![CDATA["));
memcpy(new_data + before_size + strlen ("<![CDATA["),
limit_data_start + strlen("<Data>"),
data_size);
memcpy(new_data + before_size + strlen ("<![CDATA[") + data_size,
"]]>", strlen("]]>"));
memcpy(new_data + before_size + strlen ("<![CDATA[]]>") + data_size,
limit_data_end,
after_size);
smlSafeCFree(fixed_data);
*fixed_data = new_data;
new_data = NULL;
*fixed_size += strlen("<![CDATA[]]>");
/* The position must be calculated on base of the new string. */
position = *fixed_data +
before_size + strlen ("<![CDATA[]]>") +
data_size + strlen("</Data></Item>");
smlTrace(TRACE_INTERNAL, "%s: CDATA inserted", __func__);
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
SmlBool smlXmlParserStart(SmlXmlParser *parser, const char *data, unsigned int size, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p)", __func__, parser, data, size, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(data);
smlAssert(size);
/* Repair item data if necessary.
* Some devices does not encapsulate the data correctly
* (e.g. SE M600i send the second part of a chunked item
* without CDATA and so the CR LF is normalied to a LF
* which creates a wrong length and so the item is detected
* as damaged).
*/
parser->data = NULL;
parser->size = 0;
if (! _smlXmlParserFixBrokenItemData(
data, size, &(parser->data), &(parser->size), error))
{
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
char *debugstr = smlPrintBinary(parser->data, parser->size);
smlTrace(TRACE_INTERNAL, "Xml input: %s", debugstr);
smlLog("received-%i.xml", parser->data, parser->size);
smlSafeCFree(&debugstr);
parser->got_command = FALSE;
/* Create the new parser */
parser->reader = xmlReaderForMemory(parser->data, parser->size, "/", NULL, XML_PARSE_NONET | XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA);
if (!parser->reader) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new reader");
goto error;
}
xmlSubstituteEntitiesDefault(1);
/* Check the First Node (SyncML)*/
if (!_smlXmlParserExpectNode(parser, XML_NODE_START, FALSE, SML_ELEMENT_SYNCML, error))
goto error_free_reader;
/* Check the Second Node (SyncHdr)*/
if (!_smlXmlParserExpectNode(parser, XML_NODE_START, FALSE, SML_ELEMENT_SYNCHDR, error))
goto error_free_reader;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_reader:
xmlFreeTextReader(parser->reader);
error:
parser->reader = NULL;
if (parser->data)
smlSafeCFree(&(parser->data));
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlParserEnd(SmlXmlParser *parser, SmlBool *final, SmlBool *end, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, parser, final, end, error);
CHECK_ERROR_REF
smlAssert(parser);
SmlBool got_final = FALSE;
/* Check for final */
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FINAL)) {
got_final = TRUE;
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FINAL) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
}
}
if (final)
*final = got_final;
/* Check for the end. We can only decide if this was the last message if this
* was the final message */
if (end) {
if (got_final)
*end = parser->got_command ? FALSE : TRUE;
else
*end = FALSE;
}
/* Check the closing SyncBody - which is perhaps only an empty element. */
if (strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCBODY) ||
(!xmlTextReaderIsEmptyElement(parser->reader) &&
xmlTextReaderNodeType(parser->reader) != XML_NODE_CLOSE
)
) {
smlErrorSet(error, SML_ERROR_GENERIC, "Wrong closing syncbody node");
goto error;
}
/* Check the next node (SyncML)*/
if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, SML_ELEMENT_SYNCML, error))
goto error;
xmlFreeTextReader(parser->reader);
parser->reader = NULL;
parser->size = 0;
smlSafeCFree(&(parser->data));
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
void smlXmlParserFree(SmlXmlParser *parser)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, parser);
smlAssert(parser);
if (parser->reader) {
xmlFreeTextReader(parser->reader);
}
if (parser->data)
smlSafeCFree(&(parser->data));
smlSafeFree((gpointer *)&parser);
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlBool smlXmlParserGetHeader(SmlXmlParser *parser, SmlHeader **header, SmlCred **cred, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, header, cred, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(header);
smlAssert(cred);
if (!xmlTextReaderConstName(parser->reader) || strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCHDR) || \
xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
/* It is important to notice that empty headers are not allowed. */
smlErrorSet(error, SML_ERROR_GENERIC, "Wrong starting node for a header");
goto error;
}
parser->version = SML_VERSION_UNKNOWN;
*header = smlTryMalloc0(sizeof(SmlHeader), error);
if (!*header)
goto error;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_header;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCHDR) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in SyncHdr.",
xmlTextReaderConstName(parser->reader));
goto error_free_header;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERDTD)) {
if (!_smlSyncHeaderParseDTD(&((*header)->version), parser, error))
goto error_free_header;
parser->version = (*header)->version;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERPROTO)) {
if (!_smlSyncHeaderParseProto(&((*header)->protocol), parser, error))
goto error_free_header;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SESSIONID)) {
if (!_smlXmlParserGetString(parser, &((*header)->sessionID), SML_ELEMENT_SESSIONID, error))
goto error_free_header;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MSGID)) {
if (!_smlXmlParserGetID(parser, &((*header)->messageID), SML_ELEMENT_MSGID, error))
goto error_free_header;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
if (!_smlLocationParse(&((*header)->target), parser, error))
goto error_free_header;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
if (!_smlLocationParse(&((*header)->source), parser, error))
goto error_free_header;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RESPURI)) {
/* Only one RespURI is allowed.
* If the header is not correct (more than one RespURI)
* then the last RespURI is taken.
*/
if ((*header)->responseURI) {
smlTrace(TRACE_ERROR, "%s: There is more than on RespURI.", __func__);
smlSafeCFree(&((*header)->responseURI));
}
if (!_smlXmlParserGetString(parser, &((*header)->responseURI), SML_ELEMENT_RESPURI, error))
goto error_free_header;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_NORESP)) {
/* This is an empty element. So the parser must not move one step forward. */
(*header)->noResponse = TRUE;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
if (!_smlMessageParseSynchdrMeta(parser, &((*header)->maxmsgsize), &((*header)->maxobjsize), &((*header)->emi), error))
goto error_free_header;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CRED)) {
*cred = _smlCredParse(parser, error);
if (!(*cred))
goto error_free_header;
}
else {
smlErrorSet(error, SML_ERROR_GENERIC, "Element %s not supported for SyncHdr.",
(char *)xmlTextReaderConstName(parser->reader));
goto error_free_header;
}
}
if (!(*header)->protocol) {
smlErrorSet(error, SML_ERROR_GENERIC, "No protocol set");
goto error_free_header;
}
if (!(*header)->version) {
smlErrorSet(error, SML_ERROR_GENERIC, "No dtd version set");
goto error_free_header;
}
if (!(*header)->sessionID) {
smlErrorSet(error, SML_ERROR_GENERIC, "No sessionID set");
goto error_free_header;
}
if (!(*header)->target) {
smlErrorSet(error, SML_ERROR_GENERIC, "No target set");
goto error_free_header;
}
if (!(*header)->source) {
smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
goto error_free_header;
}
if (!(*header)->messageID) {
smlErrorSet(error, SML_ERROR_GENERIC, "No msgid set");
goto error_free_header;
}
/* Check the Next Node (SyncBody)*/
if (!_smlXmlParserExpectNode(parser, XML_NODE_START, FALSE, SML_ELEMENT_SYNCBODY, error))
goto error_free_header;
/* Step once more if it is not an empty element. */
if (!xmlTextReaderIsEmptyElement(parser->reader) && !_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_header;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_header:
smlHeaderFree(*header);
*header = NULL;
if (*cred)
smlCredUnref(*cred);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlXmlParserGetStatus(SmlXmlParser *parser, SmlStatus **status, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, status, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(status);
char *locURI = NULL;
SmlItem *item = NULL;
/* Lets check if the next node is a command node */
if (smlCommandTypeFromString((char *)xmlTextReaderConstName(parser->reader), error) != SML_COMMAND_TYPE_UNKNOWN && xmlTextReaderNodeType(parser->reader) == XML_NODE_START) {
*status = NULL;
smlTrace(TRACE_EXIT, "%s: Next is command", __func__);
return TRUE;
} else {
smlErrorDeref(error);
}
/* Lets check if the next node is a final node */
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FINAL) && (xmlTextReaderNodeType(parser->reader) == XML_NODE_START || xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE)) {
*status = NULL;
smlTrace(TRACE_EXIT, "%s: Next is final", __func__);
return TRUE;
}
/* Let's check if the next node is a closing or empty syncbody node. */
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCBODY) &&
(xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE ||
xmlTextReaderIsEmptyElement(parser->reader)
)
) {
*status = NULL;
smlTrace(TRACE_EXIT, "%s: Next is closing syncbody", __func__);
return TRUE;
}
if (strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_STATUS)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unexpected element <%s>.",
(char *)xmlTextReaderConstName(parser->reader));
goto error;
}
if (xmlTextReaderIsEmptyElement(parser->reader))
{
/* An empty status element is not allowed.
* Try to finish in a way that the parser can still be used.
* The parser can ignore this mistake and finish correctly.
* Nevertheless a second mistake which crashs the NextStep
* function cannot be handled.
*/
_smlXmlParserStep(parser);
smlErrorSet(error, SML_ERROR_GENERIC, "Empty status element");
goto error;
}
if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not the start node of Status.",
xmlTextReaderConstName(parser->reader));
goto error;
}
*status = smlStatusNew(SML_ERROR_UNKNOWN, 0, 0, NULL, NULL, SML_COMMAND_TYPE_UNKNOWN, error);
if (!*status)
goto error;
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_status;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_STATUS) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Status.",
xmlTextReaderConstName(parser->reader));
goto error_free_status;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
//Errr... who cares?
if (!_smlXmlParserExpectNode(parser, XML_NODE_TEXT, FALSE, NULL, error))
goto error_free_status;
if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, SML_ELEMENT_CMDID, error))
goto error_free_status;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MSGREF)) {
/* MsgRef is defined as parsed character data:
OMA-SUP-DTD_SyncML_RepPro-V1_2-20070221-A.txt:<!ELEMENT MsgRef (#PCDATA)>
SyncML Reprent Version 1.1 20020215 - Chapter 5.1.13 MsgRef:
"[...] reference to a SyncML session-uniquie identifier referenced by SyncML result or response status."
So it could be something else then an unsigned integer.
*/
if (!_smlXmlParserGetID(parser, &((*status)->msgRef), SML_ELEMENT_MSGREF, error))
goto error_free_status;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDREF)) {
if (!_smlXmlParserGetID(parser, &((*status)->cmdRef), SML_ELEMENT_CMDREF, error))
goto error_free_status;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMD)) {
char *cmdname = NULL;
if (!_smlXmlParserGetString(parser, &cmdname, SML_ELEMENT_CMD, error))
goto error_free_status;
(*status)->type = smlCommandTypeFromString(cmdname, error);
smlSafeCFree(&cmdname);
if ((*status)->type == SML_COMMAND_TYPE_UNKNOWN)
goto error_free_status;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA)) {
if (!_smlXmlParserGetString(parser, &((*status)->data), SML_ELEMENT_DATA, error))
goto error_free_status;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCEREF)) {
if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_SOURCEREF, error))
goto error_free_status;
(*status)->sourceRef = smlLocationNew(locURI, NULL, error);
smlSafeCFree(&locURI);
if (!(*status)->sourceRef)
goto error_free_status;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGETREF)) {
if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_TARGETREF, error))
goto error_free_status;
(*status)->targetRef = smlLocationNew(locURI, NULL, error);
smlSafeCFree(&locURI);
if (!(*status)->targetRef)
goto error_free_status;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
/* Do not send SML_COMMAND_TYPE_ALERT
* because this can give trouble.
* Example: Error message for a failed ADD
*/
item = _smlItemParse(parser, NULL, (*status)->type, error);
if (!item)
goto error_free_status;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CHAL)) {
(*status)->chal = _smlChalParse(parser, error);
if (!(*status)->chal)
goto error_free_status;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
goto error;
}
}
/* Step once more */
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_status;
}
if (item)
{
if ((*status)->type == SML_COMMAND_TYPE_ALERT) {
(*status)->anchor = item->anchor;
item->anchor = NULL;
} else if ((*status)->type == SML_COMMAND_TYPE_ADD ||
(*status)->type == SML_COMMAND_TYPE_REPLACE ||
(*status)->type == SML_COMMAND_TYPE_DELETE) {
/* Usually Sync is commited at once or together with an anchor.
* If a single ADD, REPLACE or DELETE gets an item
* then there is something going wrong.
*/
g_warning("Received explicit status for add, delete or replace (%s).", xmlBufferContent(item->buffer));
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "Got wrong item");
smlItemUnref(item);
goto error_free_status;
}
smlItemUnref(item);
}
/* MsgRef is defined as #PCDATA - so it doesn't have to be a
unsigned integer AND it could be set to 0/zero as well.
This sanity check breaks if MsgRef is set to 0/zero.
if (!(*status)->msgRef) {
smlErrorSet(error, SML_ERROR_GENERIC, "No msgref set");
goto error_free_status;
}
*/
if ((*status)->type == SML_COMMAND_TYPE_UNKNOWN) {
smlErrorSet(error, SML_ERROR_GENERIC, "No cmd set");
goto error_free_status;
}
smlTrace(TRACE_INTERNAL, "Got status %p with: cmdRef %i, msgRef %i, type %i, data %s", *status, (*status)->cmdRef, (*status)->msgRef, (*status)->type, VA_STRING((*status)->data));
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_status:
smlStatusUnref(*status);
error:
*status = NULL;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlParserResult smlXmlParserGetCommand(SmlXmlParser *parser, SmlCommand **cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, cmd, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(cmd);
*cmd = NULL;
/* Lets check if the next node is a final node */
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FINAL) && (xmlTextReaderNodeType(parser->reader) == XML_NODE_START || xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE)) {
*cmd = NULL;
smlTrace(TRACE_EXIT, "%s: Next is final", __func__);
return SML_PARSER_RESULT_OTHER;
}
/* Let's check if the next node is a closing or empty syncbody node. */
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCBODY) &&
(xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE ||
xmlTextReaderIsEmptyElement(parser->reader)
)
) {
*cmd = NULL;
smlTrace(TRACE_EXIT, "%s: Next is closing syncbody", __func__);
return SML_PARSER_RESULT_OTHER;
}
/* Lets check if the next node status node */
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_STATUS) && xmlTextReaderNodeType(parser->reader) == XML_NODE_START) {
*cmd = NULL;
smlTrace(TRACE_EXIT, "%s: Next is status", __func__);
return SML_PARSER_RESULT_STATUS;
}
SmlCommandType type = smlCommandTypeFromString((char *)xmlTextReaderConstName(parser->reader), error);
if (!type)
goto error;
SmlParserResult result = SML_PARSER_RESULT_NORMAL;
switch (type) {
case SML_COMMAND_TYPE_ALERT:
if (!_smlAlertParse(parser, cmd, error))
goto error;
break;
case SML_COMMAND_TYPE_PUT:
case SML_COMMAND_TYPE_GET:
if (!_smlCommandAccessParse(parser, cmd, type, error))
goto error;
break;
case SML_COMMAND_TYPE_SYNC:
if (xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_cmd;
}
smlTrace(TRACE_EXIT, "%s: SML_PARSER_RESULT_CLOSE", __func__);
return SML_PARSER_RESULT_CLOSE;
} else {
if (!_smlCommandSyncParse(parser, cmd, error))
goto error;
result = SML_PARSER_RESULT_OPEN;
}
break;
case SML_COMMAND_TYPE_MAP:
if (!_smlCommandMapParse(parser, cmd, error))
goto error;
break;
case SML_COMMAND_TYPE_ADD:
if (!_smlChangeParse(parser, cmd, SML_COMMAND_TYPE_ADD, SML_ELEMENT_ADD, error))
goto error;
break;
case SML_COMMAND_TYPE_REPLACE:
if (!_smlChangeParse(parser, cmd, SML_COMMAND_TYPE_REPLACE, SML_ELEMENT_REPLACE, error))
goto error;
break;
case SML_COMMAND_TYPE_DELETE:
if (!_smlChangeParse(parser, cmd, SML_COMMAND_TYPE_DELETE, SML_ELEMENT_DELETE, error))
goto error;
break;
case SML_COMMAND_TYPE_RESULTS:
if (!_smlResultsParse(parser, cmd, error))
goto error;
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unsupported command type");
goto error;
}
if (!(*cmd)->cmdID) {
smlErrorSet(error, SML_ERROR_GENERIC, "No cmdid set");
goto error_free_cmd;
}
parser->got_command = TRUE;
smlTrace(TRACE_EXIT, "%s: %i", __func__, result);
return result;
error_free_cmd:
if (*cmd)
smlCommandUnref(*cmd);
error:
*cmd = NULL;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return SML_PARSER_RESULT_ERROR;
}
SmlXmlParser *smlXmlParserNew(SmlParserFunctions *functions, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, functions,error);
CHECK_ERROR_REF
smlAssert(functions);
SmlXmlParser *parser = smlTryMalloc0(sizeof(SmlXmlParser), error);
if (!parser)
goto error;
functions->start = (SmlParserStartFunction)smlXmlParserStart;
functions->free = (SmlParserFreeFunction)smlXmlParserFree;
functions->end = (SmlParserEndFunction)smlXmlParserEnd;
functions->get_header = (SmlParserHeaderFunction)smlXmlParserGetHeader;
functions->get_cmd = (SmlParserCommandFunction)smlXmlParserGetCommand;
functions->get_status = (SmlParserStatusFunction)smlXmlParserGetStatus;
smlTrace(TRACE_EXIT, "%s: %p", __func__, parser);
return parser;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
static SmlBool _smlXmlDevInfDataStoreParseDSMem(SmlXmlParser *parser, SmlDevInfDataStore *datastore, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, datastore, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(datastore);
if (xmlTextReaderIsEmptyElement(parser->reader)) {
/* An empty DSMem element is allowed according to SyncML DevInf 1.1.2. */
smlTrace(TRACE_EXIT, "%s - empty DSMem", __func__);
return TRUE;
}
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DSMEM) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in DSMem.",
xmlTextReaderConstName(parser->reader));
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXID)) {
if (!_smlXmlParserGetID(parser, &(datastore->maxid), SML_ELEMENT_MAXID, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SHAREDMEM)) {
datastore->sharedMem = TRUE;
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SHAREDMEM) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes2");
goto error;
}
}
continue;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXMEM)) {
if (!_smlXmlParserGetID(parser, &(datastore->maxmem), SML_ELEMENT_MAXMEM, error))
goto error;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
goto error;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlXmlDevInfDataStoreParseSyncCap(SmlXmlParser *parser, SmlDevInfDataStore *datastore, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, datastore, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(datastore);
while (1) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCCAP) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in SyncCap.",
xmlTextReaderConstName(parser->reader));
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCTYPE)) {
unsigned int id = 0;
if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_SYNCTYPE, error))
goto error;
id = 1 << (id - 1);
SmlDevInfSyncCap synccap = smlDevInfSyncCapConvert(id, error);
if (synccap == SML_DEVINF_SYNCTYPE_UNKNOWN &&
error != NULL)
goto error;
if (synccap != SML_DEVINF_SYNCTYPE_UNKNOWN)
smlDevInfDataStoreSetSyncCap(datastore, synccap, TRUE);
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
goto error;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlXmlDevInfDataStoreParseCTCap11(SmlXmlParser *parser, SmlDevInf *devinf, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, devinf, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(devinf);
/* This function was designed to parse one CTCap section */
/* which can include several CTCap definitions */
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
SmlDevInfCTCap *ctcap = NULL;
SmlDevInfProperty *property = NULL;
SmlDevInfPropParam *param = NULL;
char *value = NULL;
while (1) {
const char *elem_name;
elem_name = (const char*)xmlTextReaderConstName(parser->reader);
smlTrace(TRACE_INTERNAL, "read: %s\n", elem_name);
if (!strcmp(elem_name, SML_ELEMENT_CTCAP) &&
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
// this is the end of the CTCap section
break;
}
if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START)
{
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in CTCap.",
xmlTextReaderConstName(parser->reader));
goto error;
}
/* determine list item type */
SmlDevInfCTCapType type;
type = smlDevInfCTCapTypeFromString(elem_name, error);
value = NULL;
if (type != SML_DEVINF_CTCAP_UNKNOWN)
{
_smlXmlParserGetString(parser, &value, elem_name, error);
}
/* now react on the different items */
switch(type)
{
case SML_DEVINF_CTCAP_CTTYPE:
// CTType => new ctcap definition
if (ctcap != NULL)
{
if (property != NULL)
{
if (param != NULL)
{
smlDevInfPropertyAddPropParam(property, param);
param = NULL;
}
smlDevInfCTCapAddProperty(ctcap, property);
property = NULL;
}
smlDevInfAppendCTCap(devinf, ctcap);
ctcap = NULL;
}
ctcap = smlDevInfNewCTCap(error);
if (!ctcap)
goto error;
smlDevInfCTCapSetCTType(ctcap, value);
break;
case SML_DEVINF_CTCAP_VERCT:
if (ctcap == NULL)
goto error;
smlDevInfCTCapSetVerCT(ctcap, value);
break;
case SML_DEVINF_CTCAP_PROPNAME:
// PropName => new property
if (ctcap == NULL)
goto error;
if (property != NULL)
{
if (param != NULL)
{
smlDevInfPropertyAddPropParam(property, param);
param = NULL;
}
smlDevInfCTCapAddProperty(ctcap, property);
property = NULL;
}
property = smlDevInfNewProperty(error);
if (!property)
goto error;
smlDevInfPropertySetPropName(property, value);
break;
case SML_DEVINF_CTCAP_DATATYPE:
if (property == NULL)
goto error;
if (param != NULL)
smlDevInfPropParamSetDataType(param, value);
else
smlDevInfPropertySetDataType(property, value);
break;
case SML_DEVINF_CTCAP_MAXOCCUR:
if (property == NULL)
goto error;
smlDevInfPropertySetMaxOccur(property, atoi(value));
break;
case SML_DEVINF_CTCAP_MAXSIZE:
if (property == NULL)
goto error;
smlDevInfPropertySetMaxSize(property, atoi(value));
break;
case SML_DEVINF_CTCAP_NOTRUNCATE:
if (property == NULL)
goto error;
smlDevInfPropertySetNoTruncate(property);
break;
case SML_DEVINF_CTCAP_DISPLAYNAME:
if (property == NULL)
goto error;
if (param != NULL)
smlDevInfPropParamSetDisplayName(param, value);
else
smlDevInfPropertySetDisplayName(property, value);
break;
case SML_DEVINF_CTCAP_VALENUM:
if (property == NULL)
goto error;
if (param != NULL)
smlDevInfPropParamAddValEnum(param, value);
else
smlDevInfPropertyAddValEnum(property, value);
break;
case SML_DEVINF_CTCAP_PARAMNAME:
if (property == NULL)
goto error;
if (param != NULL)
{
smlDevInfPropertyAddPropParam(property, param);
param = NULL;
}
param = smlDevInfNewPropParam(error);
if (!param)
goto error;
smlDevInfPropParamSetParamName(param, value);
break;
case SML_DEVINF_CTCAP_SIZE:
smlDevInfPropertySetPropSize(property, atoi(value));
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown CTCapType: %s", elem_name);
goto error;
}
smlSafeCFree(&value);
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
}
if (param != NULL)
smlDevInfPropertyAddPropParam(property, param);
if (property != NULL)
smlDevInfCTCapAddProperty(ctcap, property);
if (ctcap != NULL)
smlDevInfAppendCTCap(devinf, ctcap);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
if (value != NULL)
smlSafeCFree(&value);
return FALSE;
}
static SmlDevInfPropParam *_smlXmlDevInfDataStoreParseCTCap12PropParam(SmlXmlParser *parser, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, error);
CHECK_ERROR_REF
smlAssert(parser);
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
SmlDevInfPropParam *param = smlDevInfNewPropParam(error);
if (!param)
goto error;
char *value = NULL;
while (1) {
const char *elem_name;
elem_name = (const char*)xmlTextReaderConstName(parser->reader);
smlTrace(TRACE_INTERNAL, "read: %s\n", elem_name);
if (!strcmp(elem_name, SML_ELEMENT_PROPPARAM) &&
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
// this is the end of the PropParam section
break;
}
if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START)
{
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in PropParam.",
xmlTextReaderConstName(parser->reader));
goto error;
}
/* determine item type */
SmlDevInfCTCapType type;
type = smlDevInfCTCapTypeFromString(elem_name, error);
value = NULL;
if (type != SML_DEVINF_CTCAP_UNKNOWN)
{
_smlXmlParserGetString(parser, &value, elem_name, error);
}
/* now react on the different items */
switch(type)
{
case SML_DEVINF_CTCAP_PARAMNAME:
smlDevInfPropParamSetParamName(param, value);
break;
case SML_DEVINF_CTCAP_DATATYPE:
smlDevInfPropParamSetDataType(param, value);
break;
case SML_DEVINF_CTCAP_DISPLAYNAME:
smlDevInfPropParamSetDisplayName(param, value);
break;
case SML_DEVINF_CTCAP_VALENUM:
smlDevInfPropParamAddValEnum(param, value);
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown CTCapType for PropParam: %s", elem_name);
goto error;
}
smlSafeCFree(&value);
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return param;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
if (value != NULL)
smlSafeCFree(&value);
return NULL;
}
static SmlDevInfProperty *_smlXmlDevInfDataStoreParseCTCap12Property(SmlXmlParser *parser, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, error);
CHECK_ERROR_REF
smlAssert(parser);
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
SmlDevInfProperty *property = smlDevInfNewProperty(error);
if (!property)
goto error;
SmlDevInfPropParam *param;
char *value = NULL;
while (1) {
const char *elem_name;
elem_name = (const char*)xmlTextReaderConstName(parser->reader);
smlTrace(TRACE_INTERNAL, "read: %s\n", elem_name);
if (!strcmp(elem_name, SML_ELEMENT_PROPERTY) &&
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
// this is the end of the Property section
break;
}
if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START)
{
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Property.",
xmlTextReaderConstName(parser->reader));
goto error;
}
/* determine item type */
SmlDevInfCTCapType type;
type = smlDevInfCTCapTypeFromString(elem_name, error);
value = NULL;
if (type != SML_DEVINF_CTCAP_UNKNOWN &&
type != SML_DEVINF_CTCAP_PROPPARAM)
{
_smlXmlParserGetString(parser, &value, elem_name, error);
}
/* now react on the different items */
switch(type)
{
case SML_DEVINF_CTCAP_PROPNAME:
smlDevInfPropertySetPropName(property, value);
break;
case SML_DEVINF_CTCAP_DATATYPE:
smlDevInfPropertySetDataType(property, value);
break;
case SML_DEVINF_CTCAP_MAXOCCUR:
smlDevInfPropertySetMaxOccur(property, atoi(value));
break;
case SML_DEVINF_CTCAP_MAXSIZE:
smlDevInfPropertySetMaxSize(property, atoi(value));
break;
case SML_DEVINF_CTCAP_NOTRUNCATE:
smlDevInfPropertySetNoTruncate(property);
break;
case SML_DEVINF_CTCAP_DISPLAYNAME:
smlDevInfPropertySetDisplayName(property, value);
break;
case SML_DEVINF_CTCAP_VALENUM:
smlDevInfPropertyAddValEnum(property, value);
break;
case SML_DEVINF_CTCAP_PROPPARAM:
param = _smlXmlDevInfDataStoreParseCTCap12PropParam(parser, error);
if (!param)
goto error;
smlDevInfPropertyAddPropParam(property, param);
break;
case SML_DEVINF_CTCAP_SIZE:
smlDevInfPropertySetPropSize(property, atoi(value));
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown CTCapType for Property: %s", elem_name);
goto error;
}
if (value) smlSafeCFree(&value);
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return property;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
if (value != NULL)
smlSafeCFree(&value);
return NULL;
}
static SmlBool _smlXmlDevInfDataStoreParseCTCap12(SmlXmlParser *parser, SmlDevInf *devinf, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, devinf, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(devinf);
/* This function was designed to parse one CTCap section */
/* which can include only one CTCap definition */
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
SmlDevInfCTCap *ctcap = smlDevInfNewCTCap(error);
if (!ctcap)
goto error;
SmlDevInfProperty *property;
char *value = NULL;
while (1) {
const char *elem_name;
elem_name = (const char*)xmlTextReaderConstName(parser->reader);
smlTrace(TRACE_INTERNAL, "read: %s\n", elem_name);
if (!strcmp(elem_name, SML_ELEMENT_CTCAP) &&
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
// this is the end of the CTCap section
break;
}
if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START)
{
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in CtCap.",
xmlTextReaderConstName(parser->reader));
goto error;
}
/* determine item type */
SmlDevInfCTCapType type;
type = smlDevInfCTCapTypeFromString(elem_name, error);
value = NULL;
if (type != SML_DEVINF_CTCAP_UNKNOWN &&
type != SML_DEVINF_CTCAP_PROPERTY)
{
_smlXmlParserGetString(parser, &value, elem_name, error);
}
/* now react on the different items */
switch(type)
{
case SML_DEVINF_CTCAP_CTTYPE:
smlDevInfCTCapSetCTType(ctcap, value);
break;
case SML_DEVINF_CTCAP_VERCT:
smlDevInfCTCapSetVerCT(ctcap, value);
break;
case SML_DEVINF_CTCAP_PROPERTY:
property = _smlXmlDevInfDataStoreParseCTCap12Property(parser, error);
if (!property)
goto error;
smlDevInfCTCapAddProperty(ctcap, property);
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown CTCapType for CTCap: %s", elem_name);
goto error;
}
if (value) smlSafeCFree(&value);
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
}
smlDevInfAppendCTCap(devinf, ctcap);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
if (value != NULL)
smlSafeCFree(&value);
return FALSE;
}
static SmlBool _smlXmlDevInfDataStoreParseCTCap(SmlXmlParser *parser, SmlDevInf *devinf, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, devinf, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(devinf);
/* SyncML 1.0 and 1.1 start a new CTCap with a new CTType definition */
/* SyncML 1.2 starts a new CTCap with a new CTCap definition */
/* SyncML 1.2 attaches the CTCaps to the appropriate datastore */
/* SyncML 1.0 and 1.1 attach the CTCaps to DevInf */
/* The SyncML version is in devinf->version */
SmlBool ret;
if (devinf->version == SML_DEVINF_VERSION_12)
ret = _smlXmlDevInfDataStoreParseCTCap12(parser, devinf, error);
else
ret = _smlXmlDevInfDataStoreParseCTCap11(parser, devinf, error);
smlTrace(TRACE_EXIT, "%s (%d)", __func__, ret);
return ret;
}
static SmlBool _smlXmlDevInfDataStoreParseRxTx(SmlXmlParser *parser, const char *element, char **cttype, char **version, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %s, %p, %p, %p)", __func__, parser, VA_STRING(element), cttype, version, error);
CHECK_ERROR_REF
smlAssert(parser);
smlAssert(element);
smlAssert(cttype);
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
while (1) {
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), element) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in %s.",
xmlTextReaderConstName(parser->reader), element);
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CTTYPE)) {
if (!_smlXmlParserGetString(parser, cttype, SML_ELEMENT_CTTYPE, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERCT)) {
/* Some phones send an empty VerCT for formats that dont have a version
* (like notes). */
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "No node at all");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERCT) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
*version = g_strdup("");
} else if (xmlTextReaderNodeType(parser->reader) == XML_NODE_TEXT) {
*version = g_strstrip(g_strdup((char *)xmlTextReaderConstValue(parser->reader)));
if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, SML_ELEMENT_VERCT, error))
goto error;
} else {
*version = g_strdup("");
continue;
}
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
goto error;
}
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlXmlDevInfDataStoreParse(SmlXmlParser *parser, SmlDevInf *devinf, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, devinf, error);
CHECK_ERROR_REF
smlAssert(devinf);
smlAssert(parser);
SmlDevInfDataStore *datastore = smlTryMalloc0(sizeof(SmlDevInfDataStore), error);
if (!datastore)
goto error;
datastore->refCount = 1;
datastore->rxPrefContentType = NULL;
datastore->txPrefContentType = NULL;
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_datastore;
}
while (1) {
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATASTORE) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in Datastore.",
xmlTextReaderConstName(parser->reader));
goto error_free_datastore;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCEREF)) {
if (!_smlXmlParserGetString(parser, &(datastore->sourceref), SML_ELEMENT_SOURCEREF, error))
goto error_free_datastore;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DISPLAYNAME)) {
if (!_smlXmlParserGetString(parser, &(datastore->displayname), SML_ELEMENT_DISPLAYNAME, error))
goto error_free_datastore;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXGUIDSIZE)) {
if (!_smlXmlParserGetID(parser, &(datastore->maxGUIDSize), SML_ELEMENT_MAXGUIDSIZE, error))
goto error_free_datastore;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RXPREF)) {
if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_RXPREF, &(datastore->rxPrefContentType), &(datastore->rxPrefVersion), error))
goto error_free_datastore;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RX)) {
char *cttype = NULL;
char *verct = NULL;
if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_RX, &cttype, &verct, error))
goto error_free_datastore;
SmlDevInfContentType *ct = smlDevInfNewContentType(cttype, verct, error);
smlSafeCFree(&cttype);
if (verct)
smlSafeCFree(&verct);
if (!ct)
goto error_free_datastore;
smlDevInfDataStoreAddRx(datastore, ct);
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TXPREF)) {
if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_TXPREF, &(datastore->txPrefContentType), &(datastore->txPrefVersion), error))
goto error_free_datastore;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TX)) {
char *cttype = NULL;
char *verct = NULL;
if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_TX, &cttype, &verct, error))
goto error_free_datastore;
SmlDevInfContentType *ct = smlDevInfNewContentType(cttype, verct, error);
smlSafeCFree(&cttype);
if (verct)
smlSafeCFree(&verct);
if (!ct)
goto error_free_datastore;
smlDevInfDataStoreAddTx(datastore, ct);
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CTCAP)) {
if (!_smlXmlDevInfDataStoreParseCTCap(parser, devinf, error))
goto error_free_datastore;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCCAP)) {
if (!_smlXmlDevInfDataStoreParseSyncCap(parser, datastore, error))
goto error_free_datastore;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DSMEM)) {
if (!_smlXmlDevInfDataStoreParseDSMem(parser, datastore, error))
goto error_free_datastore;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORT_HIERARCHICAL_SYNC)) {
if (devinf->version < SML_DEVINF_VERSION_12) {
smlErrorSet(error, SML_ERROR_GENERIC, "SupportHierarchicalSync is only supported in OMA DS 1.2 DevInf and later.");
goto error;
}
datastore->supportsHierarchicalSync = TRUE;
if (!xmlTextReaderIsEmptyElement(parser->reader)) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "The closing element of %s is missing.", SML_ELEMENT_SUPPORT_HIERARCHICAL_SYNC);
goto error;
}
}
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "A DataStore within DevInf includes the unsupported element %s.", (char *)xmlTextReaderConstName(parser->reader));
goto error_free_datastore;
}
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error_free_datastore;
}
}
smlDevInfAddDataStore(devinf, datastore);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_datastore:
smlDevInfDataStoreUnref(datastore);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlDevInf *smlXmlDevInfParse(const char *data, unsigned int size, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, size, error);
CHECK_ERROR_REF
smlAssert(data);
smlAssert(size);
SmlDevInf *devinf = NULL;
//Fixme debug if
char *debugstr = smlPrintBinary(data, size);
smlTrace(TRACE_INTERNAL, "Xml devinf input: %s", debugstr);
smlSafeCFree(&debugstr);
SmlXmlParser *parser = smlTryMalloc0(sizeof(SmlXmlParser), error);
if (!parser)
goto error;
/* Create the new parser */
parser->reader = xmlReaderForMemory(data, size, "/", NULL, XML_PARSE_NONET | XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA | XML_PARSER_SUBST_ENTITIES);
if (!parser->reader) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new reader");
goto error;
}
xmlSubstituteEntitiesDefault(1);
/* Check the First Node (DevInf)*/
if (!_smlXmlParserExpectNode(parser, XML_NODE_START, FALSE, SML_ELEMENT_DEVINF, error))
goto error;
devinf = smlTryMalloc0(sizeof(SmlDevInf), error);
if (!devinf)
goto error;
smlDevInfRef(devinf);
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
while (1) {
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DEVINF) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
break;
} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The element %s is not a start node in DevInf.",
xmlTextReaderConstName(parser->reader));
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERDTD)) {
char *verdtd = NULL;
if (!_smlXmlParserGetString(parser, &verdtd, SML_ELEMENT_VERDTD, error))
goto error;
if (verdtd && !strcmp(verdtd, "1.1"))
devinf->version = SML_DEVINF_VERSION_11;
else if (verdtd && !strcmp(verdtd, "1.0"))
devinf->version = SML_DEVINF_VERSION_10;
else if (verdtd && !strcmp(verdtd, "1.2"))
devinf->version = SML_DEVINF_VERSION_12;
else {
smlSafeCFree(&verdtd);
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown devinf version");
goto error;
}
smlSafeCFree(&verdtd);
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAN)) {
if (!_smlXmlParserGetString(parser, &(devinf->manufacturer), SML_ELEMENT_MAN, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MOD)) {
if (!_smlXmlParserGetString(parser, &(devinf->model), SML_ELEMENT_MOD, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_OEM)) {
if (!_smlXmlParserGetString(parser, &(devinf->oem), SML_ELEMENT_OEM, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FWV)) {
if (!_smlXmlParserGetString(parser, &(devinf->firmwareVersion), SML_ELEMENT_FWV, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SWV)) {
if (!_smlXmlParserGetString(parser, &(devinf->softwareVersion), SML_ELEMENT_SWV, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_HWV)) {
if (!_smlXmlParserGetString(parser, &(devinf->hardwareVersion), SML_ELEMENT_HWV, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DEVID)) {
if (!_smlXmlParserGetString(parser, &(devinf->devid), SML_ELEMENT_DEVID, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DEVTYPE)) {
char *devtype = NULL;
if (!_smlXmlParserGetString(parser, &devtype, SML_ELEMENT_DEVTYPE, error))
goto error;
devinf->devtyp = smlDevInfDevTypeFromString(devtype, error);
smlSafeCFree(&devtype);
if (devinf->devtyp == SML_DEVINF_DEVTYPE_UNKNOWN)
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_UTC)) {
if (devinf->version == SML_DEVINF_VERSION_10) {
smlErrorSet(error, SML_ERROR_GENERIC, "Devinf 1.0 does not allow UTC");
goto error;
}
devinf->supportsUTC = TRUE;
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_UTC) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes2");
goto error;
}
}
continue;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORTLARGEOBJS)) {
if (devinf->version == SML_DEVINF_VERSION_10) {
smlErrorSet(error, SML_ERROR_GENERIC, "Devinf 1.0 does not allow large objects");
goto error;
}
devinf->supportsLargeObjs = TRUE;
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORTLARGEOBJS) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes2");
goto error;
}
}
continue;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORTNUMBEROFCHANGES)) {
if (devinf->version == SML_DEVINF_VERSION_10) {
smlErrorSet(error, SML_ERROR_GENERIC, "Devinf 1.0 does not allow number of changes");
goto error;
}
devinf->supportsNumberOfChanges = TRUE;
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORTNUMBEROFCHANGES) && \
xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes2");
goto error;
}
}
continue;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATASTORE)) {
if (!_smlXmlDevInfDataStoreParse(parser, devinf, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CTCAP)) {
/* This must be SyncML 1.0 or 1.1.
* OMA DM 1.2 puts CTCap into the datastores.
*/
if (!_smlXmlDevInfDataStoreParseCTCap(parser, devinf, error))
goto error;
} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_EXT)) {
/* Ignored for now */
smlTrace(TRACE_INTERNAL, "%s: Skipping ext node");
if (!_smlXmlSkipNode(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to skip ext node");
goto error;
}
continue;
} else {
smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node: %s", (char *)xmlTextReaderConstName(parser->reader));
goto error;
}
if (!_smlXmlParserStep(parser)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
goto error;
}
}
xmlFreeTextReader(parser->reader);
parser->reader = NULL;
smlSafeFree((gpointer *)&parser);
smlTrace(TRACE_EXIT, "%s", __func__);
return devinf;
error:
if (parser && parser->reader) {
xmlFreeTextReader(parser->reader);
parser->reader = NULL;
}
if (parser)
smlSafeFree((gpointer *)&parser);
if (devinf)
smlDevInfUnref(devinf);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlError::message
char * message
Definition: sml_error_internals.h:33
smlXmlParserStart
SmlBool smlXmlParserStart(SmlXmlParser *parser, const char *data, unsigned int size, SmlError **error)
Start the parsing.
Definition: sml_xml_parse.c:1915
SmlCred
Definition: sml_elements_internals.h:87
smlErrorPrint
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
SmlDevInfCTCap
Definition: sml_devinf_internals.h:102
SmlError::refCount
gint32 refCount
Definition: sml_error_internals.h:37
SmlAnchor
Definition: sml_elements_internals.h:35
SmlStatus
Definition: sml_command_internals.h:94
SmlXmlParser
Definition: sml_xml_parse_internals.h:47
SmlHeader
Definition: sml_elements_internals.h:73
smlPrintBinary
char * smlPrintBinary(const char *data, int len)
Used for printing binary data.
Definition: sml_support.c:252
SmlItem
Definition: sml_elements_internals.h:46
SmlChal
Definition: sml_elements_internals.h:96
smlErrorIsSet
SmlBool smlErrorIsSet(SmlError **error)
Checks if the error is set.
Definition: sml_error.c:268
SmlMapItem
Definition: sml_elements_internals.h:67
SmlCommand
Definition: sml_command_internals.h:73
SmlDevInfContentType
Definition: sml_devinf_internals.h:107
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
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
SmlXmlParser::version
SmlProtocolVersion version
Definition: sml_xml_parse_internals.h:49
SmlParserFunctions
Definition: sml_parse.h:41
SmlError
Represent an error.
Definition: sml_error_internals.h:29
SmlDevInfPropParam
Definition: sml_devinf_internals.h:83