sml_ds_server.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 "sml_ds_server.h"
#include <libsyncml/syncml_internals.h>
#include "sml_ds_server_internals.h"
#include <libsyncml/sml_error_internals.h>
#include <libsyncml/sml_manager_internals.h>
#include <libsyncml/sml_session_internals.h>
#include <libsyncml/sml_command_internals.h>
#include <libsyncml/sml_elements_internals.h>
static SmlWriteContext *_write_context_find(SmlDsSession *dsession, const char *uid, SmlChangeType type)
{
smlTrace(TRACE_ENTRY, "%s(%p, %s, %i)", __func__, dsession, VA_STRING(uid), type);
g_mutex_lock(dsession->pendingMapsLock);
GList *c = NULL;
for (c = dsession->pendingMaps; c; c = c->next) {
SmlWriteContext *ctx = c->data;
if (!strcmp(uid, ctx->uid) && ctx->type == type) {
g_mutex_unlock(dsession->pendingMapsLock);
smlTrace(TRACE_EXIT, "%s: %p", __func__, ctx);
return ctx;
}
}
g_mutex_unlock(dsession->pendingMapsLock);
smlTrace(TRACE_EXIT_ERROR, "%s: Not found", __func__);
return NULL;
}
static void _write_context_free(SmlWriteContext *ctx)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, ctx);
if (ctx->status)
smlStatusUnref(ctx->status);
if (ctx->uid)
smlSafeCFree(&(ctx->uid));
if (ctx->newuid)
smlSafeCFree(&(ctx->newuid));
smlSafeFree((gpointer *)&ctx);
smlTrace(TRACE_EXIT, "%s", __func__);
}
static void _write_context_dispatch(SmlDsSession *dsession, SmlWriteContext *ctx)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, dsession, ctx);
if (!ctx->status) {
smlTrace(TRACE_EXIT, "%s: No status yet", __func__);
return;
}
smlTrace(TRACE_INTERNAL, "%s: Dispatching: uid %s, Type %i, newuid %s, result %i", __func__, VA_STRING(ctx->uid), ctx->type, VA_STRING(ctx->newuid), smlStatusGetCode(ctx->status));
if (ctx->type == SML_CHANGE_ADD &&
smlStatusGetClass(ctx->status) == SML_ERRORCLASS_SUCCESS &&
!dsession->recvMappingCallback &&
!ctx->newuid &&
dsession->server->servertype == SML_DS_SERVER)
{
smlTrace(TRACE_EXIT, "%s: No mapitem yet", __func__);
return;
}
ctx->callback(dsession, ctx->status, ctx->newuid, ctx->userdata);
g_mutex_lock(dsession->pendingMapsLock);
dsession->pendingMaps = g_list_remove(dsession->pendingMaps, ctx);
g_mutex_unlock(dsession->pendingMapsLock);
if (ctx->type == SML_CHANGE_ADD &&
smlStatusGetClass(ctx->status) == SML_ERRORCLASS_SUCCESS &&
dsession->recvEventCallback &&
!dsession->pendingMaps)
{
/* FIXME: I'm sure that this event is buggy.
* FIXME: What is the semantic of this event?
* Now there are no more MapItems ...
* do some fancy callback. Some applications will love it ;)
*/
dsession->recvEventCallback(dsession, SML_DS_EVENT_COMMITEDCHANGES, dsession->recvEventCallbackUserdata);
smlTrace(TRACE_INTERNAL, "%s: recvEventCallback commited changes callback", __func__);
}
_write_context_free(ctx);
smlTrace(TRACE_EXIT, "%s", __func__);
}
static void _alert_reply(SmlSession *session, SmlStatus *status, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
SmlDsSession *dsession = userdata;
if (dsession->sentAlertCallback)
dsession->sentAlertCallback(session, status, dsession->sentAlertCallbackUserdata);
dsession->sentAlertCallback = NULL;
dsession->sentAlertCallbackUserdata = NULL;
smlTrace(TRACE_EXIT, "%s", __func__);
}
static void _sync_reply(SmlSession *session, SmlStatus *status, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
SmlDsSession *dsession = userdata;
if (dsession->sentSyncCallback)
dsession->sentSyncCallback(session, status, dsession->sentSyncCallbackUserdata);
dsession->sentSyncCallback = NULL;
dsession->sentSyncCallbackUserdata = NULL;
// Now there are no more MapItems ... do some fancy callback. Some applications will love it ;)
if (dsession->recvEventCallback && !dsession->pendingMaps) {
dsession->recvEventCallback(dsession, SML_DS_EVENT_COMMITEDCHANGES, dsession->recvEventCallbackUserdata);
smlTrace(TRACE_INTERNAL, "%s: recvEventCallback commited changes callback", __func__);
}
smlTrace(TRACE_EXIT, "%s", __func__);
}
static void _change_reply(SmlSession *session, SmlStatus *status, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
smlAssert(session);
smlAssert(status);
SmlWriteContext *ctx = userdata;
SmlDsSession *dsession = ctx->session;
/* Please see smlChangeAssemble in sml_xml_parser.c for
* a more detailed description on the checked behaviour.
* If the received command is an Add or the remote peer
* is an OMA DS client then the remote peer must send a
* Source. Otherwise a Target must be sent by the remote
* peer. We only check the required behaviour and not
* the optional one.
*/
if (status->type == SML_COMMAND_TYPE_ADD ||
session->sessionType == SML_SESSION_TYPE_CLIENT) {
/* FIXME: How do we handle remote peers which are clients
* FIXME: and only support replace? The assembler will
* FIXME: send a replace command with a target instead of
* FIXME: an add command with a source.
*/
if (!status->sourceRef) {
smlTrace(TRACE_EXIT_ERROR, "%s: Received add status or change reply from an OMA DS server without sourceRef", __func__);
return;
}
} else {
if (!status->targetRef) {
smlTrace(TRACE_EXIT_ERROR, "%s: Received delete or modify status or change reply from an OMA DS client without targetRef", __func__);
return;
}
}
ctx->status = status;
smlStatusRef(status);
_write_context_dispatch(dsession, ctx);
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlDsServer *smlDsServerNew(const char *type, SmlLocation *location, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%s, %p, %p)", __func__, VA_STRING(type), location, error);
smlAssert(location);
CHECK_ERROR_REF
SmlDsServer *server = smlTryMalloc0(sizeof(SmlDsServer), error);
if (!server)
goto error;
server->location = location;
smlLocationRef(location);
server->contenttype = g_strdup(type);
server->servertype = SML_DS_SERVER;
smlTrace(TRACE_EXIT, "%s: %p", __func__, server);
return server;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlDsServer *smlDsClientNew(const char *type, SmlLocation *location, SmlLocation *target, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%s, %p, %p, %p)", __func__, VA_STRING(type), location, target, error);
smlAssert(location);
smlAssert(target);
CHECK_ERROR_REF
SmlDsServer *server = smlTryMalloc0(sizeof(SmlDsServer), error);
if (!server)
goto error;
server->location = location;
smlLocationRef(location);
server->target = target;
smlLocationRef(target);
server->contenttype = g_strdup(type);
server->servertype = SML_DS_CLIENT;
smlTrace(TRACE_EXIT, "%s: %p", __func__, server);
return server;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlDsServerType smlDsServerGetServerType(SmlDsServer *server)
{
smlAssert(server);
return server->servertype;
}
void smlDsServerFree(SmlDsServer *server)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, server);
smlAssert(server);
if (server->location)
smlLocationUnref(server->location);
if (server->target)
smlLocationUnref(server->target);
if (server->contenttype)
smlSafeCFree(&(server->contenttype));
smlSafeFree((gpointer *)&server);
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlDsServerSetConnectCallback(SmlDsServer *server, SmlDsSessionConnectCb callback, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, callback, userdata);
smlAssert(server);
server->connectCallback = callback;
server->connectCallbackUserdata = userdata;
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlDsServerSetSanCallback(SmlDsServer *server, SmlDsServerSanCb callback, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, callback, userdata);
smlAssert(server);
smlAssert(server->servertype == SML_DS_CLIENT);
server->sanCallback = callback;
server->sanCallbackUserdata = userdata;
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlDsServerSetSanSessionCallback(SmlDsServer *server, SmlDsServerSanSessionCb callback, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, callback, userdata);
smlAssert(server);
smlAssert(server->servertype == SML_DS_CLIENT);
server->sanSessionCallback = callback;
server->sanSessionCallbackUserdata = userdata;
smlTrace(TRACE_EXIT, "%s", __func__);
}
const char *smlDsServerGetLocation(SmlDsServer *server)
{
smlAssert(server);
if (server->location)
return server->location->locURI;
return NULL;
}
const char *smlDsServerGetContentType(SmlDsServer *server)
{
smlAssert(server);
return server->contenttype;
}
SmlBool smlDsServerAddSan(SmlDsServer *server, SmlNotification *san, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, san, error);
smlAssert(server);
smlAssert(san);
CHECK_ERROR_REF
if (!smlNotificationNewAlert(san, SML_ALERT_TWO_WAY_BY_SERVER, server->contenttype, smlLocationGetURI(server->location), error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlDsSession *smlDsServerRecvAlert(SmlDsServer *server, SmlSession *session, SmlCommand *cmd)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, session, cmd);
SmlError *error = NULL;
SmlDsSession *dsession = smlDsSessionNew(server, session, &error);
if (!dsession)
goto error;
smlDsSessionRecvAlert(session, cmd, dsession);
if (server->connectCallback)
server->connectCallback(dsession, server->connectCallbackUserdata);
smlDsSessionUnref(dsession);
smlTrace(TRACE_EXIT, "%s", __func__);
return dsession;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
return NULL;
}
SmlDsSession *smlDsServerSendAlert(SmlDsServer *server, SmlSession *session, SmlAlertType type, const char *last, const char *next, SmlStatusReplyCb callback, void *userdata, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %s, %s, %p)", __func__, server, session, type, VA_STRING(last), VA_STRING(next), error);
smlAssert(server);
smlAssert(session);
CHECK_ERROR_REF
SmlDsSession *dsession = smlDsSessionNew(server, session, error);
if (!dsession)
goto error;
if (server->manager) {
if (!smlManagerObjectRegister(server->manager, SML_COMMAND_TYPE_SYNC, session, server->location, NULL, NULL, smlDsSessionRecvSync, smlDsSessionRecvChange, dsession, error))
goto error_free_dsession;
if (!smlManagerObjectRegister(server->manager, SML_COMMAND_TYPE_MAP, session, server->location, NULL, NULL, smlDsSessionRecvMap, NULL, dsession, error))
goto error_free_dsession;
if (!smlManagerObjectRegister(server->manager, SML_COMMAND_TYPE_ALERT, session, server->location, NULL, NULL, smlDsSessionRecvAlert, NULL, dsession, error))
goto error_free_dsession;
}
if (!smlDsSessionSendAlert(dsession, type, last, next, callback, userdata, error))
goto error_free_dsession;
smlTrace(TRACE_EXIT, "%s", __func__);
return dsession;
error_free_dsession:
smlSafeFree((gpointer *)&dsession);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlDsSession *smlDsSessionNew(SmlDsServer *server, SmlSession *session, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, session, error);
smlAssert(server);
CHECK_ERROR_REF
SmlDsSession *dsession = smlTryMalloc0(sizeof(SmlDsSession), error);
if (!dsession)
goto error;
dsession->server = server;
dsession->session = session;
dsession->write_lock = g_mutex_new();
dsession->lock = g_mutex_new();
dsession->syncReply = SML_ERROR_UNKNOWN;
dsession->refCount = 1;
if (server->servertype == SML_DS_CLIENT) {
dsession->target = server->target;
smlLocationRef(dsession->target);
}
dsession->location = server->location;
smlLocationRef(dsession->location);
dsession->pendingMapsLock = g_mutex_new();
smlTrace(TRACE_EXIT, "%s: %p", __func__, dsession);
return dsession;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlDsSession *smlDsSessionRef(SmlDsSession *dsession)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, dsession);
smlAssert(dsession);
g_atomic_int_inc(&(dsession->refCount));
smlTrace(TRACE_EXIT, "%s: New refcount: %i", __func__, dsession->refCount);
return dsession;
}
void smlDsSessionUnref(SmlDsSession *dsession)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, dsession);
smlAssert(dsession);
if (g_atomic_int_dec_and_test(&(dsession->refCount))) {
smlTrace(TRACE_INTERNAL, "%s: Refcount == 0!", __func__);
if (dsession->target)
smlLocationUnref(dsession->target);
if (dsession->location)
smlLocationUnref(dsession->location);
if (dsession->alertCommand)
smlCommandUnref(dsession->alertCommand);
while (dsession->recvSync) {
SmlCommand *cmd = dsession->recvSync->data;
smlCommandUnref(cmd);
dsession->recvSync = g_list_delete_link(dsession->recvSync, dsession->recvSync);
}
while (dsession->recvChanges) {
SmlCommand *cmd = dsession->recvChanges->data;
smlCommandUnref(cmd);
dsession->recvChanges = g_list_delete_link(dsession->recvChanges, dsession->recvChanges);
}
if (dsession->syncCommand)
smlCommandUnref(dsession->syncCommand);
if (!g_mutex_trylock(dsession->pendingMapsLock)) {
smlTrace(TRACE_ERROR,
"%s: somebody still uses this object", __func__);
g_mutex_lock(dsession->pendingMapsLock);
}
while (dsession->pendingMaps) {
SmlWriteContext *ctx = dsession->pendingMaps->data;
_write_context_free(ctx);
dsession->pendingMaps = g_list_delete_link(dsession->pendingMaps, dsession->pendingMaps);
}
g_mutex_unlock(dsession->pendingMapsLock);
g_mutex_free(dsession->pendingMapsLock);
while (dsession->mapItems) {
SmlMapItem *item = dsession->mapItems->data;
smlMapItemUnref(item);
dsession->mapItems = g_list_delete_link(dsession->mapItems, dsession->mapItems);
}
if (dsession->lock)
g_mutex_free(dsession->lock);
if (dsession->write_lock)
g_mutex_free(dsession->write_lock);
smlSafeFree((gpointer *)&dsession);
}
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlDsSessionDispatch(SmlDsSession *dsession)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, dsession);
SmlError *error = NULL;
SmlStatus *reply = NULL;
/* A dispatch function should never block on a
* locked resource. This is like a busy wait
* because the thread is not available for this
* time which can block the whole system if two
* dispatchers use the same thread.
*/
if (!g_mutex_trylock(dsession->lock))
{
smlTrace(TRACE_EXIT, "%s - DsSession is already locked", __func__);
return;
}
smlTrace(TRACE_INTERNAL, "%s - locked DsSession successfully", __func__);
if (dsession->alertCommand && dsession->recvAlertCallback) {
smlTrace(TRACE_INTERNAL, "%s: Dispatching alert", __func__);
SmlErrorType type = SML_NO_ERROR;
if (!dsession->recvAlertCallback(dsession, dsession->alertCommand->private.alert.type, dsession->alertCommand->private.alert.anchor->last, dsession->alertCommand->private.alert.anchor->next, dsession->recvAlertCallbackUserdata))
type = SML_ERROR_REQUIRE_REFRESH;
dsession->recvAlertCallback = NULL;
reply = smlCommandNewReply(dsession->alertCommand, type, &error);
if (!reply)
goto error;
if (!smlSessionSendReply(dsession->session, reply, &error))
goto error;
smlStatusUnref(reply);
smlCommandUnref(dsession->alertCommand);
dsession->alertCommand = NULL;
/* unlock the final event at the manager
*
* If this is called from a test case then the datastore
* is sometimes not registered at a SmlManager.
*/
if (dsession->server->manager && dsession->finalLock)
{
dsession->finalLock = FALSE;
smlManagerSessionFinalLockUnref(dsession->server->manager, dsession->session);
}
} else if (dsession->recvSync && dsession->recvSyncCallback) {
smlTrace(TRACE_INTERNAL, "%s: Dispatching sync", __func__);
dsession->recvSyncCallback(dsession, ((SmlCommand *)(dsession->recvSync->data))->private.sync.numChanged, dsession->recvSyncCallbackUserdata);
dsession->recvSyncCallback = NULL;
while (dsession->recvSync) {
SmlCommand *cmd = dsession->recvSync->data;
smlTrace(TRACE_INTERNAL, "%s: answering sync command with cmdRef %i and msgRef %i", __func__, cmd->cmdID, cmd->msgID);
reply = smlCommandNewReply(cmd, SML_NO_ERROR, &error);
if (!reply)
goto error;
if (!smlSessionSendReply(dsession->session, reply, &error))
goto error;
smlStatusUnref(reply);
smlCommandUnref(cmd);
dsession->recvSync = g_list_delete_link(dsession->recvSync, dsession->recvSync);
}
dsession->syncReply = SML_NO_ERROR;
if (!dsession->recvChanges) {
/* There are no changes. */
/* ... do some fancy callback. Some applications will love it ;) */
if (dsession->recvEventCallback) {
dsession->recvEventCallback(dsession, SML_DS_EVENT_GOTCHANGES, dsession->recvEventCallbackUserdata);
smlTrace(TRACE_INTERNAL, "%s: recvEventCallback no changes in recvSync callback", __func__);
}
/* unlock the final event at the manager
*
* If this is called from a test case then the datastore
* is sometimes not registered at a SmlManager.
*/
if (dsession->server->manager && dsession->finalLock)
{
dsession->finalLock = FALSE;
smlManagerSessionFinalLockUnref(dsession->server->manager, dsession->session);
}
smlTrace(TRACE_INTERNAL, "%s: final handling done", __func__);
}
} else if (dsession->recvChanges && dsession->changesCallback) {
smlTrace(TRACE_INTERNAL, "%s: Dispatching changes", __func__);
while (dsession->recvChanges) {
SmlCommand *cmd = dsession->recvChanges->data;
if (cmd) {
if (!cmd->private.change.items ||
!g_list_length(cmd->private.change.items))
{
smlErrorSet(&error, SML_ERROR_GENERIC, "No items found in command.");
goto error;
}
/* All items handled at once because libsyncml
* only supports one status code for all items.
* So one failed item means that the complete
* command is aborted.
*/
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)
{
smlErrorSet(&error, SML_ERROR_GENERIC,
"Item %i of the command's item list is NULL.", i);
goto error;
}
/* We'd rather use the target of the change since it already the mapped
* uid (if we are a client for example). If it is not given we use the
* source uri. This has then to be translated by the sync engine of course */
if ((!item->source && !item->target)) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"Cannot determine UID because source an target are missing on item %i.", i);
goto error;
}
char *data = NULL;
unsigned int size = 0;
if (!smlItemStealData(item, &data, &size, &error))
goto error;
if (!dsession->changesCallback(dsession, cmd->private.change.type, item->target ? item->target->locURI : item->source->locURI, data, size, item->contenttype, dsession->changesCallbackUserdata, &error))
goto error;
}
if (cmd->private.change.type == SML_CHANGE_ADD)
reply = smlCommandNewReply(cmd, SML_ITEM_ADDED, &error);
else
reply = smlCommandNewReply(cmd, SML_NO_ERROR, &error);
if (!reply)
goto error;
if (!smlSessionSendReply(dsession->session, reply, &error))
goto error;
smlStatusUnref(reply);
smlCommandUnref(cmd);
}
dsession->recvChanges = g_list_delete_link(dsession->recvChanges, dsession->recvChanges);
}
// Now there are no more changes... do some fancy callback. Some applications will love it ;)
if (dsession->recvEventCallback) {
dsession->recvEventCallback(dsession, SML_DS_EVENT_GOTCHANGES, dsession->recvEventCallbackUserdata);
smlTrace(TRACE_INTERNAL, "%s: recvEventCallback all changes sent in recvChanges callback", __func__);
}
/* unlock the final event at the manager
*
* If this is called from a test case then the datastore
* is sometimes not registered at a SmlManager.
*/
if (dsession->server->manager)
{
dsession->finalLock = FALSE;
smlManagerSessionFinalLockUnref(dsession->server->manager, dsession->session);
}
else
smlTrace(TRACE_INTERNAL, "%s - no manager so should be a test", __func__);
} else {
smlTrace(TRACE_INTERNAL, "%s: recvChanges: %p changesCallback: %p", __func__, dsession->recvChanges, dsession->changesCallback);
}
smlTrace(TRACE_EXIT, "%s()", __func__);
g_mutex_unlock(dsession->lock);
return;
error:
if (reply)
smlStatusUnref(reply);
g_mutex_unlock(dsession->lock);
smlTrace(TRACE_EXIT_ERROR, "%s: Unable to dispatch: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
}
SmlBool smlDsSessionCheck(SmlDsSession *dsession)
{
if ((dsession->alertCommand && dsession->recvAlertCallback) || \
(dsession->recvSync && dsession->recvSyncCallback) || \
(dsession->recvChanges && dsession->changesCallback))
return TRUE;
return FALSE;
}
void smlDsSessionRecvAlert(SmlSession *session, SmlCommand *cmd, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
SmlDsSession *dsession = userdata;
SmlError *error = NULL;
smlAssert(dsession->location);
g_mutex_lock(dsession->lock);
if (!cmd->target || !cmd->source) {
SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_BAD_REQUEST, &error);
if (!reply)
goto error;
if (!smlSessionSendReply(session, reply, &error)) {
smlStatusUnref(reply);
goto error;
}
smlStatusUnref(reply);
smlTrace(TRACE_EXIT, "%s: Alert had no target or source", __func__);
return;
}
if (!smlLocationCompare(NULL, dsession->location, NULL, cmd->target)) {
SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_NOT_FOUND, &error);
if (!reply)
goto error;
if (!smlSessionSendReply(session, reply, &error)) {
smlStatusUnref(reply);
goto error;
}
smlStatusUnref(reply);
smlTrace(TRACE_EXIT, "%s: Alert does not match our location", __func__);
return;
}
smlCommandRef(cmd);
if (!dsession->target) {
dsession->target = cmd->source;
smlLocationRef(cmd->source);
}
dsession->alertCommand = cmd;
/* Lock the final event at the manager.
*
* If this is called from a test case then the datastore
* is sometimes not registered at a SmlManager.
*/
if (dsession->server->manager)
{
smlManagerSessionFinalLockRef(dsession->server->manager, session);
dsession->finalLock = TRUE;
}
else
smlTrace(TRACE_INTERNAL, "%s - no manager so should be a test", __func__);
g_mutex_unlock(dsession->lock);
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error:
g_mutex_unlock(dsession->lock);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
return;
}
void smlDsSessionRecvSync(SmlSession *session, SmlCommand *cmd, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
SmlDsSession *dsession = userdata;
SmlError *error = NULL;
/* Create a write lock on the complete DsSession object to avoid race
* conditions. If the write lock is already present then this is the
* end of the sync block and the write lock must only be removed.
*/
if (g_mutex_trylock(dsession->write_lock))
{
/* write lock succeeded so lock the complete DsSession */
g_mutex_lock(dsession->lock);
smlTrace(TRACE_INTERNAL, "%s - write lock enabled", __func__);
/* Lock the final event at the manager.
*
* If this is called from a test case then the datastore
* is sometimes not registered at a SmlManager.
*/
if (dsession->server->manager)
{
smlManagerSessionFinalLockRef(dsession->server->manager, session);
dsession->finalLock = TRUE;
dsession->emptySync = TRUE;
}
else
smlTrace(TRACE_INTERNAL, "%s - no manager so should be a test", __func__);
} else {
/* detected an old write lock so this is the closing sync call */
if (dsession->finalLock && dsession->emptySync)
{
smlTrace(TRACE_INTERNAL, "%s - empty sync of moreData ?", __func__);
dsession->finalLock = FALSE;
smlManagerSessionFinalLockUnref(dsession->server->manager, session);
}
g_mutex_unlock(dsession->lock);
g_mutex_unlock(dsession->write_lock);
smlTrace(TRACE_EXIT, "%s - removed write lock", __func__);
return;
}
if (dsession->syncReply == SML_ERROR_UNKNOWN) {
smlTrace(TRACE_INTERNAL, "%s: Storing sync command with cmdRef %i and msgRef %i", __func__, cmd->cmdID, cmd->msgID);
smlCommandRef(cmd);
dsession->recvSync = g_list_append(dsession->recvSync, cmd);
} else {
smlTrace(TRACE_INTERNAL, "%s: Using stored sync reply on cmd with cmdRef %i and msgRef %i", __func__, cmd->cmdID, cmd->msgID);
SmlStatus *reply = smlCommandNewReply(cmd, dsession->syncReply, &error);
if (!reply)
goto error;
if (!smlSessionSendReply(dsession->session, reply, &error))
goto error;
smlStatusUnref(reply);
}
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error:
g_mutex_unlock(dsession->lock);
g_mutex_unlock(dsession->write_lock);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
}
void smlDsSessionRecvChange(SmlSession *session, SmlCommand *cmd, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
SmlDsSession *dsession = userdata;
/* It is not necessary to lock the DsSesion object
* because RecvSync already enabled a global write_lock.
*/
dsession->recvChanges = g_list_append(dsession->recvChanges, cmd);
smlCommandRef(cmd);
/* This is important for fast and consistent final signalling. */
dsession->emptySync = FALSE;
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlDsSessionRecvMap(SmlSession *session, SmlCommand *cmd, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
SmlDsSession *dsession = userdata;
SmlError *error = NULL;
g_mutex_lock(dsession->lock);
SmlStatus *reply = smlCommandNewReply(cmd, SML_NO_ERROR, &error);
if (!reply)
goto error;
if (!smlSessionSendReply(session, reply, &error))
goto error;
smlStatusUnref(reply);
GList *m = NULL;
for (m = cmd->private.map.items; m; m = m->next) {
SmlMapItem *item = m->data;
if (dsession->recvMappingCallback) {
dsession->recvMappingCallback(
dsession,
item->target,
item->source,
dsession->recvMappingCallbackUserdata);
} else {
SmlWriteContext *ctx = _write_context_find(dsession, item->target->locURI, SML_CHANGE_ADD);
if (ctx) {
ctx->newuid = g_strdup(item->source->locURI);
_write_context_dispatch(dsession, ctx);
} else {
smlTrace(TRACE_ERROR, "%s: Unknown map ... %s => %s",
__func__,
VA_STRING(item->target->locURI),
VA_STRING(item->source->locURI));
}
}
}
g_mutex_unlock(dsession->lock);
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error:
g_mutex_unlock(dsession->lock);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
}
void smlDsSessionGetAlert(SmlDsSession *dsession, SmlDsSessionAlertCb callback, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, dsession, callback, userdata);
smlAssert(dsession);
smlAssert(callback);
dsession->recvAlertCallback = callback;
dsession->recvAlertCallbackUserdata = userdata;
smlDsSessionDispatch(dsession);
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlBool smlDsSessionSendAlert(SmlDsSession *dsession, SmlAlertType type, const char *last, const char *next, SmlStatusReplyCb callback, void *userdata, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %s, %p)", __func__, dsession, type, VA_STRING(last), VA_STRING(next), error);
smlAssert(dsession);
CHECK_ERROR_REF
SmlCommand *alert = smlCommandNewAlert(type, dsession->target, dsession->location, next, last, NULL, error);
if (!alert)
goto error;
dsession->sentAlertCallback = callback;
dsession->sentAlertCallbackUserdata = userdata;
if (!smlSessionSendCommand(dsession->session, alert, NULL, _alert_reply, dsession, error))
goto error;
smlCommandUnref(alert);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
void smlDsSessionGetMapping(
SmlDsSession *dsession,
SmlDsSessionMapCb mapCallback,
void *userdata)
{
smlTrace(TRACE_ENTRY, "%s", __func__);
dsession->recvMappingCallback = mapCallback;
dsession->recvMappingCallbackUserdata = userdata;
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlDsSessionGetChanges(SmlDsSession *dsession, SmlDsSessionChangesCb chgCallback, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, dsession, chgCallback, userdata);
smlAssert(dsession);
smlAssert(chgCallback);
dsession->changesCallback = chgCallback;
dsession->changesCallbackUserdata = userdata;
smlDsSessionDispatch(dsession);
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlDsSessionGetSync(SmlDsSession *dsession, SmlDsSessionSyncCb syncCallback, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, dsession, syncCallback, userdata);
smlAssert(dsession);
smlAssert(syncCallback);
dsession->recvSyncCallback = syncCallback;
dsession->recvSyncCallbackUserdata = userdata;
smlDsSessionDispatch(dsession);
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlDsSessionGetEvent(SmlDsSession *dsession, SmlDsSessionEventCb eventCallback, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, dsession, eventCallback, userdata);
smlAssert(dsession);
smlAssert(eventCallback);
dsession->recvEventCallback = eventCallback;
dsession->recvEventCallbackUserdata = userdata;
smlDsSessionDispatch(dsession);
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlBool smlDsSessionSendSync(SmlDsSession *dsession, unsigned int num_changes, SmlStatusReplyCb callback, void *userdata, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p)", __func__, dsession, num_changes, callback, userdata, error);
smlAssert(dsession);
CHECK_ERROR_REF
if (dsession->syncCommand) {
smlErrorSet(error, SML_ERROR_GENERIC, "There already was a sync command started");
goto error;
}
dsession->sentSyncCallback = callback;
dsession->sentSyncCallbackUserdata = userdata;
dsession->syncCommand = smlCommandNewSync(dsession->target, dsession->location, num_changes, error);
if (!dsession->syncCommand)
goto error;
if (!smlSessionStartCommand(dsession->session, dsession->syncCommand, NULL, _sync_reply, dsession, error))
goto error;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
//Send a change to the remote side
SmlBool smlDsSessionQueueChange(SmlDsSession *dsession, SmlChangeType type, const char *uid, const char *data, unsigned int size, const char *contenttype, SmlDsSessionWriteCb callback, void *userdata, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %p, %i, %s, %p, %p, %p)", __func__, dsession, type, VA_STRING(uid), data, size, VA_STRING(contenttype), callback, userdata, error);
smlAssert(dsession);
CHECK_ERROR_REF
if (!dsession->syncCommand) {
smlErrorSet(error, SML_ERROR_GENERIC, "You have to start a sync command first");
goto error;
}
SmlCommand *cmd = smlCommandNewChange(type, uid, data, size, contenttype, error);
if (!cmd)
goto error;
SmlWriteContext *ctx = smlTryMalloc0(sizeof(SmlWriteContext), error);
if (!ctx)
goto error_free_cmd;
ctx->callback = callback;
ctx->userdata = userdata;
ctx->uid = g_strdup(uid);
ctx->type = type;
ctx->session = dsession;
g_mutex_lock(dsession->pendingMapsLock);
dsession->pendingMaps = g_list_append(dsession->pendingMaps, ctx);
g_mutex_unlock(dsession->pendingMapsLock);
if (!smlSessionSendCommand(dsession->session, cmd, dsession->syncCommand, _change_reply, ctx, error))
goto error_free_ctx;
smlCommandUnref(cmd);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_ctx:
smlSafeCFree(&(ctx->uid));
smlSafeFree((gpointer *)&ctx);
error_free_cmd:
smlCommandUnref(cmd);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlDsSessionCloseSync(SmlDsSession *dsession, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, dsession, error);
smlAssert(dsession);
CHECK_ERROR_REF
if (!dsession->syncCommand) {
smlErrorSet(error, SML_ERROR_GENERIC, "There already was a sync command started");
goto error;
}
if (!smlSessionEndCommand(dsession->session, NULL, error))
goto error;
smlCommandUnref(dsession->syncCommand);
dsession->syncCommand = NULL;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlDsSessionQueueMap(SmlDsSession *dsession, const char *uid, const char *newuid, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %p)", __func__, dsession, VA_STRING(uid), VA_STRING(newuid), error);
smlAssert(dsession);
CHECK_ERROR_REF
SmlMapItem *item = smlMapItemNew(uid, newuid, error);
if (!item)
goto error;
dsession->mapItems = g_list_append(dsession->mapItems, item);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlDsSessionCloseMap(SmlDsSession *dsession, SmlStatusReplyCb callback, void *userdata, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, dsession, callback, userdata, error);
smlAssert(dsession);
CHECK_ERROR_REF
if (!dsession->mapItems) {
smlTrace(TRACE_EXIT, "%s: No mapitems", __func__);
return TRUE;
}
SmlCommand *cmd = smlCommandNewMap(dsession->server->target, dsession->server->location, error);
if (!cmd)
goto error;
while (dsession->mapItems) {
SmlMapItem *item = dsession->mapItems->data;
if (!smlCommandAddMapItem(cmd, item, error))
goto error_free_cmd;
smlMapItemUnref(item);
dsession->mapItems = g_list_remove(dsession->mapItems, item);
}
if (!smlSessionSendCommand(dsession->session, cmd, NULL, callback, userdata, error))
goto error_free_cmd;
smlCommandUnref(cmd);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_cmd:
smlCommandUnref(cmd);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
const char *smlDsSessionGetLocation(SmlDsSession *dsession)
{
smlAssert(dsession);
return smlDsServerGetLocation(dsession->server);
}
SmlDsServer *smlDsSessionGetServer(SmlDsSession *dsession)
{
smlAssert(dsession);
return dsession->server;
}
const char *smlDsSessionGetContentType(SmlDsSession *dsession)
{
smlAssert(dsession);
return smlDsServerGetContentType(dsession->server);
}
static void _recv_manager_alert(SmlSession *session, SmlCommand *cmd, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
SmlDsServer *server = userdata;
SmlError *error = NULL;
SmlDsSession *dsession = smlDsServerRecvAlert(server, session, cmd);
if (!dsession)
goto error;
if (!smlManagerObjectRegister(server->manager, SML_COMMAND_TYPE_SYNC, session, server->location, NULL, NULL, smlDsSessionRecvSync, smlDsSessionRecvChange, dsession, &error))
goto error_free_dsession;
if (!smlManagerObjectRegister(server->manager, SML_COMMAND_TYPE_MAP, session, server->location, NULL, NULL, smlDsSessionRecvMap, NULL, dsession, &error))
goto error_free_dsession;
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error_free_dsession:
smlSafeFree((gpointer *)&dsession);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
if (error)
smlErrorDeref(&error);
}
static void _recv_manager_san(SmlSession *session, SmlCommand *cmd, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
SmlDsServer *server = userdata;
SmlError *error = NULL;
/* ensure that the session is in client mode */
session->sessionType = SML_SESSION_TYPE_CLIENT;
/* fix target */
smlLocationUnref(server->target);
server->target = cmd->source;
smlLocationRef(server->target);
/* handle the SAN */
if (server->sanSessionCallback) {
SmlErrorType type = server->sanSessionCallback(
server,
session,
cmd->private.alert.type,
server->sanSessionCallbackUserdata);
SmlStatus *reply = smlCommandNewReply(cmd, type, &error);
if (!reply)
goto error;
if (!smlSessionSendReply(session, reply, &error))
goto error;
smlStatusUnref(reply);
} else if (server->sanCallback) {
SmlErrorType type = server->sanCallback(server, cmd->private.alert.type, server->sanCallbackUserdata);
SmlStatus *reply = smlCommandNewReply(cmd, type, &error);
if (!reply)
goto error;
if (!smlSessionSendReply(session, reply, &error))
goto error;
smlStatusUnref(reply);
} else {
smlTrace(TRACE_INTERNAL, "%s: SAN ignored", __func__);
SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_NOT_IMPLEMENTED, &error);
if (!reply)
goto error;
if (!smlSessionSendReply(session, reply, &error))
goto error;
smlStatusUnref(reply);
}
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
if (error)
smlErrorDeref(&error);
}
SmlBool smlDsServerRegister(SmlDsServer *server, SmlManager *manager, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, manager, error);
smlAssert(server);
smlAssert(manager);
CHECK_ERROR_REF
if (!smlManagerObjectRegister(manager, SML_COMMAND_TYPE_ALERT, NULL, server->location, NULL, NULL, _recv_manager_alert, NULL, server, error))
goto error;
if (!smlManagerObjectRegister(manager, SML_COMMAND_TYPE_ALERT, NULL, NULL, NULL,server->contenttype, _recv_manager_san, NULL, server, error))
goto error;
server->manager = manager;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlLocation *smlDsSessionGetTarget(SmlDsSession *dsession)
{
smlAssert(dsession);
return dsession->target;
}
SmlNotification
Definition: sml_notification_internals.h:33
smlDsSessionGetChanges
void smlDsSessionGetChanges(SmlDsSession *dsession, SmlDsSessionChangesCb chgCallback, void *userdata)
Gets a already received sync command.
Definition: sml_ds_server.c:1022
smlSessionSendReply
SmlBool smlSessionSendReply(SmlSession *session, SmlStatus *status, SmlError **error)
Sends a reply to a command.
Definition: sml_session.c:2536
SmlDsSession::sentSyncCallback
SmlStatusReplyCb sentSyncCallback
Definition: sml_ds_server_internals.h:79
smlErrorPrint
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
SmlDsSession::sentAlertCallback
SmlStatusReplyCb sentAlertCallback
Definition: sml_ds_server_internals.h:60
smlItemStealData
SmlBool smlItemStealData(SmlItem *item, char **data, unsigned int *size, SmlError **error)
Definition: sml_elements.c:470
SmlDsSession::recvEventCallback
SmlDsSessionEventCb recvEventCallback
Definition: sml_ds_server_internals.h:83
SmlDsSession::recvSyncCallback
SmlDsSessionSyncCb recvSyncCallback
Definition: sml_ds_server_internals.h:64
SmlManager
Definition: sml_manager_internals.h:35
smlSessionEndCommand
SmlBool smlSessionEndCommand(SmlSession *session, SmlCommand *parent, SmlError **error)
ends a command
Definition: sml_session.c:2495
SmlStatus
Definition: sml_command_internals.h:94
smlDsSessionSendAlert
SmlBool smlDsSessionSendAlert(SmlDsSession *dsession, SmlAlertType type, const char *last, const char *next, SmlStatusReplyCb callback, void *userdata, SmlError **error)
Sends the alert to the remote side.
Definition: sml_ds_server.c:972
SmlItem
Definition: sml_elements_internals.h:46
smlDsSessionGetAlert
void smlDsSessionGetAlert(SmlDsSession *dsession, SmlDsSessionAlertCb callback, void *userdata)
Gets a already received alert.
Definition: sml_ds_server.c:946
SmlDsServer
Definition: sml_ds_server_internals.h:101
SmlDsSession::recvMappingCallback
SmlDsSessionMapCb recvMappingCallback
Definition: sml_ds_server_internals.h:94
smlDsSessionSendSync
SmlBool smlDsSessionSendSync(SmlDsSession *dsession, unsigned int num_changes, SmlStatusReplyCb callback, void *userdata, SmlError **error)
Start the sync command to send to the other side.
Definition: sml_ds_server.c:1077
SmlDsServer::connectCallback
SmlDsSessionConnectCb connectCallback
Definition: sml_ds_server_internals.h:110
SmlDsSession::syncCommand
SmlCommand * syncCommand
Definition: sml_ds_server_internals.h:87
smlDsSessionCloseMap
SmlBool smlDsSessionCloseMap(SmlDsSession *dsession, SmlStatusReplyCb callback, void *userdata, SmlError **error)
Closes the map command.
Definition: sml_ds_server.c:1214
SmlMapItem
Definition: sml_elements_internals.h:67
smlDsServerSetConnectCallback
void smlDsServerSetConnectCallback(SmlDsServer *server, SmlDsSessionConnectCb callback, void *userdata)
Registers a callback that will get called once a client connects.
Definition: sml_ds_server.c:279
SmlSession
Definition: sml_session_internals.h:27
SmlCommand
Definition: sml_command_internals.h:73
smlSessionSendCommand
SmlBool smlSessionSendCommand(SmlSession *session, SmlCommand *cmd, SmlCommand *parent, SmlStatusReplyCb callback, void *userdata, SmlError **error)
Sends a command with a parent.
Definition: sml_session.c:2422
SmlDsSession
Definition: sml_ds_server_internals.h:45
SmlDsServer::target
SmlLocation * target
Definition: sml_ds_server_internals.h:104
smlTrace
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
SmlLocation
Definition: sml_elements_internals.h:40
smlSessionStartCommand
SmlBool smlSessionStartCommand(SmlSession *session, SmlCommand *cmd, SmlCommand *parent, SmlStatusReplyCb callback, void *userdata, SmlError **error)
starts a command with a parent
Definition: sml_session.c:2454
smlDsSessionCloseSync
SmlBool smlDsSessionCloseSync(SmlDsSession *dsession, SmlError **error)
Closes the sync command.
Definition: sml_ds_server.c:1160
smlManagerObjectRegister
SmlBool smlManagerObjectRegister(SmlManager *manager, SmlCommandType type, SmlSession *session, SmlLocation *location, SmlLocation *source, const char *contentType, SmlCommandCb callback, SmlCommandCb childCallback, void *userdata, SmlError **error)
Register an object with a session.
Definition: sml_manager.c:926
smlTryMalloc0
void * smlTryMalloc0(long n_bytes, SmlError **error)
Safely mallocs.
Definition: sml_support.c:335
SmlWriteContext
Definition: sml_ds_server_internals.h:35
smlErrorSet
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
SmlDsSession::recvAlertCallback
SmlDsSessionAlertCb recvAlertCallback
Definition: sml_ds_server_internals.h:55
SmlDsSession::recvSync
GList * recvSync
Definition: sml_ds_server_internals.h:72
SmlError
Represent an error.
Definition: sml_error_internals.h:29