obex_server.c
/*
* libsyncml - A syncml protocol implementation
* Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
* Copyright (C) 2008-2009 Michael Bell <michael.bell@opensync.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <libsyncml/syncml.h>
#include <libsyncml/syncml_internals.h>
#include <libsyncml/sml_error_internals.h>
#include <libsyncml/sml_transport_internals.h>
#include "obex_internals.h"
#include "obex_server_internals.h"
#ifndef WIN32
#include <fcntl.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#endif
static gboolean _dispatch_obex(gpointer data)
{
SmlLinkObexServerEnv *linkenv = data;
int result = OBEX_HandleInput(linkenv->handle, 0);
if (result < 0) {
SmlError *error = NULL;
smlErrorSet(&error, SML_ERROR_GENERIC,
"The handling of the obex input failed (%i).",
result);
smlTransportReceiveEvent(linkenv->env->tsp, linkenv->link, SML_TRANSPORT_EVENT_ERROR, NULL, error);
smlTrace(TRACE_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
return FALSE;
}
if (linkenv->disconnect) {
smlTrace(TRACE_INTERNAL, "%s: disconnecting link", __func__);
OBEX_TransportDisconnect(linkenv->handle);
// close(linkenv->fd);
linkenv->destroy = TRUE;
}
if (linkenv->destroy) {
smlTrace(TRACE_INTERNAL, "%s: Destroying link %p %p", __func__, linkenv, linkenv->link);
/* preserve the link and the transport for the disconnect event */
SmlTransport *tsp = linkenv->env->tsp;
SmlLink *link_ = linkenv->link;
smlLinkRef(link_);
/* the environment must be cleaned up before the event is sent */
smlLinkDeref(linkenv->link);
g_source_unref(linkenv->source);
OBEX_Cleanup(linkenv->handle);
if (tsp->context)
g_main_context_unref(tsp->context);
smlSafeFree((gpointer *)&linkenv);
link_->link_data = NULL;
/* send the event */
smlTransportReceiveEvent(tsp, link_, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
smlLinkDeref(link_);
return FALSE;
}
return TRUE;
}
static void _smlTransportObexServerLinkEvent(obex_t *handle, obex_object_t *object, int mode, int event, int obex_cmd, int obex_rsp)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %i, %i, %i)", __func__, handle, object, mode, event, obex_cmd, obex_rsp);
SmlLinkObexServerEnv *linkenv = OBEX_GetUserData(handle);
SmlError *error = NULL;
obex_headerdata_t header;
uint8_t headertype = 0;
uint32_t len = 0;
uint32_t conid = 0;
SmlMimeType mimetype = SML_MIMETYPE_UNKNOWN;
if (linkenv->disconnect) {
OBEX_ObjectSetRsp(object, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
smlTrace(TRACE_EXIT, "%s: This link was disconnected", __func__);
return;
}
switch (event) {
case OBEX_EV_PROGRESS:
smlTrace(TRACE_INTERNAL, "%s: Progress", __func__);
break;
case OBEX_EV_REQDONE:
smlTrace(TRACE_INTERNAL, "%s: Done", __func__);
if (obex_cmd == OBEX_CMD_DISCONNECT) {
linkenv->destroy = TRUE;
}
break;
case OBEX_EV_REQHINT:
smlTrace(TRACE_INTERNAL, "%s: Hint", __func__);
/* Comes BEFORE the lib parses anything. */
switch (obex_cmd) {
case OBEX_CMD_GET:
/* At this point we received a get request. If we dont have any data
* to send yet, we iterate the main context until the data arrived. */
while (!linkenv->send_data && !linkenv->disconnect && !linkenv->error) {
if (!g_main_context_pending(linkenv->env->tsp->context)) {
usleep(100);
continue;
}
smlTrace(TRACE_INTERNAL, "%s: Dispatching command queue since we dont have data to send", __func__);
g_main_context_iteration(linkenv->env->tsp->context, TRUE);
}
/* We received a disconnect event so we reply with an error */
if (linkenv->disconnect) {
OBEX_ObjectSetRsp(object, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
smlTrace(TRACE_INTERNAL, "%s: Get was aborted", __func__);
break;
}
case OBEX_CMD_PUT:
case OBEX_CMD_CONNECT:
case OBEX_CMD_DISCONNECT:
OBEX_ObjectSetRsp(object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
break;
default:
OBEX_ObjectSetRsp(object, OBEX_RSP_NOT_IMPLEMENTED, OBEX_RSP_NOT_IMPLEMENTED);
break;
}
break;
case OBEX_EV_REQ:
switch (obex_cmd) {
case OBEX_CMD_SETPATH:
smlTrace(TRACE_INTERNAL, "%s: Got SETPATH command", __func__);
OBEX_ObjectSetRsp(object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
break;
case OBEX_CMD_PUT:;
smlTrace(TRACE_INTERNAL, "%s: Got PUT command", __func__);
uint32_t length = 0;
char *body = NULL;
while (OBEX_ObjectGetNextHeader(handle, object, &headertype, &header, &len)) {
smlTrace(TRACE_INTERNAL, "%s: Next header %i, %d, %p", __func__, headertype, header.bq4, header.bs);
switch (headertype) {
case OBEX_HDR_CONNECTION:
smlTrace(TRACE_INTERNAL, "%s: Found connection number: %d", __func__, header.bq4);
conid = header.bq4;
break;
case OBEX_HDR_TYPE:;
char *mimetypestr = g_strndup((char *)header.bs, len);
/*char *mimetypestr = g_malloc0(len);// / 2 + 1);
if (OBEX_UnicodeToChar((unsigned char *)mimetypestr, header.bs, len / 2 + 1) == -1) {
smlSafeCFree(&mimetypestr);
smlErrorSet(&error, SML_ERROR_GENERIC, "unable to convert from unicode");
goto error;
}*/
smlTrace(TRACE_INTERNAL, "%s: Found type: %s", __func__, VA_STRING(mimetypestr));
if (!strcmp(mimetypestr, SML_ELEMENT_WBXML)) {
mimetype = SML_MIMETYPE_WBXML;
} else if (!strcmp(mimetypestr, SML_ELEMENT_XML)) {
mimetype = SML_MIMETYPE_XML;
} else if (!strcmp(mimetypestr, SML_ELEMENT_SAN)) {
mimetype = SML_MIMETYPE_SAN;
} else {
smlSafeCFree(&mimetypestr);
smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mime type");
goto error;
}
smlSafeCFree(&mimetypestr);
break;
case OBEX_HDR_LENGTH:
smlTrace(TRACE_INTERNAL, "%s: Found length: %d", __func__, header.bq4);
length = header.bq4;
break;
case OBEX_HDR_BODY:
if (!length) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Length must come before the body");
goto error;
}
/* We need one byte more than the response length
* because the data can be a native XML message.
* If the data is a native XML message then it is
* sometimes directly used as string.
*
* The string is automatically NULL terminated
* because smlTryMalloc0 fills the new memory with NULLs.
*/
body = smlTryMalloc0(length + 1, &error);
if (!body)
goto error;
memcpy(body, header.bs, length);
break;
default:
smlTrace(TRACE_INTERNAL, "%s: Unknown header", __func__);
}
}
if (!conid) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Missing connection id");
goto error;
}
if (conid != linkenv->conid) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Wrong connection id");
goto error;
}
if (mimetype == SML_MIMETYPE_UNKNOWN) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Missing mime type");
goto error;
}
if (!length) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Missing length");
goto error;
}
if (!body) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Missing body");
goto error;
}
SmlTransportData *tspdata = smlTransportDataNew(body, length, mimetype, TRUE, &error);
if (!tspdata)
goto error;
if (smlTransportReceiveEvent(linkenv->env->tsp, linkenv->link, SML_TRANSPORT_EVENT_DATA, tspdata, NULL))
OBEX_ObjectSetRsp(object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
else
OBEX_ObjectSetRsp(object, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
smlTransportDataDeref(tspdata);
break;
case OBEX_CMD_GET:;
smlTrace(TRACE_INTERNAL, "%s: Got GET command", __func__);
while (OBEX_ObjectGetNextHeader(handle, object, &headertype, &header, &len)) {
smlTrace(TRACE_INTERNAL, "%s: Next header %i, %d, %p", __func__, headertype, header.bq4, header.bs);
switch (headertype) {
case OBEX_HDR_CONNECTION:
smlTrace(TRACE_INTERNAL, "%s: Found connection number: %d", __func__, header.bq4);
conid = header.bq4;
break;
case OBEX_HDR_TYPE:;
char *mimetypestr = g_strndup((char *)header.bs, len);
/*char *mimetypestr = g_malloc0(len / 2 + 1);
if (OBEX_UnicodeToChar((unsigned char *)mimetypestr, header.bs, len / 2 + 1) == -1) {
smlSafeCFree(&mimetypestr);
smlErrorSet(&error, SML_ERROR_GENERIC, "unable to convert from unicode");
goto error;
}*/
smlTrace(TRACE_INTERNAL, "%s: Found type: %s", __func__, VA_STRING(mimetypestr));
if (!strcmp(mimetypestr, SML_ELEMENT_WBXML)) {
mimetype = SML_MIMETYPE_WBXML;
} else if (!strcmp(mimetypestr, SML_ELEMENT_XML)) {
mimetype = SML_MIMETYPE_XML;
} else if (!strcmp(mimetypestr, SML_ELEMENT_SAN)) {
mimetype = SML_MIMETYPE_SAN;
} else {
smlSafeCFree(&mimetypestr);
smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mime type");
goto error;
}
smlSafeCFree(&mimetypestr);
break;
default:
smlTrace(TRACE_INTERNAL, "%s: Unknown header", __func__);
}
}
if (!conid) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Missing connection id");
goto error;
}
if (conid != linkenv->conid) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Wrong connection id");
goto error;
}
if (mimetype == SML_MIMETYPE_UNKNOWN) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Missing mime type");
goto error;
}
if (linkenv->error) {
if (smlErrorGetClass(&(linkenv->error)) <= SML_ERRORCLASS_RETRY)
OBEX_ObjectSetRsp(object, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
else
OBEX_ObjectSetRsp(object, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
smlErrorDeref(&(linkenv->error));
linkenv->error = NULL;
smlTrace(TRACE_INTERNAL, "%s: Sent error in response to get", __func__);
break;
}
if (!linkenv->send_data) {
smlErrorSet(&error, SML_ERROR_GENERIC, "No data to send");
goto error;
}
if (mimetype != linkenv->send_data->type) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"Wrong mimetype %d requested. %d was expected.",
mimetype, linkenv->send_data->type);
goto error;
}
/* Now the data and size */
header.bq4 = (uint32_t)linkenv->send_data->size;
OBEX_ObjectAddHeader(handle, object, OBEX_HDR_LENGTH, header, sizeof(uint32_t), 0);
header.bs = (unsigned char *)linkenv->send_data->data;
OBEX_ObjectAddHeader(handle, object, OBEX_HDR_BODY, header, linkenv->send_data->size, 0);
smlTransportDataDeref(linkenv->send_data);
linkenv->send_data = NULL;
OBEX_ObjectSetRsp(object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
break;
case OBEX_CMD_CONNECT:;
smlTrace(TRACE_INTERNAL, "%s: Got CONNECT command", __func__);
char *target = NULL;
while (OBEX_ObjectGetNextHeader(handle, object, &headertype, &header, &len)) {
smlTrace(TRACE_INTERNAL, "%s: Next header %i, %d, %p", __func__, headertype, header.bq4, header.bs);
switch (headertype) {
case OBEX_HDR_TARGET:
target = g_strndup((char *)header.bs, len);
smlTrace(TRACE_INTERNAL, "%s: Found target: %s", __func__, VA_STRING(target));
break;
default:
smlTrace(TRACE_INTERNAL, "%s: Unknown header", __func__);
}
}
if (!target || strcmp(target, "SYNCML-SYNC")) {
smlSafeCFree(&target);
smlErrorSet(&error, SML_ERROR_GENERIC, "Missing target");
goto error;
}
smlSafeCFree(&target);
header.bq4 = linkenv->conid;
OBEX_ObjectAddHeader(handle, object, OBEX_HDR_CONNECTION, header, sizeof(linkenv->conid), OBEX_FL_FIT_ONE_PACKET);
header.bs = (unsigned char *)"SYNCML-SYNC";
OBEX_ObjectAddHeader(handle, object, OBEX_HDR_WHO, header, strlen((char *)header.bs), OBEX_FL_FIT_ONE_PACKET);
linkenv->link = smlLinkNew(linkenv->env->tsp, linkenv, &error);
if (!linkenv->link)
goto error;
if (smlTransportReceiveEvent(linkenv->env->tsp, linkenv->link, SML_TRANSPORT_EVENT_CONNECT_DONE, NULL, NULL))
OBEX_ObjectSetRsp(object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
else {
OBEX_ObjectSetRsp(object, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
linkenv->disconnect = TRUE;
}
break;
case OBEX_CMD_DISCONNECT:
smlTrace(TRACE_INTERNAL, "%s: Got DISCONNECT command", __func__);
OBEX_ObjectSetRsp(object, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
break;
default:
smlTrace(TRACE_INTERNAL, "%s: Denied %02x request", __func__, obex_cmd);
OBEX_ObjectSetRsp(object, OBEX_RSP_NOT_IMPLEMENTED, OBEX_RSP_NOT_IMPLEMENTED);
break;
}
break;
case OBEX_EV_LINKERR:
smlTrace(TRACE_INTERNAL, "%s: Link broken (this does not have to be an error)!", __func__);
break;
default:
smlTrace(TRACE_INTERNAL, "%s: Unknown event!", __func__);
break;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error:
OBEX_ObjectSetRsp(object, OBEX_RSP_BAD_REQUEST, OBEX_RSP_BAD_REQUEST);
smlErrorRef(&error);
smlTransportReceiveEvent(linkenv->env->tsp, linkenv->link, SML_TRANSPORT_EVENT_ERROR, NULL, error);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
}
static void _smlTransportObexServerMainEvent(obex_t *handle, obex_object_t *object, int mode, int event, int obex_cmd, int obex_rsp)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %i, %i, %i)",
__func__, handle, object, mode, event, obex_cmd, obex_rsp);
SmlTransportObexServerEnv *env = OBEX_GetUserData(handle);
SmlError *error = NULL;
SmlLinkObexServerEnv *linkenv = NULL;
switch (event) {
case OBEX_EV_ACCEPTHINT:
/* This event means that there is a new connection. */
/* Create the env that handles this link */
linkenv = smlTryMalloc0(sizeof(SmlLinkObexServerEnv), &error);
if (!linkenv)
goto error;
linkenv->env = env;
linkenv->handle = OBEX_ServerAccept(env->handle, _smlTransportObexServerLinkEvent, linkenv);
if (!linkenv->handle) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"OBEX_ServerAccept failed.");
smlSafeFree((gpointer *)&linkenv);
goto error;
}
smlTrace(TRACE_INTERNAL, "%s: New obex_t * handle %p", __func__, linkenv->handle);
/* Now we create a source that watches the new obex connection */
linkenv->source = g_idle_source_new();
g_source_set_callback(linkenv->source, _dispatch_obex, linkenv, NULL);
g_source_attach(linkenv->source, env->tsp->context);
if (env->tsp->context)
g_main_context_ref(env->tsp->context);
/* We first have to add the connection id which we generate for this transport */
env->lastConId++;
linkenv->conid = env->lastConId;
// the user data was already ste accept
// OBEX_SetUserData(linkenv->handle, linkenv);
break;
default:
smlErrorSet(&error, SML_ERROR_GENERIC,
"Unknown OBEX event %d.", event);
goto error;
break;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error:
OBEX_ObjectSetRsp(object, OBEX_RSP_BAD_REQUEST, OBEX_RSP_BAD_REQUEST);
smlErrorRef(&error);
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
}
/* These function supervise the file descriptor where we are listening for incoming
* connections */
static gboolean _fd_prepare(GSource *source, gint *timeout_)
{
smlTrace(TRACE_INTERNAL, "%s(%p, %p)", __func__, source, timeout_);
/* There is no need to dispatch this too fast since
* it does not influence the transmission of data, only
* the connection */
*timeout_ = 50;
return FALSE;
}
static gboolean _fd_check(GSource *source)
{
SmlTransportObexServerEnv *env = *((SmlTransportObexServerEnv **)(source + 1));
fd_set rfds;
FD_ZERO(&rfds);
int fd = OBEX_GetFD(env->handle);
FD_SET(fd, &rfds);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 1;
int retval = select(fd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1)
smlTrace(TRACE_ERROR, "%s: Unable to select()", __func__);
else if (retval)
return TRUE;
return FALSE;
}
static gboolean _fd_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, source, callback, user_data);
SmlTransportObexServerEnv *env = user_data;
SmlError *error = NULL;
/* this is a non-locking function */
int result = OBEX_HandleInput(env->handle, 0);
if (result < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"OBEX_HandleInput on main handle returned %d.",
result);
goto error;
}
if (result == 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"OBEX_HandleInput on main handle get a timeout.");
goto error;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
return FALSE;
}
static SmlBool smlTransportObexServerSetConfigOption(
SmlTransport *tsp,
const char *name,
const char *value,
SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %p)", __func__, tsp, VA_STRING(name), VA_STRING(value), error);
CHECK_ERROR_REF
smlAssert(tsp);
smlAssert(tsp->transport_data);
SmlTransportObexServerEnv *env = tsp->transport_data;
if (!strcmp(name, SML_TRANSPORT_CONFIG_PORT)) {
env->port = atoi(value);
smlTrace(TRACE_INTERNAL, "%s: Port %i detected", __func__, env->port);
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_BLUETOOTH_ADDRESS)) {
#ifdef ENABLE_BLUETOOTH
int code = str2ba(value, env->bdaddr);
if (code != 0) {
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
"Bluetooth MAC cannot be interpreted (%d).", code);
goto error;
}
smlTrace(TRACE_INTERNAL, "%s: Bluetooth MAC %s detected",
__func__, VA_STRING(value));
#else
smlErrorSet(error, SML_ERROR_GENERIC, "Bluetooth not enabled");
goto error;
#endif
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_BLUETOOTH_CHANNEL)) {
env->port = atoi(value);
smlTrace(TRACE_INTERNAL, "%s: Bluetooth channel %i detected",
__func__, env->port);
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_IRDA_SERVICE)) {
env->irda_service = g_strdup(value);
smlTrace(TRACE_INTERNAL, "%s: IRDA service %s detected",
__func__, VA_STRING(env->irda_service));
} else {
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
"Unknown parameter %s found.", name);
goto error;
}
smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool smlTransportObexServerSetConnectionType(
SmlTransport *tsp,
SmlTransportConnectionType type,
SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, tsp, type, error);
CHECK_ERROR_REF
smlAssert(tsp);
smlAssert(tsp->transport_data);
SmlTransportObexServerEnv *env = tsp->transport_data;
env->type = type;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
static SmlBool smlTransportObexServerInit(SmlTransport *tsp, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
CHECK_ERROR_REF
smlAssert(tsp);
smlAssert(tsp->transport_data);
SmlTransportObexServerEnv *env = tsp->transport_data;
smlAssert(env->handle == NULL);
switch (env->type) {
#ifdef ENABLE_OPENOBEX_TCP
case SML_TRANSPORT_CONNECTION_TYPE_NET:;
/* init object */
env->handle = OBEX_Init(OBEX_TRANS_INET,
_smlTransportObexServerMainEvent,
OBEX_FL_KEEPSERVER);
if (!env->handle) {
smlErrorSet(error, SML_ERROR_GENERIC,
"Unable to create OBEX IRDA transport.");
goto error;
}
OBEX_SetUserData(env->handle, env);
/* prepare IP address etc. */
if (!env->port)
g_warning("Using default INET port.");
size_t size = sizeof(struct sockaddr_in);
struct sockaddr_in *addr = smlTryMalloc0(size, error);
if (!addr)
goto error;
addr->sin_family = PF_INET;
addr->sin_port = htons(env->port);
addr->sin_addr.s_addr = INADDR_ANY;
/* register server */
if (GET_OBEX_RESULT(TcpOBEX_ServerRegister(env->handle, (struct sockaddr *) addr, size)) < 0)
{
smlErrorSet(error, SML_ERROR_GENERIC,
"Unable to register INET OBEX server. %s (%i).",
strerror(errno), errno);
smlSafeFree((gpointer *)&addr);
goto error;
}
smlSafeFree((gpointer *)&addr);
break;
#endif /* ENABLE_OPENOBEX_TCP */
case SML_TRANSPORT_CONNECTION_TYPE_SERIAL:
/* init object */
env->handle = OBEX_Init(OBEX_TRANS_IRDA,
_smlTransportObexServerMainEvent,
OBEX_FL_KEEPSERVER);
if (!env->handle) {
smlErrorSet(error, SML_ERROR_GENERIC,
"Unable to create OBEX IRDA transport.");
goto error;
}
OBEX_SetUserData(env->handle, env);
/* register server */
if (GET_OBEX_RESULT(IrOBEX_ServerRegister(env->handle, env->irda_service)))
{
smlErrorSet(error, SML_ERROR_GENERIC,
"Unable to register IRDA OBEX server. %s (%i).",
strerror(errno), errno);
goto error;
}
break;
case SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH:
#ifdef ENABLE_BLUETOOTH
/* init object */
env->handle = OBEX_Init(OBEX_TRANS_BLUETOOTH,
_smlTransportObexServerMainEvent,
OBEX_FL_KEEPSERVER);
if (!env->handle) {
smlErrorSet(error, SML_ERROR_GENERIC,
"Unable to create OBEX Bluetooth transport.");
goto error;
}
OBEX_SetUserData(env->handle, env);
/* register server */
if (GET_OBEX_RESULT(BtOBEX_ServerRegister(env->handle, env->bdaddr, env->port)) < 0)
{
smlErrorSet(error, SML_ERROR_GENERIC,
"Unable to register Bluetooth OBEX server. %s (%i).",
strerror(errno), errno);
goto error;
}
#else
smlErrorSet(error, SML_ERROR_GENERIC, "Bluetooth not enabled");
goto error;
#endif
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown obex type");
goto error;
}
smlTrace(TRACE_INTERNAL, "%s: server registered successfully", __func__);
/* Now we create a source that watches for incoming connection requests */
env->functions = smlTryMalloc0(sizeof(GSourceFuncs), error);
if (!env->functions)
goto error;
env->functions->prepare = _fd_prepare;
env->functions->check = _fd_check;
env->functions->dispatch = _fd_dispatch;
env->functions->finalize = NULL;
env->source = g_source_new(env->functions, sizeof(GSource) + sizeof(SmlTransportObexServerEnv *));
SmlTransportObexServerEnv **envptr = (SmlTransportObexServerEnv **)(env->source + 1);
*envptr = env;
g_source_set_callback(env->source, NULL, env, NULL);
g_source_attach(env->source, tsp->context);
if (tsp->context)
g_main_context_ref(tsp->context);
smlTrace(TRACE_EXIT, "%s: %p", __func__, env);
return TRUE;
error:
if (env->handle) {
OBEX_Cleanup(env->handle);
env->handle = NULL;
}
if (env)
smlSafeFree((gpointer *)&env);
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool smlTransportObexServerFinalize(void *data, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, error);
CHECK_ERROR_REF
smlAssert(data);
SmlTransportObexServerEnv *env = data;
smlAssert(env->tsp);
if (env->handle)
OBEX_Cleanup(env->handle);
/* detach source */
if (env->source) {
g_source_destroy(env->source);
g_source_unref(env->source);
}
/* free context */
if (env->tsp->context)
g_main_context_unref(env->tsp->context);
/* free functions */
if (env->functions)
smlSafeFree((gpointer *)&(env->functions));
if (env->irda_service)
smlSafeCFree(&(env->irda_service));
smlSafeFree((gpointer *)&env);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
static void smlTransportObexServerSend(void *userdata, void *linkdata, SmlTransportData *data, SmlError *error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, userdata, linkdata, data, error);
smlAssert(data || error);
smlAssert(userdata);
// Unused: SmlTransportObexServerEnv *env = userdata;
SmlLinkObexServerEnv *linkenv = linkdata;
smlAssert(linkenv);
SmlError *local_error = NULL;
if (linkdata == NULL) {
/* send is called before connect succeeded or
* the user of the library ignored the link
*/
smlErrorSet(&local_error, SML_ERROR_GENERIC,
"The OBEX server tries to send before a connection with a client was established or the link was ignored.");
goto error;
}
if (error) {
smlAssert(!data);
linkenv->error = error;
smlTrace(TRACE_EXIT, "%s: Error set", __func__);
return;
}
if (linkenv->send_data) {
smlErrorSet(&local_error, SML_ERROR_GENERIC, "We already have waiting data");
goto error;
}
linkenv->send_data = data;
smlTransportDataRef(linkenv->send_data);
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&local_error));
smlTransportReceiveEvent(linkenv->env->tsp, linkenv->link, SML_TRANSPORT_EVENT_ERROR, NULL, local_error);
smlErrorDeref(&local_error);
return;
}
static void smlTransportObexServerDisconnect(void *data, void *linkdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, linkdata);
smlAssert(data);
/* The OBEX client usually performs the disconnect.
* If the server disconnects first then the client
* interprets this as an error. So if we want to
* abort a connection then this is the correct way.
*/
if (linkdata) {
SmlLinkObexServerEnv *linkenv = linkdata;
linkenv->disconnect = TRUE;
smlTrace(TRACE_ERROR, "%s - explicit disconnect by OBEX server", __func__);
}
smlTrace(TRACE_EXIT, "%s", __func__);
return;
}
SmlBool smlTransportObexServerNew(SmlTransport *tsp, SmlError **error)
{
CHECK_ERROR_REF
smlAssert(tsp);
tsp->functions.set_config_option = smlTransportObexServerSetConfigOption;
tsp->functions.set_connection_type = smlTransportObexServerSetConnectionType;
tsp->functions.initialize = smlTransportObexServerInit;
tsp->functions.finalize = smlTransportObexServerFinalize;
tsp->functions.send = smlTransportObexServerSend;
tsp->functions.disconnect = smlTransportObexServerDisconnect;
SmlTransportObexServerEnv *env = smlTryMalloc0(sizeof(SmlTransportObexServerEnv), error);
if (!env)
return FALSE;
tsp->transport_data = env;
env->tsp = tsp;
env->lastConId = 0;
env->port = 0;
return TRUE;
}
smlErrorPrint
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
SmlLinkObexServerEnv
Definition: obex_server_internals.h:48
SmlTransportObexServerEnv
Definition: obex_server_internals.h:30
smlErrorGetClass
SmlErrorClass smlErrorGetClass(SmlError **error)
Gets the error class.
Definition: sml_error.c:382
SmlTransport
Definition: sml_transport_internals.h:69
SmlTransportData
Definition: sml_transport_internals.h:112
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
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