obex_client.c
/*
* libsyncml - A syncml protocol implementation
* Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
* Copyright (C) 2007-2009 Michael Bell <michael.bell@opensync.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <libsyncml/syncml.h>
#include <libsyncml/syncml_internals.h>
#include <libsyncml/sml_error_internals.h>
#include <libsyncml/sml_transport_internals.h>
#include "obex_internals.h"
#include "obex_client_internals.h"
#include "obex_client_vendor_internals.h"
#ifdef ENABLE_BLUETOOTH_SDPLIB
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
#endif
#include <fcntl.h>
#ifndef WIN32
#include <sys/poll.h>
#include <sys/stat.h>
#include <termios.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <strings.h>
#ifndef WIN32
static SmlBool obex_cable_at(
SmlTransportObexClientEnv *userdata,
const char *cmd,
char *rspbuf,
int rspbuflen,
int timeout,
SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %s, %p, %i, %i, %p)", __func__, userdata, VA_STRING(cmd), rspbuf, rspbuflen, timeout, error);
CHECK_ERROR_REF
fd_set ttyset;
struct timeval tv;
gint fd;
SmlBool statusOnly = FALSE;
char *answer = NULL;
char *answer_end = NULL;
int answer_size;
char tmpbuf[100] = {0,};
int actual;
int total = 0;
int done = 0;
if (!strncasecmp("ATZ", cmd, 3)) {
smlTrace(TRACE_INTERNAL, "%s: detected standard ATZ command", __func__);
statusOnly = TRUE;
}
if (!strncasecmp("AT+CPROT=0", cmd, 10)) {
smlTrace(TRACE_INTERNAL, "%s: detected standard AT+CPROT=0 command", __func__);
statusOnly = TRUE;
}
/* send AT command */
fd = userdata->fd;
rspbuf[0] = 0;
if(fd < 0) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The file descriptor for the cable AT command is not legal (%d).",
fd);
goto error;
}
if(cmd != NULL) {
// Write command
gint cmdlen;
cmdlen = strlen(cmd);
if(write(fd, cmd, cmdlen) < cmdlen) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The command cannot be written (completely) to the file descriptor.");
goto error;
}
}
/* read the answer
* the default answer for AT commands is: \nresult\n\nOK\n
*/
while(!done) {
FD_ZERO(&ttyset);
FD_SET(fd, &ttyset);
tv.tv_sec = timeout;
tv.tv_usec = 0;
if(select(fd+1, &ttyset, NULL, NULL, &tv)) {
actual = read(fd, &tmpbuf[total], sizeof(tmpbuf) - total);
if(actual < 0) {
smlErrorSet(error, SML_ERROR_GENERIC,
"read failed with return code %i.",
actual);
goto error;
}
total += actual;
/* Answer didn't come within the length of the buffer. Cancel! */
if(total == sizeof(tmpbuf)) {
smlErrorSet(error, SML_ERROR_NOT_IMPLEMENTED,
"The size of the answer to the AT command %s was too large for the internal buffer.",
VA_STRING(cmd));
goto error;
}
if( (answer = index(tmpbuf, '\n')) && /* \n infront of response */
(answer_end = index(answer+1, '\n')) /* \n at end of repsonse */
) {
/* check for an error */
if (!strncasecmp("ERROR", answer+1, 5)) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The AT command %s failed.",
VA_STRING(cmd));
goto error;
}
/* read the trailing \nOK\n if necessary */
if (statusOnly) {
/* Found end of answer */
done = 1;
} else {
char *ok = NULL;
char *ok_end = NULL;
if ( (ok = index(answer_end+1, '\n')) && /* \n infront of ok */
(ok_end = index(ok+1, '\n')) /* \n at end of ok */
) {
/* Found end of answer */
done = 1;
}
}
}
} else {
/* Anser didn't come in time. Cancel */
smlErrorSet(error, SML_ERROR_INTERNAL_IO_ERROR,
"The read operation for the answer of the AT command was timed out.");
goto error;
}
}
smlTrace(TRACE_INTERNAL, "%s: answer: %s", __func__, VA_STRING(answer));
// Remove heading and trailing \r
if((*answer_end == '\r') || (*answer_end == '\n'))
answer_end--;
if((*answer_end == '\r') || (*answer_end == '\n'))
answer_end--;
if((*answer == '\r') || (*answer == '\n'))
answer++;
if((*answer == '\r') || (*answer == '\n'))
answer++;
answer_size = (answer_end) - answer +1;
smlTrace(TRACE_INTERNAL, "%s: answer size=%d", __func__, answer_size);
if( (answer_size) >= rspbuflen ) {
smlErrorSet(error, SML_ERROR_NOT_IMPLEMENTED,
"The size of the answer to the AT command %s was too large.",
VA_STRING(cmd));
goto error;
}
strncpy(rspbuf, answer, answer_size);
rspbuf[answer_size] = 0;
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
static int smlTransportObexClientCableDisconnect(obex_t *handle, gpointer ud) {
smlTrace(TRACE_INTERNAL, "%s(%p, %p)", __func__, handle, ud);
SmlTransportObexClientEnv *userdata = (SmlTransportObexClientEnv *) ud;
if (userdata->fd >= 0) {
// Send a break to get out of OBEX-mode
#ifdef TCSBRKP
if(ioctl(userdata->fd, TCSBRKP, 0) < 0) {
#elif defined(TCSBRK)
if(ioctl(userdata->fd, TCSBRK, 0) < 0) {
#else
if(tcsendbreak(userdata->fd, 0) < 0) {
#endif /* TCSBRKP */
smlTrace(TRACE_INTERNAL, "%s: Unable to send break!", __func__);
}
tcsetattr(userdata->fd, TCSANOW, &userdata->oldtio);
close(userdata->fd);
}
return 0;
}
SmlTransportObexVendorType smlTransportObexClientGetVendor(const char *manufacturer)
{
smlTrace(TRACE_ENTRY, "%s(%s)", __func__, VA_STRING(manufacturer));
smlAssert(manufacturer);
SmlTransportObexVendorType vendor = SML_OBEX_VENDOR_UNKNOWN;
char *big = g_ascii_strup(manufacturer, -1);
if (strstr(big, "SAMSUNG") != NULL) {
smlTrace(TRACE_INTERNAL, "%s - Samsung found.", __func__);
vendor = SML_OBEX_VENDOR_SAMSUNG;
}
smlSafeCFree(&big);
smlTrace(TRACE_EXIT, "%s - %d", __func__, vendor);
return vendor;
}
static int smlTransportObexClientCableConnect(obex_t *handle, gpointer ud)
{
smlTrace(TRACE_ENTRY, "%s", __func__);
SmlError *error = NULL;
SmlTransportObexClientEnv *userdata = (SmlTransportObexClientEnv *) ud;
struct termios newtio;
char rspbuf[201];
rspbuf[sizeof(rspbuf)-1] = 0; /* sanitize for %s in printf format */
userdata->fd = open(userdata->path, O_RDWR|O_NONBLOCK|O_NOCTTY);
if (userdata->fd < 0) {
smlErrorSet(&error, SML_ERROR_INTERNAL_IO_ERROR,
"A valid file descriptor must be non-negative.");
goto error;
}
tcgetattr(userdata->fd, &userdata->oldtio);
bzero(&newtio, sizeof(struct termios));
newtio.c_cflag = B115200 | CLOCAL | CS8 | CREAD | CRTSCTS;
newtio.c_cc[VMIN] = 1;
newtio.c_cc[VTIME] = 0;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
tcflush(userdata->fd, TCIFLUSH);
tcsetattr(userdata->fd, TCSANOW, &newtio);
/* init the communication line */
if (!obex_cable_at(userdata, "ATZ\r", rspbuf, sizeof(rspbuf) - 1, 1, &error))
goto error;
if (strcasecmp("OK", rspbuf)) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The ATZ command was answered with %s.",
rspbuf);
goto error;
}
if (!userdata->at_command && !userdata->manufacturer)
{
/* get the manufacturer */
if (!obex_cable_at(userdata, "AT+CGMI\r", rspbuf, sizeof(rspbuf), 1, &error)) {
smlTrace(TRACE_ERROR, "%s: Comm-error sending AT+CGMI\\r", __func__);
goto error;
}
userdata->manufacturer = g_strdup(rspbuf);
smlTrace(TRACE_ERROR, "%s: manufacturer %s", __func__, VA_STRING(userdata->manufacturer));
}
if (!userdata->at_command && !userdata->model)
{
/* get the model */
if (!obex_cable_at(userdata, "AT+CGMM\r", rspbuf, sizeof(rspbuf), 1, &error)) {
smlTrace(TRACE_ERROR, "%s: Comm-error sending AT+CGMM\\r", __func__);
goto error;
}
userdata->model = g_strdup(rspbuf);
smlTrace(TRACE_ERROR, "%s: model %s", __func__, VA_STRING(userdata->model));
}
if (!userdata->at_command)
{
/* start vendor specific handling */
SmlTransportObexVendorType vendor = SML_OBEX_VENDOR_UNKNOWN;
if (userdata->manufacturer) {
smlTrace(TRACE_INTERNAL, "%s: Try to find an appropriate AT command", __func__);
vendor = smlTransportObexClientGetVendor(userdata->manufacturer);
}
if (vendor) {
switch(vendor) {
case SML_OBEX_VENDOR_SAMSUNG:
smlTrace(TRACE_INTERNAL, "%s: Samsung detected", __func__);
if (!smlTransportObexVendorSamsungInit(userdata)) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The intialization for Samsung failed.");
goto error;
}
break;
default:
smlTrace(TRACE_ERROR, "%s: Ups, vendor defined but not handled.", __func__);
break;
}
}
}
if (!userdata->at_command) {
/* setup default */
smlTrace(TRACE_INTERNAL, "%s: Setting up default AT command AT+CPROT=0\\r", __func__);
userdata->at_command = g_strdup("AT+CPROT=0\r");
}
/* start the synchronization */
if (!obex_cable_at(userdata, userdata->at_command, rspbuf, sizeof(rspbuf) - 1, 1, &error))
goto error;
if (strcasecmp("CONNECT", rspbuf)) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The command AT+CPROT=0 failed with answer %s.",
rspbuf);
goto error;
}
// fcntl(userdata->fd, F_SETFL, O_NONBLOCK);
return 0;
error:
perror(smlErrorPrint(&error));
smlTransportObexClientCableDisconnect(handle, userdata);
smlTrace(TRACE_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
return -1;
}
static int smlTransportObexClientCableWrite(obex_t *handle, gpointer ud, guint8 *buf, int buflen) {
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %i)", __func__, handle, ud, buf, buflen);
SmlTransportObexClientEnv *userdata = (SmlTransportObexClientEnv *) ud;
int written = 0;
int ret = 0;
while (ret >= 0 && written < buflen) {
ret = write(userdata->fd, buf + written, buflen - written);
if (ret >= 0)
written += ret;
}
smlTrace(TRACE_EXIT, "%s: %i", __func__, written);
return written;
}
gint obex_cable_handleinput(obex_t *handle, gpointer ud, gint timeout) {
smlTrace(TRACE_ENTRY, "%s(%p, %p, %i)", __func__, handle, ud, timeout);
struct timeval to;
fd_set readfds;
char buf[512];
SmlTransportObexClientEnv *userdata;
int ret = 0;
int actual = 0;
userdata = (SmlTransportObexClientEnv*) ud;
FD_ZERO(&readfds);
FD_SET(userdata->fd, &readfds);
to.tv_sec = timeout;
to.tv_usec = 0;
smlTrace(TRACE_INTERNAL, "%s - %i", __func__, __LINE__);
ret = select(userdata->fd + 1, &readfds, NULL, NULL, &to);
if (ret < 1)
goto error;
smlTrace(TRACE_INTERNAL, "%s - %i", __func__, __LINE__);
if ((actual = read(userdata->fd, buf, sizeof(buf))) <= 0)
goto end;
smlTrace(TRACE_INTERNAL, "%s - %i", __func__, __LINE__);
OBEX_CustomDataFeed(handle, (unsigned char *) buf, actual);
end:
smlTrace(TRACE_EXIT, "%s(%p, %p, %i)", __func__, handle, ud, timeout);
return actual;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %i", __func__, ret);
return ret;
}
#endif
static void smlTransportObexClientEvent(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);
SmlTransportObexClientEnv *env = OBEX_GetUserData(handle);
SmlError *error = NULL;
switch (event) {
case OBEX_EV_PROGRESS:
smlTrace(TRACE_INTERNAL, "%s: Progress", __func__);
break;
case OBEX_EV_REQDONE:
smlTrace(TRACE_INTERNAL, "%s: Request Done", __func__);
env->busy = FALSE;
if (obex_rsp != OBEX_RSP_SUCCESS) {
switch(obex_cmd) {
case OBEX_CMD_CONNECT:
smlErrorSet(&error, SML_ERROR_INTERNAL_MISCONFIGURATION,
"The OBEX client cannot connect to the configured device or resource (%s - 0x%x).",
OBEX_ResponseToString(obex_rsp),
obex_rsp);
break;
default:
smlErrorSet(&error, SML_ERROR_INTERNAL_IO_ERROR,
"%s (0x%x)",
OBEX_ResponseToString(obex_rsp),
obex_rsp);
break;
}
goto error;
}
/* Get the connection id if we connected */
switch (obex_cmd) {
case OBEX_CMD_CONNECT:;
uint8_t headertype = 0;
obex_headerdata_t header;
uint32_t len;
char *who = NULL;
while (OBEX_ObjectGetNextHeader(env->obexhandle, 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);
env->connection_id = header.bq4;
break;
case OBEX_HDR_WHO:
who = g_strndup((char *)header.bs, len);
smlTrace(TRACE_INTERNAL, "%s: Found who: %s", __func__, VA_STRING(who));
break;
default:
smlTrace(TRACE_INTERNAL, "%s: Unknown header", __func__);
}
}
if (!env->connection_id) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Missing connection id");
smlSafeCFree(&who);
goto error;
}
smlTrace(TRACE_INTERNAL, "%s: Got who: %s", __func__, VA_STRING(who));
if (!who || strcmp(who, "SYNCML-SYNC")) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Missing or wrong who response");
smlSafeCFree(&who);
goto error;
}
smlSafeCFree(&who);
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_CONNECT_DONE, NULL, NULL);
break;
case OBEX_CMD_DISCONNECT:;
if (!env->isDisconnected)
{
env->busy = FALSE;
env->isDisconnected = TRUE;
OBEX_TransportDisconnect(env->obexhandle);
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
}
break;
case OBEX_CMD_GET:;
smlTrace(TRACE_INTERNAL, "%s: Got GET command done", __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, len %i", __func__, headertype, header.bq4, header.bs, len);
switch (headertype) {
case OBEX_HDR_LENGTH:
smlTrace(TRACE_INTERNAL, "%s: Found length: %d", __func__, header.bq4);
length = header.bq4;
break;
case OBEX_HDR_BODY:
if (!length) {
smlTrace(TRACE_INTERNAL, "%s: Length not given. Calculating it to: %i", __func__, len);
length = len;
}
if (!length) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Got zero length!");
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 (!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, env->mimetype, TRUE, &error);
if (!tspdata)
goto error;
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DATA, tspdata, NULL);
smlTransportDataDeref(tspdata);
break;
}
break;
case OBEX_EV_LINKERR:
if (obex_rsp == 0)
{
/* This is a disconnect. */
if (!env->isDisconnected)
{
env->busy = FALSE;
env->isDisconnected = TRUE;
OBEX_TransportDisconnect(env->obexhandle);
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
}
} else {
/* This is a normal error. */
smlErrorSet(&error, SML_ERROR_GENERIC,
"Link Error: %s - 0x%x",
OBEX_ResponseToString(obex_rsp), obex_rsp);
smlTrace(TRACE_INTERNAL, "%s - %s",
__func__, smlErrorPrint(&error));
goto error;
}
break;
case OBEX_EV_STREAMEMPTY:
smlTrace(TRACE_INTERNAL, "%s: Empty Stream", __func__);
break;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error:
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
env->busy = FALSE;
env->error = TRUE;
return;
}
static SmlBool smlTransportObexClientSetConfigOption(
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);
smlAssert(name); /* should be checked by smlTransportSetConfigOption */
SmlTransportObexClientEnv *env = tsp->transport_data;
/* This transport does not accept NULL as value. */
if (!value)
{
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
"The configuration option %s requires a value.", name);
goto error;
}
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_BLUETOOTH_ADDRESS)) {
/* This is the MAC of the remote device. */
env->path = g_strdup(value);
smlTrace(TRACE_INTERNAL, "%s: Bluetooth MAC %s detected", __func__, VA_STRING(env->path));
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_PORT)) {
if (env->type == SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH)
g_warning("Please use %s instead of %s.",
SML_TRANSPORT_CONFIG_BLUETOOTH_CHANNEL,
SML_TRANSPORT_CONFIG_PORT);
env->port = atoi(value);
smlTrace(TRACE_INTERNAL, "%s: Port or Bluetooth channel %i detected",
__func__, env->port);
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_URL)) {
if (env->type == SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH)
g_warning("Please use %s instead of %s.",
SML_TRANSPORT_CONFIG_BLUETOOTH_ADDRESS,
SML_TRANSPORT_CONFIG_URL);
env->path = g_strdup(value);
smlTrace(TRACE_INTERNAL, "%s: URL or Bluetooth MAC %s detected",
__func__, VA_STRING(env->path));
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_IRDA_SERVICE)) {
if (env->irda_service)
smlSafeCFree(&(env->irda_service));
env->irda_service = g_strdup(value);
smlTrace(TRACE_INTERNAL, "%s: IrDA service %s detected", __func__, VA_STRING(env->irda_service));
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_AT_COMMAND)) {
if (env->at_command)
smlSafeCFree(&(env->at_command));
env->at_command = g_strdup(value);
smlTrace(TRACE_INTERNAL, "%s: AT command %s detected", __func__, VA_STRING(env->at_command));
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_AT_MANUFACTURER)) {
if (env->manufacturer)
smlSafeCFree(&(env->manufacturer));
env->manufacturer = g_strdup(value);
smlTrace(TRACE_INTERNAL, "%s: AT manufacturer %s detected", __func__, VA_STRING(env->manufacturer));
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_AT_MODEL)) {
if (env->model)
smlSafeCFree(&(env->model));
env->model = g_strdup(value);
smlTrace(TRACE_INTERNAL, "%s: AT model %s detected", __func__, VA_STRING(env->model));
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_DATASTORE)) {
/* This is needed for vendors which signal the datastore with AT commands. */
SmlTransportObexDatastoreType datastore = SML_TRANSPORT_OBEX_DATASTORE_UNKNOWN;
if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_EVENT, value))
datastore = SML_TRANSPORT_OBEX_DATASTORE_EVENT;
if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_TODO, value))
datastore = SML_TRANSPORT_OBEX_DATASTORE_TODO;
if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_CONTACT, value))
datastore = SML_TRANSPORT_OBEX_DATASTORE_CONTACT;
if (!strcasecmp(SML_TRANSPORT_CONFIG_DATASTORE_NOTE, value))
datastore = SML_TRANSPORT_OBEX_DATASTORE_NOTE;
if (!datastore) {
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "Unknown datastore %s found.", value);
goto error;
}
SmlTransportObexDatastoreType *type = smlTryMalloc0(sizeof(SmlTransportObexDatastoreType), error);
if (!type)
goto error;
*type = datastore;
env->datastores = g_list_append(env->datastores, type);
smlTrace(TRACE_INTERNAL, "%s: Datastore %i detected", __func__, datastore);
} else {
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "Unknown parameter %s found.", name);
goto error;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool smlTransportObexClientSetConnectionType(
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);
SmlTransportObexClientEnv *env = tsp->transport_data;
env->type = type;
switch (env->type) {
case SML_TRANSPORT_CONNECTION_TYPE_NET:
env->obexhandle = OBEX_Init(OBEX_TRANS_FD, smlTransportObexClientEvent, 0);
break;
case SML_TRANSPORT_CONNECTION_TYPE_IRDA:
env->obexhandle = OBEX_Init(OBEX_TRANS_IRDA, smlTransportObexClientEvent, 0);
break;
case SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH:
env->obexhandle = OBEX_Init(OBEX_TRANS_BLUETOOTH, smlTransportObexClientEvent, 0);
break;
case SML_TRANSPORT_CONNECTION_TYPE_SERIAL:
env->obexhandle = OBEX_Init(OBEX_TRANS_CUSTOM, smlTransportObexClientEvent, 0);
break;
case SML_TRANSPORT_CONNECTION_TYPE_USB:
env->obexhandle = OBEX_Init(OBEX_TRANS_USB, smlTransportObexClientEvent, 0);
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown obex type");
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
if (!env->obexhandle) {
smlErrorSet(error, SML_ERROR_GENERIC, "Unable to open connection");
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
#ifndef WIN32
if (env->type == SML_TRANSPORT_CONNECTION_TYPE_SERIAL) {
obex_ctrans_t cabletrans = {
smlTransportObexClientCableConnect,
smlTransportObexClientCableDisconnect,
NULL,
smlTransportObexClientCableWrite,
obex_cable_handleinput,
env};
OBEX_RegisterCTransport(env->obexhandle, &cabletrans);
}
#endif
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
static SmlBool smlTransportObexClientInit(SmlTransport *tsp, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
CHECK_ERROR_REF
smlAssert(tsp);
smlAssert(tsp->transport_data);
SmlTransportObexClientEnv *env = tsp->transport_data;
OBEX_SetUserData(env->obexhandle, env);
switch (env->type) {
case SML_TRANSPORT_CONNECTION_TYPE_NET:
if (!env->path) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The hostname or address is missing.");
goto error;
}
break;
case SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH:
if (!env->path) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The bluetooth address is missing.");
goto error;
}
#ifndef ENABLE_BLUETOOTH_SDPLIB
if (!env->port) {
smlErrorSet(error, SML_ERROR_GENERIC,
"The bluetooth channel is missing.");
goto error;
}
#endif
break;
default:
/* nothing to check here */
break;
}
smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
return TRUE;
error:
OBEX_Cleanup(env->obexhandle);
if (env->path)
smlSafeCFree(&(env->path));
if (env->at_command)
smlSafeCFree(&(env->at_command));
if (env->manufacturer)
smlSafeCFree(&(env->manufacturer));
if (env->model)
smlSafeCFree(&(env->model));
smlSafeFree((gpointer *)&env);
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool smlTransportObexClientFinalize(void *data, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, error);
CHECK_ERROR_REF
smlAssert(data);
SmlTransportObexClientEnv *env = data;
smlAssert(env->tsp);
if (env->path) smlSafeCFree(&(env->path));
OBEX_Cleanup(env->obexhandle);
if (env->irda_service)
smlSafeCFree(&(env->irda_service));
if (env->at_command)
smlSafeCFree(&(env->at_command));
if (env->manufacturer)
smlSafeCFree(&(env->manufacturer));
if (env->model)
smlSafeCFree(&(env->model));
while (env->datastores) {
SmlTransportObexDatastoreType *type = env->datastores->data;
env->datastores = g_list_remove(env->datastores, type);
smlSafeFree((gpointer *) &type);
}
smlSafeFree((gpointer *)&env);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
static void smlTransportObexClientConnect(void *data)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
smlAssert(data);
SmlTransportObexClientEnv *env = data;
int fd = 0;
SmlError *error = NULL;
unsigned int obex_intf_cnt;
obex_interface_t *obex_intf;
env->isDisconnected = FALSE;
if (env->type == SML_TRANSPORT_CONNECTION_TYPE_NET) {
smlTrace(TRACE_INTERNAL, "%s: connecting to inet address %s:%d",
__func__, VA_STRING(env->path), env->port);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(env->port);
struct hostent *hostinfo = gethostbyname (env->path);
if (!hostinfo) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown host %s", env->path);
goto error;
}
addr.sin_addr = *(struct in_addr *) hostinfo->h_addr_list[0];
fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Cannot create socket: %s", strerror(errno));
goto error;
}
smlTrace(TRACE_INTERNAL, "%s: socket %i", __func__, fd);
char *addrstr = inet_ntoa(addr.sin_addr);
smlTrace(TRACE_INTERNAL, "%s: peer addr = %d %s %i", __func__, hostinfo->h_addr_list[0], VA_STRING(addrstr), env->port);
if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Cannot connect socket: %s", strerror(errno));
goto error_close;
}
smlTrace(TRACE_INTERNAL, "%s: connect done", __func__);
} else if (env->type == SML_TRANSPORT_CONNECTION_TYPE_USB) {
smlTrace(TRACE_INTERNAL, "%s: connecting to usb interface %i", __func__, env->port);
obex_intf_cnt = OBEX_EnumerateInterfaces(env->obexhandle);
smlTrace(TRACE_INTERNAL, "%s: found %i interfaces", __func__, obex_intf_cnt);
if (obex_intf_cnt <= 0) {
smlErrorSet(&error, SML_ERROR_GENERIC, "There are no valid USB interfaces.");
goto error;
} else if (env->port >= obex_intf_cnt) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to find the USB interface number %i", env->port);
goto error;
} else {
if (GET_OBEX_RESULT(OBEX_InterfaceConnect(env->obexhandle, &obex_intf[env->port])) < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The interface cannot be connected. %s (%i).",
strerror(errno), errno);
goto error;
}
}
smlTrace(TRACE_INTERNAL, "%s: usb connect done", __func__);
} else if (env->type == SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH) {
#ifdef ENABLE_BLUETOOTH
smlTrace(TRACE_INTERNAL, "%s: connecting to bluetooth device %s channel %i", __func__, VA_STRING(env->path), env->port);
uint8_t channel = env->port;
bdaddr_t bdaddr;
str2ba(env->path, &bdaddr);
#ifdef ENABLE_BLUETOOTH_SDPLIB
if (channel == 0) {
/* FIXME: Is this code really memory leak free !? */
sdp_session_t *sdp_session = sdp_connect(BDADDR_ANY, &bdaddr, SDP_RETRY_IF_BUSY);
if (!sdp_session) {
smlErrorSet(&error, SML_ERROR_GENERIC, "The Bluetooth connect to search for the channel failed.");
goto error;
}
unsigned char syncml_client_uuid[] = {
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02};
uuid_t uuid;
uint32_t range = SDP_ATTR_PROTO_DESC_LIST;
sdp_uuid128_create(&uuid, syncml_client_uuid);
sdp_list_t *attribute = sdp_list_append(0, &range);
sdp_list_t *class = sdp_list_append(0, &uuid);
sdp_list_t *list;
if (sdp_service_search_attr_req(sdp_session, class, SDP_ATTR_REQ_INDIVIDUAL, attribute, &list) < 0) {
sdp_close(sdp_session);
sdp_list_free(attribute, 0);
sdp_list_free(class, 0);
smlErrorSet(
&error, SML_ERROR_GENERIC,
"The service search on the Bluetooth device failed.");
goto error;
}
sdp_list_free(attribute, 0);
sdp_list_free(class, 0);
sdp_list_t *iterator;
for(iterator = list; iterator; iterator = iterator->next) {
sdp_record_t *item = (sdp_record_t *) iterator->data;
sdp_list_t *protos = NULL;
sdp_get_access_protos(item, &protos);
if(protos) {
channel = sdp_get_proto_port(protos, RFCOMM_UUID);
}
sdp_record_free(item);
}
sdp_list_free(list, 0);
sdp_close(sdp_session);
sleep(1);
smlTrace(TRACE_INTERNAL,
"%s: SDP SyncML channel for %s: %d",
__func__, env->path, channel);
}
#endif
if (GET_OBEX_RESULT(BtOBEX_TransportConnect(env->obexhandle, BDADDR_ANY, &bdaddr, channel)) < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The Bluetooth connect failed. %s (%i).",
strerror(errno), errno);
goto error;
}
smlTrace(TRACE_INTERNAL, "%s: bluetooth connect done", __func__);
#else
smlErrorSet(&error, SML_ERROR_GENERIC, "Bluetooth not enabled");
goto error;
#endif
} else if (env->type == SML_TRANSPORT_CONNECTION_TYPE_IRDA) {
smlTrace(TRACE_INTERNAL, "%s: connecting to IrDA service %s", __func__, env->irda_service ? env->irda_service : "OBEX");
if (GET_OBEX_RESULT(IrOBEX_TransportConnect(env->obexhandle, env->irda_service)) < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The IrDA connect failed. %s (%i).",
strerror(errno), errno);
goto error;
}
smlTrace(TRACE_INTERNAL, "%s: IrDA connect done", __func__);
} else {
#ifndef WIN32
struct termios tio;
memset(&tio, 0, sizeof(tio));
/* Open the file descriptor */
fd = open(env->path, O_RDWR | O_NOCTTY);
if (fd == -1) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to open %s: %s", env->path, strerror(errno));
goto error;
}
/* Lock it*/
if (lockf(fd, F_TLOCK, 0)) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to lock %s: %s", env->path, strerror(errno));
goto error_close;
}
if (tcgetattr(fd, &tio)) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to get attr from %s", env->path);
goto error_unlock;
}
/* Make the transmission raw (8 bit clean, no echo etc) */
/* Set the speed to a higher value (9600 would be default) */
#if defined(sun) && defined(__SVR4)
tio.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
tio.c_oflag &= ~OPOST;
tio.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
tio.c_cflag &= ~(CSIZE|PARENB);
tio.c_cflag |= CS8;
#else
cfmakeraw(&tio);
#endif
cfsetispeed(&tio, B115200);
cfsetospeed(&tio, B115200);
if (tcsetattr(fd, TCSANOW, &tio)) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to set attr from %s", env->path);
goto error_unlock;
}
tcflush(fd, TCIFLUSH);
#endif
}
if (env->type != SML_TRANSPORT_CONNECTION_TYPE_USB
&& env->type != SML_TRANSPORT_CONNECTION_TYPE_BLUETOOTH
&& env->type != SML_TRANSPORT_CONNECTION_TYPE_IRDA) {
/* Start the obex transport */
if (GET_OBEX_RESULT(FdOBEX_TransportSetup(env->obexhandle, fd, fd, 4096)) < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The OBEX transport setup failed. %s (%i).",
strerror(errno), errno);
goto error_unlock;
}
}
/* Make a new connect object */
obex_object_t *obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_CONNECT);
if (!obj) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to create new connect object");
goto error_transport_close;
}
/* Now add the header for the sync target */
obex_headerdata_t header;
header.bs = (unsigned char *)"SYNCML-SYNC";
OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TARGET, header, strlen((char *)header.bs), OBEX_FL_FIT_ONE_PACKET);
env->busy = TRUE;
/* Now we need to send the request */
if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The OBEX request failed. %s (%i).",
strerror(errno), errno);
goto error_free_obj;
}
if (env->error) {
smlTrace(TRACE_EXIT, "%s: Unable to send connect request. Bailing out", __func__);
return;
}
/* Lets see if it was successfull */
while (env->busy) {
int result = OBEX_HandleInput(env->obexhandle, 20);
if (result < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The connect request was not successful (%i).",
result);
goto error;
} else if (result == 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The connect request was timed out.");
goto error;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error_free_obj:
OBEX_ObjectDelete(env->obexhandle, obj);
error_transport_close:
OBEX_Cleanup(env->obexhandle);
error_unlock:
#ifndef WIN32
if (!lockf(fd, F_ULOCK, 0))
smlTrace(TRACE_ERROR, "%s: error_unlock failed.", __func__);
#endif
error_close:
close(fd);
error:
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
}
static void smlTransportObexClientDisconnect(void *data, void *linkdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, linkdata);
smlAssert(data);
SmlTransportObexClientEnv *env = data;
SmlError *error = NULL;
if (env->isDisconnected) {
/* This is a bug. If the transport is disconnected
* then there is no reason for a second disconnect.
* Nevertheless this mistake should not be fatal.
*/
smlTrace(TRACE_EXIT_ERROR,
"%s - disconnect called on already disconnected transport",
__func__);
g_warning("%s called on disconnected OBEX connection.", __func__);
return;
}
env->error = FALSE;
/* Make a new disconnect object */
obex_object_t *obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_DISCONNECT);
if (!obj) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to create new disconnect object");
goto error;
}
/* Add the connection id */
obex_headerdata_t header;
header.bq4 = env->connection_id;
OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_CONNECTION, header, sizeof(env->connection_id), OBEX_FL_FIT_ONE_PACKET);
env->busy = TRUE;
/* Now we need to send the request */
if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The OBEX request cannot be sent. %s (%i).",
strerror(errno), errno);
goto error_free_obj;
}
if (env->error) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to send disconnect request. Bailing out");
goto error;
}
/* Lets see if it was successfull. We wait a certain period of
* time (3 sec) for the answer to the disconnect. If we dont receive
* the answer we just disconnect anyways. Some phones never answer
* with a A0 (success) to the disconnect request, so we must have
* a timeout here */
int maxLoop = 3;
int i = 0;
while (env->busy) {
smlTrace(TRACE_INTERNAL, "%s: Disconnect loop %i", __func__, i);
int result = OBEX_HandleInput(env->obexhandle, 1);
/* timeout and success are not relevant */
if (result < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The disconnect request was not accepted (%i).",
result);
goto error;
} else if (result == 0 && !env->busy && !env->isDisconnected) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The disconnect request was timed out.");
goto error;
}
if (i == maxLoop) {
smlTrace(TRACE_INTERNAL, "%s: Did not receive a response to our disconnect", __func__);
break;
}
i++;
}
/* Note: The disconnect is signaled by OBEX_EV_LINKERR or OBEX_CMD_DISCONNECT.
* We only signal disconnect here if a phone does not answer. */
if (!env->isDisconnected)
{
env->busy = FALSE;
env->isDisconnected = TRUE;
OBEX_TransportDisconnect(env->obexhandle);
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
}
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error_free_obj:
OBEX_ObjectDelete(env->obexhandle, obj);
error:
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
}
static void smlTransportObexClientSend(void *userdata, void *link_, SmlTransportData *data, SmlError *error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, userdata, link_, data, error);
smlAssert(error || data);
smlAssert(userdata);
SmlTransportObexClientEnv *env = userdata;
smlAssert(env);
if (error)
goto error;
env->error = FALSE;
env->mimetype = data->type;
/* Make a new put command */
obex_object_t *obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_PUT);
if (!obj) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to create new put object");
goto error;
}
/* Now add the header for the put target */
obex_headerdata_t header;
smlTrace(TRACE_INTERNAL, "%s: Adding connection id %i", __func__, env->connection_id);
/* Add the connection id */
header.bq4 = env->connection_id;
OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_CONNECTION, header, sizeof(env->connection_id), OBEX_FL_FIT_ONE_PACKET);
const char *target = NULL;
switch (data->type) {
case SML_MIMETYPE_WBXML:
target = SML_ELEMENT_WBXML;
break;
case SML_MIMETYPE_XML:
target = SML_ELEMENT_XML;
break;
case SML_MIMETYPE_SAN:
target = SML_ELEMENT_SAN;
break;
default:
smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mime type");
goto error_free_obj;
}
smlTrace(TRACE_INTERNAL, "%s: Target %s", __func__, VA_STRING(target));
/* Now convert to unicode. It requires 2 bytes per char + end*/
/*unsigned char *unicode = g_malloc0(2 * strlen(target) + 2);
unsigned int unicodesize = OBEX_CharToUnicode(unicode, (const unsigned char *)target, 2 * strlen(target) + 2);
if (unicodesize == -1) {
smlErrorSet(&error, SML_ERROR_GENERIC, "unable to convert to unicode");
goto error_free_obj;
}*/
/* Now add the target mime type */
/*header.bs = (unsigned char *)unicode;
OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, unicodesize, 0);*/
header.bs = (unsigned char *)target;
OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, strlen(target) + 1, 0);
/* Now the data and size */
header.bq4 = (uint32_t)data->size;
OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_LENGTH, header, sizeof(uint32_t), 0);
header.bs = (unsigned char *)data->data;
OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_BODY, header, data->size, 0);
env->busy = TRUE;
/* Now we need to send the request */
if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The OBEX request cannot be sent. %s (%i).",
strerror(errno), errno);
goto error_free_obj;
}
/* Lets see if it was successfull */
while (env->busy) {
int result = OBEX_HandleInput(env->obexhandle, 20);
if (result < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The request was not successful (%d).",
result);
goto error;
} else if (result == 0 && !env->busy) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The request was timed out.");
goto error;
}
}
if (env->error) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to send put request. Bailing out");
goto error;
}
smlTrace(TRACE_INTERNAL, "%s: Done sending the put request", __func__);
if (!data->needsAnswer) {
smlTrace(TRACE_EXIT, "%s: No answer is needed", __func__);
return;
}
/* Make a new get command */
obj = OBEX_ObjectNew(env->obexhandle, OBEX_CMD_GET);
if (!obj) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to create new get object");
goto error;
}
/* Now add the header for the get target */
smlTrace(TRACE_INTERNAL, "%s: Adding connection id %i", __func__, env->connection_id);
/* Add the connection id */
header.bq4 = env->connection_id;
OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_CONNECTION, header, sizeof(env->connection_id), OBEX_FL_FIT_ONE_PACKET);
/* If smlTransportData.get_type is set, switch the mimetype for the GET command.
This is needed for SyncML 1.2 SAN notificion:
If this obex send sequence sends SyncML 1.2 SAN notification, switch the
mimetype back to regular mimetype (not the SAN mimetype). The mimtype for the
regular sync got stored in smlTransportData.type_get */
if (data->type_get != SML_MIMETYPE_UNKNOWN)
{
switch (data->type_get) {
case SML_MIMETYPE_WBXML:
target = SML_ELEMENT_WBXML;
break;
case SML_MIMETYPE_XML:
target = SML_ELEMENT_XML;
break;
case SML_MIMETYPE_SAN:
target = SML_ELEMENT_SAN;
break;
default:
smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mime type");
goto error_free_obj;
}
/* After this point, the entire conversion will be XML or WBXML. No more SAN mimetype. */
env->mimetype = data->type_get;
smlTrace(TRACE_INTERNAL, "%s: Switch to new target: %s", __func__, VA_STRING(target));
}
/* Now add the target mime type */
//header.bs = (unsigned char *)unicode;
//OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, unicodesize, 0);
header.bs = (unsigned char *)target;
OBEX_ObjectAddHeader(env->obexhandle, obj, OBEX_HDR_TYPE, header, strlen(target) + 1, 0);
env->busy = TRUE;
/* Now we need to send the request */
if (GET_OBEX_RESULT(OBEX_Request(env->obexhandle, obj)) < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The OBEX request cannot be sent. %s (%i).",
strerror(errno), errno);
goto error_free_obj;
}
/* Lets see if it was successfull */
smlTrace(TRACE_INTERNAL, "%s: Wait for the answer ...", __func__);
while (env->busy) {
int result = OBEX_HandleInput(env->obexhandle, 20);
if (result < 0) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The answer was not received successfully (%i).",
result);
goto error;
} else if (result == 0 && !env->busy) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The answer was timed out.");
goto error;
}
}
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error_free_obj:
OBEX_ObjectDelete(env->obexhandle, obj);
error:
smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
return;
}
SmlBool smlTransportObexClientNew(SmlTransport *tsp, SmlError **error)
{
CHECK_ERROR_REF
smlAssert(tsp);
tsp->functions.set_config_option = smlTransportObexClientSetConfigOption;
tsp->functions.set_connection_type = smlTransportObexClientSetConnectionType;
tsp->functions.initialize = smlTransportObexClientInit;
tsp->functions.finalize = smlTransportObexClientFinalize;
tsp->functions.connect = smlTransportObexClientConnect;
tsp->functions.disconnect = smlTransportObexClientDisconnect;
tsp->functions.send = smlTransportObexClientSend;
SmlTransportObexClientEnv *env = smlTryMalloc0(sizeof(SmlTransportObexClientEnv), error);
if (!env)
return FALSE;
tsp->transport_data = env;
env->tsp = tsp;
return TRUE;
}
smlErrorPrint
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
SmlTransportObexClientEnv
Definition: obex_client_internals.h:48
SmlTransportData::needsAnswer
SmlBool needsAnswer
Definition: sml_transport_internals.h:121
SmlTransport
Definition: sml_transport_internals.h:69
SmlTransportData
Definition: sml_transport_internals.h:112
smlTrace
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
SmlTransportData::type_get
SmlMimeType type_get
Definition: sml_transport_internals.h:132
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