sml_manager.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 "syncml.h"
#include "syncml_internals.h"
#include "sml_error_internals.h"
#include "sml_manager_internals.h"
#include "sml_transport_internals.h"
#include "sml_command_internals.h"
#include "sml_elements_internals.h"
#include "sml_session_internals.h"
typedef struct managerSession {
SmlLink *link;
SmlSession *session;
unsigned int finalLock;
SmlTransport *transport;
GList *objects;
} managerSession;
managerSession *_smlManagerGetManagerSession(SmlManager *manager, SmlSession *session);
SmlObject *smlManagerObjectFindInternal(
SmlManager *manager,
SmlSession *session,
SmlCommandType type,
SmlLocation *target,
SmlLocation *source,
const char* contentType);
static managerSession *_manager_session_find(SmlManager *manager, SmlSession *session)
{
smlAssert(session);
smlAssert(manager);
GList *s = NULL;
for (s = manager->sessions; s; s = s->next) {
managerSession *sess = s->data;
if (sess->session == session)
return sess;
}
return NULL;
}
static void _manager_session_free(managerSession *sess)
{
smlAssert(sess);
smlSessionUnref(sess->session);
if (sess->link)
smlLinkDeref(sess->link);
while (sess->objects) {
SmlObject *object = sess->objects->data;
smlManagerObjectFree(object);
sess->objects = g_list_delete_link(sess->objects, sess->objects);
}
smlSafeFree((gpointer *)&sess);
}
static gboolean _manager_prepare_internal(GSource *source, gint *timeout_)
{
smlTrace(TRACE_INTERNAL, "%s(%p, %p)", __func__, source, timeout_);
*timeout_ = 1;
return FALSE;
}
static gboolean _manager_check_internal(GSource *source)
{
SmlManager *manager = *((SmlManager **)(source + 1));
GList *s = NULL;
for (s = manager->sessions; s; s = s->next) {
managerSession *session = s->data;
if (smlSessionCheck(session->session))
return TRUE;
}
return FALSE;
}
static gboolean _manager_dispatch_internal(GSource *source, GSourceFunc callback, gpointer user_data)
{
smlTrace(TRACE_INTERNAL, "%s(%p, %p, %p)", __func__, source, callback, user_data);
int max = 100;
SmlManager *manager = user_data;
GList *s = NULL;
for (s = manager->sessions; s; s = s->next) {
managerSession *session = s->data;
while (smlSessionCheck(session->session) && max) {
/* 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 (smlSessionTryLock(session->session))
{
smlSessionDispatch(session->session);
smlSessionUnlock(session->session);
}
max--;
}
}
return TRUE;
}
static void _smlManagerSendEvent(SmlManager *manager, SmlManagerEventType type, SmlSession *session, SmlCommand *command, SmlCommand *parent, SmlError *error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p, %p)", __func__, manager, type, session, command, parent, error);
SmlError *lerr = NULL;
SmlManagerEvent *event = smlTryMalloc0(sizeof(SmlManagerEvent), &lerr);
if (event) {
event->type = type;
if (session) {
event->session = session;
smlSessionRef(session);
} else {
event->session = NULL;
}
if (command) {
event->command = command;
smlCommandRef(command);
} else {
event->command = NULL;
}
if (parent) {
event->parent = parent;
smlCommandRef(parent);
} else {
event->parent = NULL;
}
if (error) {
event->error = error;
smlErrorRef(&error);
}
smlQueueSend(manager->userEventQueue, event);
} else {
/* error handling is not possible here :( */
g_error("%s", smlErrorPrint(&lerr));
smlErrorDeref(&lerr);
}
smlTrace(TRACE_EXIT, "%s", __func__);
}
void _smlManagerEventFree(SmlManagerEvent *event)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, event);
smlAssert(event);
if (event->session)
smlSessionUnref(event->session);
if (event->command)
smlCommandUnref(event->command);
if (event->parent)
smlCommandUnref(event->parent);
if (event->error)
smlErrorDeref(&(event->error));
smlSafeFree((gpointer *)&event);
smlTrace(TRACE_EXIT, "%s", __func__);
}
/* If we return FALSE in this function, the current request will get
* aborted */
static SmlBool _smlManagerDataHandler(SmlTransport *tsp, SmlLink *link_, SmlTransportEventType type, SmlTransportData *data, SmlError *error, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p, %p, %p)", __func__, tsp, link_, type, data, error, userdata);
smlAssert(userdata);
SmlManager *manager = userdata;
SmlParser *parser = NULL;
SmlHeader *header = NULL;
SmlCred *cred = NULL;
SmlSession *session = NULL;
SmlError *locerror = NULL;
switch (type) {
case SML_TRANSPORT_EVENT_CONNECT_DONE:
/* Pass the connect through */
_smlManagerSendEvent(manager, SML_MANAGER_CONNECT_DONE, NULL, NULL, NULL, NULL);
break;
case SML_TRANSPORT_EVENT_DISCONNECT_DONE:
/* Pass the disconnect through */
_smlManagerSendEvent(manager, SML_MANAGER_DISCONNECT_DONE, NULL, NULL, NULL, NULL);
break;
case SML_TRANSPORT_EVENT_DATA:
parser = smlParserNew(data->type, 0, &locerror);
if (!parser)
goto session_error;
/* add manager to parser for OMA DS 1.2 SAN handling */
smlParserSetManager(parser, manager);
/* start to parse the data */
if (!smlParserStart(parser, data->data, data->size, &locerror))
goto error_free_parser;
/* Get the header of the message*/
header = NULL;
cred = NULL;
if (!smlParserGetHeader(parser, &header, &cred, &locerror))
goto error_free_parser;
/* Find the session if available otherwise register it as new */
session = smlManagerSessionFind(manager, header->sessionID);
if (!session) {
/* If we use the target and source from the header of the received message
* then we MUST interchange source and target */
SmlSessionType sessionType;
switch(smlTransportGetType(tsp))
{
case SML_TRANSPORT_OBEX_CLIENT:
/* this can happen if the remote
* client creates a new session ID.
*/
sessionType = SML_SESSION_TYPE_SERVER;
break;
case SML_TRANSPORT_OBEX_SERVER:
sessionType = SML_SESSION_TYPE_CLIENT;
break;
case SML_TRANSPORT_HTTP_SERVER:
sessionType = SML_SESSION_TYPE_SERVER;
break;
default:
smlErrorSet(&locerror,
SML_ERROR_NOT_IMPLEMENTED,
"New session was unexpectedly received for transport %d.",
smlTransportGetType(tsp));
goto error_free_header;
break;
}
if (!(session = smlSessionNew(
sessionType, data->type,
header->version, header->protocol,
header->source, header->target,
header->sessionID, 1, &locerror)))
goto error_free_header;
if (link_)
{
char *responseURI = smlTransportGetResponseURI(link_, session, &locerror);
if (!responseURI && locerror)
goto error_free_header;
if (responseURI)
{
if (!smlSessionSetResponseURI(session, responseURI, &locerror))
{
smlSafeCFree(&responseURI);
goto error_free_header;
}
smlSafeCFree(&responseURI);
}
}
if (!smlManagerSessionAdd(manager, session, link_, &locerror))
goto error_free_header;
smlSessionUnref(session);
} else {
/* SECURITY: let's check ResponseURI if supported
* SECURITY: this is important to avoid replay attacks
*/
if (link_) {
char *responseUri = smlTransportGetResponseURI(link_, session, &locerror);
if (!responseUri && locerror) {
goto error_free_header;
}
if (responseUri)
smlSafeCFree(&responseUri);
}
}
/* This function is called by the tranport layer which
* runs in an own thread. Therefore we must lock the
* according session because otherwise we can get into
* trouble with parallel access to a SmlSession which is
* not safe against parallel use by multiple threads.
*/
smlSessionLock(session);
/* Find the manager session so that we can update the link. We
* have to do this since each time a transport with links receives
* a new data packets, it might create a new link (connectionless
* protocols). So we have to update the link to be able to answer
* the data */
GList *s;
for (s = manager->sessions; s; s = s->next) {
managerSession *sess = s->data;
if (sess->session == session) {
if (sess->link)
smlLinkDeref(sess->link);
sess->link = link_;
if (link_)
smlLinkRef(sess->link);
break;
}
}
/* Now check if the header is valid etc */
if (!smlSessionReceiveHeader(session, header, &locerror))
goto error_free_header;
/* Then check if we are allowed to authenticate */
if (!smlManagerDispatchHeader(manager, session, header, cred, &locerror))
goto error_free_header;
smlHeaderFree(header);
if (cred)
smlCredUnref(cred);
/* Now let the session handle the commands etc */
if (!smlSessionReceiveBody(session, parser, &locerror))
goto error_free_parser;
/* Free the parser */
smlParserFree(parser);
/* make the session accessible for others */
smlSessionUnlock(session);
break;
case SML_TRANSPORT_EVENT_ERROR:
/* Pass the error through */
smlErrorDuplicate(&locerror, &error);
goto transport_error;
break;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_header:
if (header)
smlHeaderFree(header);
if (cred)
smlCredUnref(cred);
error_free_parser:
smlParserFree(parser);
if (session)
smlSessionUnlock(session);
session_error:
if (session == NULL && link_ && type == SML_TRANSPORT_EVENT_DATA) {
/* If there is no session and a link then this means
* that there is a failing server connection.
* It is a good idea to close the connection because
* the session has no chance to signal a clean
* disconnect to the link.
*/
SmlError *herror = NULL;
if (!smlTransportDisconnect(manager->transport, link_, &herror)) {
smlTrace(TRACE_ERROR, "%s: Disconnect on error failed. %s",
smlErrorPrint(&herror));
smlErrorDeref(&herror);
}
}
_smlManagerSendEvent(manager, SML_MANAGER_SESSION_ERROR, session, NULL, NULL, locerror);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&locerror));
smlErrorDeref(&locerror);
return FALSE;
transport_error:
_smlManagerSendEvent(manager, SML_MANAGER_TRANSPORT_ERROR, NULL, NULL, NULL, locerror);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&locerror));
smlErrorDeref(&locerror);
return FALSE;
}
SmlManager *smlManagerNew(SmlTransport *tsp, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
smlAssert(tsp);
CHECK_ERROR_REF
SmlManager *manager = smlTryMalloc0(sizeof(SmlManager), error);
if (!manager)
goto error;
smlTransportSetEventCallback(tsp, _smlManagerDataHandler, manager);
manager->transport = tsp;
manager->running_mutex = g_mutex_new();
manager->running = g_cond_new();
manager->userEventQueue = smlQueueNew(error);
if (!manager->userEventQueue)
goto error_free_manager;
smlTrace(TRACE_EXIT, "%s", __func__);
return manager;
error_free_manager:
smlSafeFree((gpointer *)&manager);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
void smlManagerFree(SmlManager *manager)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, manager);
smlAssert(manager);
/* stop automatic session dispatch environment if necessary */
if (manager->context)
smlManagerStop(manager);
/* Deactivate the event callback of the transport layer
* because the manager is no longer available
*/
if (manager->transport)
smlTransportSetEventCallback(manager->transport, NULL, NULL);
while (manager->sessions) {
managerSession *sess = manager->sessions->data;
_manager_session_free(sess);
manager->sessions = g_list_delete_link(manager->sessions, manager->sessions);
}
while (manager->objects) {
SmlObject *object = manager->objects->data;
smlManagerObjectFree(object);
manager->objects = g_list_remove(manager->objects, object);
}
if (manager->userEventQueue) {
SmlManagerEvent *event = NULL;
while ((event = smlQueueTryPop(manager->userEventQueue)))
_smlManagerEventFree(event);
smlQueueFree(manager->userEventQueue);
manager->userEventQueue = NULL;
}
g_cond_free(manager->running);
g_mutex_free(manager->running_mutex);
smlSafeFree((gpointer *)&manager);
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlManagerSetEventCallback(SmlManager *manager, SmlManagerEventCb callback, void *userdata)
{
smlAssert(manager);
smlAssert(callback);
manager->eventCallback = callback;
manager->eventCallbackUserdata = userdata;
}
SmlBool smlManagerStart(SmlManager *manager, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, manager, error);
smlAssert(manager);
CHECK_ERROR_REF
manager->context = g_main_context_new();
if (!manager->context) {
smlErrorSet(error, SML_ERROR_GENERIC,
"Cannot create new main context.");
goto error;
}
manager->functions = smlTryMalloc0(sizeof(GSourceFuncs), error);
if (!manager->functions)
goto error;
manager->functions->prepare = _manager_prepare_internal;
manager->functions->check = _manager_check_internal;
manager->functions->dispatch = _manager_dispatch_internal;
manager->functions->finalize = NULL;
manager->thread = smlThreadNew(manager->context, error);
if (!manager->thread)
goto error;
smlThreadStart(manager->thread);
manager->source = g_source_new(manager->functions, sizeof(GSource) + sizeof(SmlManager *));
SmlManager **managerptr = (SmlManager **)(manager->source + 1);
*managerptr = manager;
g_source_set_callback(manager->source, NULL, manager, NULL);
g_source_attach(manager->source, manager->context);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
if (manager->context) {
g_main_context_unref(manager->context);
manager->context = NULL;
}
if (manager->functions)
smlSafeFree((gpointer *)&(manager->functions));
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
void smlManagerStop(SmlManager *manager)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, manager);
smlAssert(manager);
smlAssert(manager->thread);
/* cleanup automatic session dispatch environment
* the order is highly important
*/
/* stop thread */
smlThreadStop(manager->thread);
smlThreadFree(manager->thread);
manager->thread = NULL;
/* detach source */
g_source_unref(manager->source);
manager->source = NULL;
/* free context */
g_main_context_unref(manager->context);
manager->context = NULL;
/* free functions */
smlSafeFree((gpointer *)&(manager->functions));
manager->functions = NULL;
/* close all open connections */
GList *sessionItem = manager->sessions;
while (sessionItem) {
managerSession *sess = sessionItem->data;
/* disconnect if necessary
*
* only do this if:
* 1. the transport layer is still connected
* and
* 2.1. this is a client connection or the server itself
* or
* 2.2. this is an active server connection
*/
SmlError *error = NULL;
if (manager->transport->connected &&
(!sess->link || sess->link->link_data) &&
!smlTransportDisconnect(manager->transport, sess->link, &error)) {
g_warning("Errors from the transport layer " \
"cannot be handled while freeing the manager. %s",
smlErrorPrint(&error));
smlErrorDeref(&error);
}
sessionItem = g_list_next(sessionItem);
}
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlManagerRun(SmlManager *manager)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, manager);
smlAssert(manager);
g_mutex_lock(manager->running_mutex);
g_cond_wait(manager->running, manager->running_mutex);
g_mutex_unlock(manager->running_mutex);
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlManagerQuit(SmlManager *manager)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, manager);
smlAssert(manager);
g_mutex_lock(manager->running_mutex);
g_cond_signal(manager->running);
g_mutex_unlock(manager->running_mutex);
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlSession *smlManagerSessionFind(SmlManager *manager, const char *sessionID)
{
smlTrace(TRACE_ENTRY, "%s(%p, %s)", __func__, manager, VA_STRING(sessionID));
GList *s = NULL;
for (s = manager->sessions; s; s = s->next) {
managerSession *session = s->data;
if (!strcmp(smlSessionGetSessionID(session->session), sessionID)) {
smlTrace(TRACE_EXIT, "%s: FOUND %p", __func__, session);
return session->session;
}
}
smlTrace(TRACE_EXIT, "%s: NOT FOUND", __func__);
return NULL;
}
void smlManagerDispatch(SmlManager *manager)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, manager);
smlAssert(manager);
SmlManagerEvent *event = NULL;
while ((event = smlQueueTryPop(manager->userEventQueue))) {
smlTrace(TRACE_INTERNAL, "%s: type ::= %d", __func__, event->type);
/* Check that this is not a locked final event. */
if (event->session != NULL &&
event->type == SML_MANAGER_SESSION_FINAL)
{
managerSession *sess = _smlManagerGetManagerSession(manager, event->session);
if (sess && sess->finalLock)
{
smlTrace(TRACE_INTERNAL, "%s - locked final (%i)",
__func__, sess->finalLock);
smlQueuePushHead(manager->userEventQueue, event);
/* It is necessary to stop the dispatching here.
* The events MUST BE dispatched in the same
* order like they were received. If the while
* loop does not break here then the loop tries
* to dispatch the locked event again and again.
*/
break;
}
}
/* Execute the event callback. */
smlAssert(manager->eventCallback);
manager->eventCallback(manager, event->type, event->session, event->error, manager->eventCallbackUserdata);
_smlManagerEventFree(event);
}
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlBool smlManagerCheck(SmlManager *manager)
{
return smlQueueCheck(manager->userEventQueue);
}
static void _event_callback(SmlSession *session, SmlSessionEventType type, SmlCommand *command, SmlCommand *parent, SmlStatus *reply, SmlError *error, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p, %p, %p)", __func__, session, type, command, parent, reply, error, userdata);
SmlManager *manager = userdata;
SmlError *locerror = NULL;
switch (type) {
case SML_SESSION_EVENT_ESTABLISHED:
/* Pass the established through */
_smlManagerSendEvent(manager, SML_MANAGER_SESSION_ESTABLISHED, session, NULL, NULL, NULL);
break;
case SML_SESSION_EVENT_HEADER_REPLY:
break;
case SML_SESSION_EVENT_RESPONSE_URI:
if (!smlTransportSetResponseURI(
manager->transport,
smlLocationGetURI(smlSessionGetTarget(session)),
&locerror))
{
_smlManagerSendEvent(manager, SML_MANAGER_SESSION_ERROR, session, NULL, NULL, locerror);
goto error;
}
break;
case SML_SESSION_EVENT_COMMAND_START:
case SML_SESSION_EVENT_COMMAND_END:
if (!smlManagerDispatchCommand(manager, session, command, &locerror)) {
_smlManagerSendEvent(manager, SML_MANAGER_SESSION_WARNING, session, NULL, NULL, locerror);
goto error;
}
break;
case SML_SESSION_EVENT_CHILD_COMMAND:
if (!smlManagerDispatchChildCommand(manager, session, parent, command, &locerror)) {
_smlManagerSendEvent(manager, SML_MANAGER_SESSION_WARNING, session, NULL, NULL, locerror);
goto error;
}
break;
case SML_SESSION_EVENT_FINAL:
/* Pass the final through */
_smlManagerSendEvent(manager, SML_MANAGER_SESSION_FINAL, session, NULL, NULL, NULL);
break;
case SML_SESSION_EVENT_END:
/* Pass the end through */
_smlManagerSendEvent(manager, SML_MANAGER_SESSION_END, session, NULL, NULL, NULL);
break;
case SML_SESSION_EVENT_FLUSH:
/* Pass the flush through */
_smlManagerSendEvent(manager, SML_MANAGER_SESSION_FLUSH, session, NULL, NULL, NULL);
break;
case SML_SESSION_EVENT_ERROR:
/* Pass the error through */
smlErrorDuplicate(&locerror, &error);
_smlManagerSendEvent(manager, SML_MANAGER_SESSION_ERROR, session, NULL, NULL, locerror);
goto error;
break;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&locerror));
smlErrorDeref(&locerror);
}
static void _data_send_callback(SmlSession *session, SmlTransportData *data, void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, data, userdata);
managerSession *sess = userdata;
/* The transport layer is able to signal the error via an event. */
SmlError *error = NULL;
if (!smlTransportSend(sess->transport, sess->link, data, &error))
{
smlTrace(TRACE_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
}
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlBool smlManagerSessionAdd(SmlManager *manager, SmlSession *session, SmlLink *link_, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, manager, session, link_, error);
CHECK_ERROR_REF
smlSessionRef(session);
if (smlSessionGetSessionID(session)) {
//Check if sessionID exists
smlTrace(TRACE_INTERNAL, "Checking if session ID %s already exists", smlSessionGetSessionID(session));
if (smlManagerSessionFind(manager, smlSessionGetSessionID(session))) {
smlErrorSet(error, SML_ERROR_GENERIC, "Session ID already exists");
goto error;
}
} else {
char *lastid = smlManagerGetNewSessionID(manager);
smlSessionSetSessionID(session, lastid);
smlSafeCFree(&lastid);
}
managerSession *sess = smlTryMalloc0(sizeof(managerSession), error);
if (!sess)
goto error;
sess->session = session;
sess->finalLock = 0;
if (link_) {
sess->link = link_;
smlLinkRef(link_);
}
sess->transport = manager->transport;
manager->sessions = g_list_append(manager->sessions, sess);
/* prepare large object support */
if (0 < manager->localMaxMsgSize &&
(
manager->localMaxMsgSize < smlSessionGetLocalMaxMsgSize(session) ||
smlSessionGetLocalMaxMsgSize(session) <= 0
))
smlSessionSetLocalMaxMsgSize(session, manager->localMaxMsgSize);
if (0 < manager->localMaxObjSize &&
(
manager->localMaxObjSize < smlSessionGetLocalMaxObjSize(session) ||
smlSessionGetLocalMaxObjSize(session) <= 0
))
smlSessionSetLocalMaxObjSize(session, manager->localMaxObjSize);
if (smlSessionGetLocalMaxObjSize(session) &&
smlSessionGetLocalMaxMsgSize(session))
smlSessionUseLargeObjects(session, TRUE);
else
smlSessionUseLargeObjects(session, FALSE);
smlSessionSetEventCallback(session, _event_callback, manager);
smlSessionSetDataCallback(session, _data_send_callback, sess);
_smlManagerSendEvent(manager, SML_MANAGER_SESSION_NEW, session, NULL, NULL, NULL);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlSessionUnref(session);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
managerSession *_smlManagerGetManagerSession(SmlManager *manager, SmlSession *session)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, manager, session);
GList *s;
for (s = manager->sessions; s; s = s->next) {
managerSession *sess = s->data;
if (sess->session == session) {
smlTrace(TRACE_EXIT, "%s - %p", __func__, sess);
return sess;
}
}
smlTrace(TRACE_EXIT, "%s: Not Found", __func__);
return NULL;
}
void smlManagerSessionRemove(SmlManager *manager, SmlSession *session)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, manager, session);
managerSession *sess = _smlManagerGetManagerSession(manager, session);
if (sess) {
manager->sessions = g_list_remove(manager->sessions, sess);
_manager_session_free(sess);
smlTrace(TRACE_EXIT, "%s", __func__);
} else {
smlTrace(TRACE_EXIT, "%s: Not Found", __func__);
}
}
void smlManagerSessionFinalLockRef(SmlManager *manager, SmlSession *session)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, manager, session);
managerSession *sess = _smlManagerGetManagerSession(manager, session);
if (sess) {
sess->finalLock++;
smlTrace(TRACE_EXIT, "%s", __func__);
} else {
smlTrace(TRACE_EXIT, "%s: Not Found", __func__);
}
}
void smlManagerSessionFinalLockUnref(SmlManager *manager, SmlSession *session)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, manager, session);
managerSession *sess = _smlManagerGetManagerSession(manager, session);
if (sess) {
sess->finalLock--;
smlTrace(TRACE_EXIT, "%s - %i", __func__, sess->finalLock);
} else {
smlTrace(TRACE_EXIT, "%s: Not Found", __func__);
}
}
SmlBool smlManagerObjectRegister(SmlManager *manager, SmlCommandType type, SmlSession *session, SmlLocation *location, SmlLocation *source, const char *contentType, SmlCommandCb callback, SmlCommandCb childCallback, void *userdata, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p, %s, %p, %p, %p, %p)", __func__, manager, type, session, location, source, VA_STRING(contentType), callback, childCallback, userdata, error);
smlAssert(manager);
smlAssert(callback);
CHECK_ERROR_REF
/* first we have to check if this an update for an already registered object */
/* this is important for actions which require more than one DsSession */
SmlObject *object = smlManagerObjectFindInternal(
manager, session, type,
location, source, contentType);
if (object != NULL && session != NULL)
{
/* check that it is not a global manager object */
SmlObject *hObject = smlManagerObjectFindInternal(
manager, NULL, type,
location, source, contentType);
if (hObject != NULL && hObject == object)
{
/* the found object is a global manager object */
/* so we have to register a new object */
object = NULL;
}
}
if (object)
{
/* there is an object and so we reconfigure it */
smlTrace(TRACE_INTERNAL, "%s: prepare a reusable object", __func__);
if (object->location)
smlLocationUnref(object->location);
if (object->source)
smlLocationUnref(object->source);
if (object->contentType)
smlSafeCFree(&(object->contentType));
}
else
{
/* let's create and register a new object */
smlTrace(TRACE_INTERNAL, "%s: create and register a new object", __func__);
object = smlTryMalloc0(sizeof(SmlObject), error);
if (!object)
goto error;
if (session) {
managerSession *sess = _manager_session_find(manager, session);
if (!sess) {
smlErrorSet(error, SML_ERROR_GENERIC, "Session not found");
goto error_free_object;
}
sess->objects = g_list_append(sess->objects, object);
} else {
manager->objects = g_list_append(manager->objects, object);
}
}
smlTrace(TRACE_INTERNAL, "%s: configure registered object", __func__);
object->type = type;
if (location) {
object->location = location;
smlLocationRef(location);
}
if (source) {
object->source = source;
smlLocationRef(source);
}
if (contentType) {
object->contentType = g_strdup(contentType);
}
object->commandCallback = callback;
object->childCallback = childCallback;
object->commandCallbackUserdata = userdata;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_object:
smlManagerObjectFree(object);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
void smlManagerObjectDeregister(SmlManager *manager, SmlCommandType type, SmlLocation *location, SmlLocation *source)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p)", __func__, manager, type, location, source);
smlAssert(manager);
GList *o2 = g_list_copy(manager->objects);
GList *o = NULL;
for (o = o2; o; o = o->next) {
SmlObject *object = o->data;
if (object->type != type)
continue;
if (!smlLocationCompare(NULL, object->location, NULL, location))
continue;
if (!smlLocationCompare(NULL, object->source, NULL, source))
continue;
smlManagerObjectFree(object);
manager->objects = g_list_remove(manager->objects, object);
}
g_list_free(o2);
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlManagerObjectFree(SmlObject *object)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, object);
smlAssert(object);
if (object->location)
smlLocationUnref(object->location);
if (object->source)
smlLocationUnref(object->source);
if (object->contentType)
smlSafeCFree(&(object->contentType));
smlSafeFree((gpointer *)&object);
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlObject *smlManagerObjectFind(SmlManager *manager, SmlSession *session, SmlCommand *cmd)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, manager, session, cmd);
smlAssert(manager);
smlAssert(cmd);
SmlObject *object = smlManagerObjectFindInternal(
manager,
session,
cmd->type,
cmd->target, cmd->source,
cmd->private.alert.contentType);
smlTrace(TRACE_EXIT, "%s(%p)", __func__, object);
return object;
}
SmlObject *smlManagerObjectFindInternal(
SmlManager *manager,
SmlSession *session,
SmlCommandType type,
SmlLocation *target,
SmlLocation *source,
const char* contentType)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %d, %p, %p)", __func__, manager, session, type, target, source);
smlAssert(manager);
SmlObject *object = NULL;
GList *o = NULL;
/* We first search for the object in the session specific objects */
if (session) {
managerSession *sess = _manager_session_find(manager, session);
if (sess) {
for (o = sess->objects; o; o = o->next) {
object = o->data;
if (object->type != SML_COMMAND_TYPE_UNKNOWN && type != object->type)
continue;
if (!smlLocationCompare(NULL, object->location, NULL, target))
continue;
if (!smlLocationCompare(NULL, object->source, NULL, source))
continue;
smlTrace(TRACE_EXIT, "%s: FOUND (session): %p", __func__, object);
return object;
}
}
}
for (o = manager->objects; o; o = o->next) {
object = o->data;
if (object->type != SML_COMMAND_TYPE_UNKNOWN && type != object->type)
continue;
if (type == SML_COMMAND_TYPE_ALERT && contentType) {
if (object->contentType) {
/* This item is a san 11 alert */
if (!strcmp(contentType, object->contentType)) {
smlTrace(TRACE_EXIT, "%s: FOUND SAN TARGET: %p", __func__, object);
return object;
}
}
continue;
}
if (!smlLocationCompare(NULL, object->location, NULL, target))
continue;
if (!smlLocationCompare(NULL, object->source, NULL, source))
continue;
/* Check if the target is a san target */
if (object->contentType)
continue;
smlTrace(TRACE_EXIT, "%s: FOUND: %p", __func__, object);
return object;
}
smlTrace(TRACE_EXIT, "%s: NOT FOUND", __func__);
return NULL;
}
SmlTransport *smlManagerGetTransport(SmlManager *manager)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, manager);
smlAssert(manager);
SmlTransport *tsp = manager->transport;
smlTrace(TRACE_EXIT, "%s: %p", __func__, tsp);
return tsp;
}
SmlBool smlManagerDispatchHeader(SmlManager *manager, SmlSession *session, SmlHeader *header, SmlCred *cred, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p)", __func__, manager, session, header, cred, error);
smlAssert(manager);
smlAssert(session);
smlAssert(header);
CHECK_ERROR_REF
if (manager->headerCallback &&
session->sessionType == SML_SESSION_TYPE_SERVER) {
/* Authentication is only specified for OMA DS server. */
manager->headerCallback(session, header, cred, manager->headerCallbackUserdata);
} else {
smlTrace(TRACE_INTERNAL, "%s: Header not handled!", __func__);
if (session->sessionType == SML_SESSION_TYPE_SERVER &&
!session->established) {
/* An OMA DS server must handle the header for security
* reasons - especially authentication.
*
* Question: can we detect such a bug in an earlier phase?
*/
g_warning("%s: This is an OMA DS server. " \
"The header callback is missing. " \
"All requests will be accepted without authentication.",
__func__);
session->established = TRUE;
smlSessionDispatchEvent(
session, SML_SESSION_EVENT_ESTABLISHED,
NULL, NULL, NULL, NULL);
}
SmlStatus *status = smlStatusNew(
SML_NO_ERROR, 0,
header->messageID,
header->source, header->target,
SML_COMMAND_TYPE_HEADER, error);
if (!status)
goto error;
if (!smlSessionSendReply(session, status, error)) {
smlStatusUnref(status);
goto error;
}
smlStatusUnref(status);
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlManagerDispatchChildCommand(SmlManager *manager, SmlSession *session, SmlCommand *parent, SmlCommand *cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p)", __func__, manager, session, parent, cmd, error);
smlAssert(manager);
smlAssert(session);
smlAssert(parent);
CHECK_ERROR_REF
//Check if a handler for this object at this path has been installed.
SmlObject *object = smlManagerObjectFind(manager, session, parent);
if (object) {
//Check if a handler for this object at this path has been installed.
if (!object->childCallback) {
smlErrorSet(error, SML_ERROR_GENERIC, "No handler for the child was installed");
goto error;
}
object->childCallback(session, cmd, object->commandCallbackUserdata);
} else {
SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_NOT_FOUND, error);
if (!reply)
goto error;
if (!smlSessionSendReply(session, reply, error)) {
smlStatusUnref(reply);
goto error;
}
smlStatusUnref(reply);
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to find child command handler");
goto error;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlManagerDispatchCommand(SmlManager *manager, SmlSession *session, SmlCommand *cmd, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, manager, session, cmd, error);
smlAssert(manager);
smlAssert(session);
smlAssert(cmd);
CHECK_ERROR_REF
//Check if a handler for this object at this path has been installed.
SmlObject *object = smlManagerObjectFind(manager, session, cmd);
if (object) {
//Check if a handler for this object at this path has been installed.
if (!object->commandCallback) {
smlErrorSet(error, SML_ERROR_GENERIC, "No handler for the child was installed");
goto error;
}
object->commandCallback(session, cmd, object->commandCallbackUserdata);
} else {
const char *type = smlCommandTypeToString(cmd->type, error);
/* If the type is unknown then this can be part of the error. */
smlErrorDeref(error);
const char *srcuri = (cmd->source && cmd->source->locURI) ? cmd->source->locURI : "NULL";
const char *dsturi = (cmd->target && cmd->target->locURI) ? cmd->target->locURI : "NULL";
smlErrorSet(error, SML_ERROR_NOT_FOUND, "Unable to find command handler (%s: %s -> %s)", (type) ? type : "UNKNOWN", srcuri, dsturi);
SmlError *locerror = NULL;
SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_NOT_FOUND, &locerror);
if (!reply) {
smlErrorSet(error, SML_ERROR_NOT_FOUND, "%s %s",
smlErrorPrint(error),
smlErrorPrint(&locerror));
goto error;
}
if (!smlSessionSendReply(session, reply, &locerror)) {
smlStatusUnref(reply);
smlErrorSet(error, SML_ERROR_NOT_FOUND, "%s %s",
smlErrorPrint(error),
smlErrorPrint(&locerror));
goto error;
}
smlStatusUnref(reply);
goto error;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
void smlManagerRegisterHeaderHandler(SmlManager *manager, SmlHeaderCb callback, SmlStatusReplyCb statuscb, void *userdata)
{
smlAssert(manager);
manager->headerCallback = callback;
manager->headerStatusCallback = statuscb;
manager->headerCallbackUserdata = userdata;
}
void smlManagerSetLocalMaxMsgSize(SmlManager *manager, unsigned int size)
{
smlAssert(manager);
manager->localMaxMsgSize = size;
}
void smlManagerSetLocalMaxObjSize(SmlManager *manager, unsigned int size)
{
smlAssert(manager);
manager->localMaxObjSize = size;
}
char *smlManagerGetNewSessionID(SmlManager *manager)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, manager);
smlAssertMsg(manager, "The manager is required to avoid duplicated session IDs.");
char *sessionString = NULL;
while (sessionString == NULL)
{
/* WARNING: only 4 byte session IDs are theoretically allowed !!! */
/* create random session ID - glib only supports 32 bit random numbers */
unsigned int sessionID = (unsigned int) g_random_int_range (0, 0xFFFF);
sessionString = g_strdup_printf("%u", sessionID);
smlTrace(TRACE_INTERNAL, "%s: new potential session ID is %lu.", __func__, sessionID);
if (smlManagerSessionFind(manager, sessionString))
smlSafeCFree(&sessionString);
}
smlTrace(TRACE_EXIT, "%s - %s", __func__, VA_STRING(sessionString));
return sessionString;
}
SmlLink *smlManagerSessionGetLink(
SmlManager *manager,
SmlSession *session,
SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, manager, session, error);
CHECK_ERROR_REF
managerSession *_msession = _manager_session_find(manager, session);
if (!_msession)
{
smlErrorSet(error, SML_ERROR_GENERIC,
"The session %d is not registered at the manager.",
smlSessionGetSessionID(session));
goto error;
}
if (_msession->link)
smlLinkRef(_msession->link);
smlTrace(TRACE_EXIT, "%s - %p", __func__, _msession->link);
return _msession->link;
error:
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return NULL;
}
smlSessionSendReply
SmlBool smlSessionSendReply(SmlSession *session, SmlStatus *status, SmlError **error)
Sends a reply to a command.
Definition: sml_session.c:2536
SmlCred
Definition: sml_elements_internals.h:87
smlErrorPrint
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
smlParserGetHeader
SmlBool smlParserGetHeader(SmlParser *parser, SmlHeader **header, SmlCred **cred, SmlError **error)
Parses the SyncHdr.
Definition: sml_parse.c:216
smlManagerStop
void smlManagerStop(SmlManager *manager)
Stops the active manager part.
Definition: sml_manager.c:565
smlSessionSetLocalMaxObjSize
void smlSessionSetLocalMaxObjSize(SmlSession *session, unsigned int limit)
Definition: sml_session.c:2363
smlParserFree
void smlParserFree(SmlParser *parser)
Frees a parser.
Definition: sml_parse.c:108
SmlManager
Definition: sml_manager_internals.h:35
SmlStatus
Definition: sml_command_internals.h:94
smlErrorDuplicate
void smlErrorDuplicate(SmlError **target, SmlError **source)
Duplicates the error into the target.
Definition: sml_error.c:337
smlQueueSend
void smlQueueSend(SmlQueue *queue, void *data)
Sends a message down a queue.
Definition: sml_queue.c:353
SmlHeader
Definition: sml_elements_internals.h:73
SmlParser
Definition: sml_parse_internals.h:25
SmlTransport
Definition: sml_transport_internals.h:69
smlQueueNew
SmlQueue * smlQueueNew(SmlError **error)
Creates a new asynchronous queue.
Definition: sml_queue.c:81
smlTransportSetResponseURI
SmlBool smlTransportSetResponseURI(SmlTransport *tsp, const char *uri, SmlError **error)
Sets the response URI after initialization.
Definition: sml_transport.c:1041
SmlTransportData
Definition: sml_transport_internals.h:112
SmlSession
Definition: sml_session_internals.h:27
smlParserNew
SmlParser * smlParserNew(SmlMimeType type, unsigned int limit, SmlError **error)
Creates a new parser.
Definition: sml_parse.c:50
SmlCommand
Definition: sml_command_internals.h:73
SmlLink
Definition: sml_transport_internals.h:92
smlTrace
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
smlParserStart
SmlBool smlParserStart(SmlParser *parser, const char *data, unsigned int size, SmlError **error)
Starts the parser on a given data buffer.
Definition: sml_parse.c:132
SmlLocation
Definition: sml_elements_internals.h:40
SmlManagerEvent
Definition: sml_manager_internals.h:63
smlSessionNew
SmlSession * smlSessionNew(SmlSessionType sessionType, SmlMimeType mimetype, SmlProtocolVersion version, SmlProtocolType protocol, SmlLocation *target, SmlLocation *source, const char *sessionID, unsigned int messageID, SmlError **error)
Creates a new session.
Definition: sml_session.c:1832
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
managerSession
Definition: sml_manager.c:32
SmlObject
Definition: sml_manager_internals.h:71
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