sml_command.c
/*
* libsyncml - A syncml protocol implementation
* Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
* Copyright (C) 2008 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 "syncml.h"
#include "syncml_internals.h"
#include "sml_parse_internals.h"
#include "sml_command_internals.h"
#include "sml_session_internals.h"
#include "sml_elements_internals.h"
#include "sml_error_internals.h"
SmlCommandType smlCommandTypeFromString(const char *name, SmlError **error)
{
CHECK_ERROR_REF
if (!name)
return SML_COMMAND_TYPE_UNKNOWN;
if (!strcmp(name, SML_ELEMENT_ALERT)) {
return SML_COMMAND_TYPE_ALERT;
} else if (!strcmp(name, SML_ELEMENT_SYNC)) {
return SML_COMMAND_TYPE_SYNC;
} else if (!strcmp(name, SML_ELEMENT_PUT)) {
return SML_COMMAND_TYPE_PUT;
} else if (!strcmp(name, SML_ELEMENT_SYNCHDR)) {
return SML_COMMAND_TYPE_HEADER;
} else if (!strcmp(name, SML_ELEMENT_ADD)) {
return SML_COMMAND_TYPE_ADD;
} else if (!strcmp(name, SML_ELEMENT_REPLACE)) {
return SML_COMMAND_TYPE_REPLACE;
} else if (!strcmp(name, SML_ELEMENT_MAP)) {
return SML_COMMAND_TYPE_MAP;
} else if (!strcmp(name, SML_ELEMENT_DELETE)) {
return SML_COMMAND_TYPE_DELETE;
} else if (!strcmp(name, SML_ELEMENT_RESULTS)) {
return SML_COMMAND_TYPE_RESULTS;
} else if (!strcmp(name, SML_ELEMENT_GET)) {
return SML_COMMAND_TYPE_GET;
}
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown command name \"%s\"", name);
return SML_COMMAND_TYPE_UNKNOWN;
}
const char *smlCommandTypeToString(SmlCommandType type, SmlError **error)
{
CHECK_ERROR_REF
switch (type) {
case SML_COMMAND_TYPE_ALERT:
return SML_ELEMENT_ALERT;
case SML_COMMAND_TYPE_SYNC:
return SML_ELEMENT_SYNC;
case SML_COMMAND_TYPE_PUT:
return SML_ELEMENT_PUT;
case SML_COMMAND_TYPE_HEADER:
return SML_ELEMENT_SYNCHDR;
case SML_COMMAND_TYPE_ADD:
return SML_ELEMENT_ADD;
case SML_COMMAND_TYPE_REPLACE:
return SML_ELEMENT_REPLACE;
case SML_COMMAND_TYPE_DELETE:
return SML_ELEMENT_DELETE;
case SML_COMMAND_TYPE_MAP:
return SML_ELEMENT_MAP;
case SML_COMMAND_TYPE_GET:
return SML_ELEMENT_GET;
case SML_COMMAND_TYPE_RESULTS:
return SML_ELEMENT_RESULTS;
default:
;
}
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown command type \"%i\"", type);
return NULL;
}
SmlStatus *smlStatusNew(SmlErrorType data, unsigned int cmdref, unsigned int msgref, SmlLocation *sourceref, SmlLocation *targetref, SmlCommandType type, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%i, %i, %i, %p, %p, %i, %p)", __func__, data, cmdref, msgref, sourceref, targetref, type, error);
CHECK_ERROR_REF
SmlStatus *status = smlTryMalloc0(sizeof(SmlStatus), error);
if (!status)
goto error;
status->refCount = 1;
status->cmdRef = cmdref;
status->msgRef = msgref;
status->type = type;
if (data != SML_ERROR_UNKNOWN)
status->data = g_strdup_printf("%i", data);
if (sourceref) {
status->sourceRef = sourceref;
smlLocationRef(sourceref);
}
if (targetref) {
status->targetRef = targetref;
smlLocationRef(targetref);
}
smlTrace(TRACE_EXIT, "%s: %p", __func__, status);
return status;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlStatus *smlStatusRef(SmlStatus *status)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, status);
smlAssert(status);
g_atomic_int_inc(&(status->refCount));
smlTrace(TRACE_EXIT, "%s: New refcount: %i", __func__, status->refCount);
return status;
}
void smlStatusUnref(SmlStatus *status)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, status);
smlAssert(status);
if (g_atomic_int_dec_and_test(&(status->refCount))) {
smlTrace(TRACE_INTERNAL, "%s: Refcount == 0!", __func__);
if (status->sourceRef)
smlLocationUnref(status->sourceRef);
if (status->targetRef)
smlLocationUnref(status->targetRef);
if (status->data)
smlSafeCFree(&(status->data));
if (status->anchor)
smlAnchorFree(status->anchor);
if (status->item)
smlItemUnref(status->item);
smlSafeFree((gpointer *)&status);
}
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlErrorType smlStatusGetCode(SmlStatus *status)
{
smlAssert(status);
smlAssert(status->data);
return atoi(status->data);
}
SmlErrorClass smlStatusGetClass(SmlStatus *status)
{
smlAssert(status);
smlAssert(status->data);
return (atoi(status->data) / 100);
}
SmlCommand *smlStatusGetResult(SmlStatus *status)
{
if (status->type == SML_COMMAND_TYPE_RESULTS)
return status->result;
return NULL;
}
SmlBool smlStatusIsResult(SmlStatus *status)
{
return status->type == SML_COMMAND_TYPE_RESULTS ? TRUE : FALSE;
}
SmlCommand *smlCommandNew(SmlCommandType type, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%i, %p)", __func__, type, error);
CHECK_ERROR_REF
SmlCommand *cmd = smlTryMalloc0(sizeof(SmlCommand), error);
if (!cmd)
goto error;
cmd->refCount = 1;
cmd->type = type;
smlTrace(TRACE_EXIT, "%s: %p", __func__, cmd);
return cmd;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlStatus *smlCommandNewReply(const SmlCommand *cmd, SmlErrorType code, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, cmd, code, error);
CHECK_ERROR_REF
smlAssert(cmd);
SmlStatus *reply = smlStatusNew(code, cmd->cmdID, cmd->msgID, cmd->source, cmd->target, cmd->type, error);
if (!reply)
goto error;
switch (cmd->type) {
case SML_COMMAND_TYPE_ALERT:
if (cmd->private.alert.anchor) {
reply->anchor = smlAnchorNew(NULL, cmd->private.alert.anchor->next, error);
if (!reply->anchor)
goto error;
}
break;
default:
;
}
smlTrace(TRACE_EXIT, "%s: %p", __func__, reply);
return reply;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlCommand *smlCommandNewResult(SmlCommand *cmd, SmlLocation *source, char *data, unsigned int size, const char *contenttype, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %i, %s, %p)", __func__, cmd, source, data, size, VA_STRING(contenttype), error);
CHECK_ERROR_REF
smlAssert(cmd);
SmlCommand *result = smlCommandNew(SML_COMMAND_TYPE_RESULTS, error);
if (!result)
goto error;
result->private.results.status = smlStatusNew(SML_NO_ERROR, cmd->cmdID, cmd->msgID, cmd->source, cmd->target, SML_COMMAND_TYPE_RESULTS, error);
if (!result->private.results.status)
goto error_free_cmd;
result->private.results.status->item = smlItemNewForData(data, size, error);
smlSafeCFree(&data);
if (!result->private.results.status->item)
goto error_free_cmd;
result->private.results.status->item->contenttype = g_strdup(contenttype);
result->private.results.status->item->source = smlLocationClone(source, error);
if (!result->private.results.status->item->source)
goto error_free_cmd;
smlTrace(TRACE_EXIT, "%s: %p", __func__, result);
return result;
error_free_cmd:
smlCommandUnref(result);
error:
if (data)
smlSafeCFree(&data);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlCommand *smlCommandRef(SmlCommand *cmd)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, cmd);
smlAssert(cmd);
g_atomic_int_inc(&(cmd->refCount));
smlTrace(TRACE_EXIT, "%s: New refcount: %i", __func__, cmd->refCount);
return cmd;
}
void smlCommandUnref(SmlCommand *cmd)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, cmd);
smlAssert(cmd);
if (g_atomic_int_dec_and_test(&(cmd->refCount))) {
smlTrace(TRACE_INTERNAL, "%s: Refcount == 0!", __func__);
if (cmd->parent) {
cmd->parent->children = g_list_remove(cmd->parent->children, cmd);
smlCommandUnref(cmd->parent);
cmd->parent = NULL;
}
switch (cmd->type) {
case SML_COMMAND_TYPE_UNKNOWN:
case SML_COMMAND_TYPE_HEADER:
case SML_COMMAND_TYPE_SYNC:
break;
case SML_COMMAND_TYPE_ALERT:
if (cmd->private.alert.anchor)
smlAnchorFree(cmd->private.alert.anchor);
if (cmd->private.alert.contentType)
smlSafeCFree(&(cmd->private.alert.contentType));
break;
case SML_COMMAND_TYPE_PUT:
case SML_COMMAND_TYPE_GET:
if (cmd->private.access.type)
smlSafeCFree(&(cmd->private.access.type));
if (cmd->private.access.item)
smlItemUnref(cmd->private.access.item);
break;
case SML_COMMAND_TYPE_ADD:
case SML_COMMAND_TYPE_REPLACE:
case SML_COMMAND_TYPE_DELETE:
if (cmd->private.change.items)
{
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);
smlItemUnref(item);
}
g_list_free(cmd->private.change.items);
}
break;
case SML_COMMAND_TYPE_MAP:
while (cmd->private.map.items) {
SmlMapItem *item = cmd->private.map.items->data;
smlMapItemUnref(item);
cmd->private.map.items = g_list_delete_link(cmd->private.map.items, cmd->private.map.items);
}
break;
case SML_COMMAND_TYPE_RESULTS:
if (cmd->private.results.status)
smlStatusUnref(cmd->private.results.status);
break;
}
if (cmd->target)
smlLocationUnref(cmd->target);
if (cmd->source)
smlLocationUnref(cmd->source);
smlSafeFree((gpointer *)&cmd);
}
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlCommand *smlCommandNewChange(SmlChangeType type, const char *uid, const char *data, unsigned int size, const char *contenttype, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%i, %s, %p, %i, %s, %p)", __func__, type, VA_STRING(uid), data, size, VA_STRING(contenttype), error);
CHECK_ERROR_REF
SmlCommand *cmd = NULL;
switch (type) {
case SML_CHANGE_ADD:
cmd = smlCommandNew(SML_COMMAND_TYPE_ADD, error);
break;
case SML_CHANGE_REPLACE:
cmd = smlCommandNew(SML_COMMAND_TYPE_REPLACE, error);
break;
case SML_CHANGE_DELETE:
cmd = smlCommandNew(SML_COMMAND_TYPE_DELETE, error);
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown changetype");
}
if (!cmd)
goto error;
SmlItem *item = smlItemNewForData(data, size, error);
if (!item)
goto error_free_cmd;
cmd->private.change.items = g_list_append(NULL, item);
if (!cmd->private.change.items)
{
smlItemUnref(item);
goto error_free_cmd;
}
SmlLocation *loc = smlLocationNew(uid, NULL, error);
if (!loc)
goto error_free_cmd;
/* The usage of target and source depends on the role of the
* local peer. We cannot determine if the local peer is an
* OMA DS client or server. This function is only used by
* smlDsSessionQueueChange. So the correct usage of target
* and source must be ensured by the XML assembler.
*/
item->source = loc;
if (type != SML_CHANGE_ADD) {
/* If this is an Add command then nobody knows
* which ID the remote peer will be use for this item.
*/
smlLocationRef(loc);
item->target = loc;
}
item->contenttype = g_strdup(contenttype);
smlTrace(TRACE_EXIT, "%s: %p", __func__, cmd);
return cmd;
error_free_cmd:
smlCommandUnref(cmd);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlCommand *smlCommandNewPartialChange(SmlChangeType type, const char *uid, const char *data, unsigned int complete_size, unsigned int partial_size, const char *contenttype, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%i, %s, %p, %i, %i, %s, %p)", __func__, type, VA_STRING(uid), data, complete_size, partial_size, VA_STRING(contenttype), error);
CHECK_ERROR_REF
SmlCommand *cmd = NULL;
switch (type) {
case SML_CHANGE_ADD:
cmd = smlCommandNew(SML_COMMAND_TYPE_ADD, error);
break;
case SML_CHANGE_REPLACE:
cmd = smlCommandNew(SML_COMMAND_TYPE_REPLACE, error);
break;
case SML_CHANGE_DELETE:
cmd = smlCommandNew(SML_COMMAND_TYPE_DELETE, error);
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown changetype");
}
if (!cmd)
goto error;
cmd->size = complete_size;
SmlItem *item = smlItemNewForData(data, partial_size, error);
if (!item)
goto error_free_cmd;
cmd->private.change.items = g_list_append(NULL, item);
if (!cmd->private.change.items)
{
smlItemUnref(item);
goto error_free_cmd;
}
SmlLocation *loc = smlLocationNew(uid, NULL, error);
if (!loc)
goto error_free_cmd;
if (type != SML_CHANGE_ADD)
item->target = loc;
else
item->source = loc;
item->moreData = TRUE;
item->contenttype = g_strdup(contenttype);
smlTrace(TRACE_EXIT, "%s: %p", __func__, cmd);
return cmd;
error_free_cmd:
smlCommandUnref(cmd);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlCommand *smlCommandNewAlert(SmlAlertType type, SmlLocation *target, SmlLocation *source, const char *next, const char *last, const char *contenttype, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%i, %p, %p, %s, %s, %s, %p)", __func__, type, target, source, VA_STRING(next), VA_STRING(last), VA_STRING(contenttype), error);
CHECK_ERROR_REF
SmlCommand *cmd = smlCommandNew(SML_COMMAND_TYPE_ALERT, error);
if (!cmd)
goto error;
if (target) {
cmd->target = target;
smlLocationRef(target);
}
if (source) {
cmd->source = source;
smlLocationRef(source);
}
/* A synchronization anchor makes only sense if it is not an
* alert for the next part of an incomplete package (next
* message alert) or SAN.
*/
if (type != SML_ALERT_NEXT_MESSAGE && type != SML_ALERT_TWO_WAY_BY_SERVER) {
/* If this is a slow sync alert then it makes no sense
* to send the last sync anchor because a slow sync is
* only necessary if there was no earlier sync or
* something goes wrong since then which means there is
* no safe last anchor.
*/
if (type == SML_ALERT_SLOW_SYNC) {
if (last)
smlTrace(TRACE_INTERNAL,
"%s: removing last anchor (%s) from slow sync alert",
__func__, VA_STRING(last));
cmd->private.alert.anchor = smlAnchorNew(NULL, next, error);
} else
cmd->private.alert.anchor = smlAnchorNew(last, next, error);
if (!cmd->private.alert.anchor)
goto error;
}
cmd->private.alert.type = type;
cmd->private.alert.contentType = g_strdup(contenttype);
smlTrace(TRACE_EXIT, "%s: %p", __func__, cmd);
return cmd;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlCommand *smlCommandNewSync(SmlLocation *target, SmlLocation *source, unsigned int num_changes, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p)", __func__, target, source, num_changes, error);
CHECK_ERROR_REF
SmlCommand *cmd = smlCommandNew(SML_COMMAND_TYPE_SYNC, error);
if (!cmd)
goto error;
cmd->target = target;
smlLocationRef(target);
cmd->source = source;
smlLocationRef(source);
cmd->private.sync.numChanged = num_changes;
smlTrace(TRACE_EXIT, "%s: %p", __func__, cmd);
return cmd;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlCommand *smlCommandNewPut(SmlLocation *target, SmlLocation *source, const char *data, unsigned int size, const char *contenttype, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %i, %s, %p)", __func__, target, source, data, size, VA_STRING(contenttype), error);
CHECK_ERROR_REF
SmlCommand *cmd = smlCommandNew(SML_COMMAND_TYPE_PUT, error);
if (!cmd)
goto error;
cmd->private.access.item = smlItemNewForData(data, size, error);
if (!cmd->private.access.item)
goto error_free_cmd;
if (target) {
cmd->target = target;
smlLocationRef(target);
smlItemSetTarget(cmd->private.access.item, cmd->target);
}
if (source) {
cmd->source = source;
smlLocationRef(source);
smlItemSetSource(cmd->private.access.item, cmd->source);
}
cmd->private.access.item->contenttype = g_strdup(contenttype);
smlTrace(TRACE_EXIT, "%s: %p", __func__, cmd);
return cmd;
error_free_cmd:
smlCommandUnref(cmd);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlCommand *smlCommandNewGet(SmlLocation *target, const char *contenttype, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, target, VA_STRING(contenttype), error);
CHECK_ERROR_REF
smlAssert(target);
SmlCommand *cmd = smlCommandNew(SML_COMMAND_TYPE_GET, error);
if (!cmd)
goto error;
cmd->private.access.item = smlItemNew(0, error);
if (!cmd->private.access.item)
goto error_free_cmd;
cmd->target = target;
smlLocationRef(target);
smlItemSetTarget(cmd->private.access.item, cmd->target);
cmd->private.access.item->contenttype = g_strdup(contenttype);
smlTrace(TRACE_EXIT, "%s: %p", __func__, cmd);
return cmd;
error_free_cmd:
smlCommandUnref(cmd);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlCommand *smlCommandNewMap(SmlLocation *target, SmlLocation *source, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, target, source, error);
CHECK_ERROR_REF
SmlCommand *cmd = smlCommandNew(SML_COMMAND_TYPE_MAP, error);
if (!cmd)
goto error;
cmd->target = target;
smlLocationRef(target);
cmd->source = source;
smlLocationRef(source);
smlTrace(TRACE_EXIT, "%s: %p", __func__, cmd);
return cmd;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlBool smlCommandAddMapItem(SmlCommand *map, SmlMapItem *item, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, map, item, error);
CHECK_ERROR_REF
smlAssert(map);
smlAssert(map->type == SML_COMMAND_TYPE_MAP);
smlAssert(item);
smlMapItemRef(item);
map->private.map.items = g_list_append(map->private.map.items, item);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
SmlAlertType smlAlertTypeConvert(unsigned int id, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%u, %p)", __func__, id, error);
CHECK_ERROR_REF
if (id == 0 ||
id == 100 ||
(200 <= id && id <= 210) ||
(221 <= id && id <= 223))
{
SmlAlertType result = (SmlAlertType) id;
smlTrace(TRACE_EXIT, "%s - %u", __func__, result);
return result;
} else {
smlErrorSet(error, SML_ERROR_GENERIC,
"The unknown alert code %u was detected.", id);
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return SML_ALERT_UNKNOWN;
}
}
smlErrorPrint
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
SmlError::type
SmlErrorType type
Definition: sml_error_internals.h:31
SmlError::refCount
gint32 refCount
Definition: sml_error_internals.h:37
SmlStatus
Definition: sml_command_internals.h:94
SmlItem
Definition: sml_elements_internals.h:46
SmlMapItem
Definition: sml_elements_internals.h:67
SmlCommand
Definition: sml_command_internals.h:73
smlTrace
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
smlCommandNewPartialChange
SmlCommand * smlCommandNewPartialChange(SmlChangeType type, const char *uid, const char *data, unsigned int complete_size, unsigned int partial_size, const char *contenttype, SmlError **error)
Definition: sml_command.c:433
SmlLocation
Definition: sml_elements_internals.h:40
smlTryMalloc0
void * smlTryMalloc0(long n_bytes, SmlError **error)
Safely mallocs.
Definition: sml_support.c:335
smlErrorSet
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
SmlError
Represent an error.
Definition: sml_error_internals.h:29