diff --git a/obexd/gwobex/gw-obex.c b/obexd/gwobex/gw-obex.c
new file mode 100644
index 0000000..6549b09
--- /dev/null
+++ b/obexd/gwobex/gw-obex.c
+/**
+ @file gw-obex.c
+
+ @author Johan Hedberg <johan.hedberg@nokia.com>
+
+ Copyright (C) 2004-2006 Nokia Corporation. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License, version 2.1, as published by the Free Software Foundation.
+
+ 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 <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "log.h"
+#include "gw-obex.h"
+#include "utils.h"
+#include "obex-xfer.h"
+#include "obex-priv.h"
+
+
+gboolean gw_obex_get_file(GwObex *ctx,
+ const gchar *local,
+ const gchar *remote,
+ const gchar *type,
+ gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_get(ctx, local, remote, type, NULL, NULL, -1, FALSE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_get_fd(GwObex *ctx,
+ gint fd,
+ const gchar *remote,
+ const gchar *type,
+ gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_get(ctx, NULL, remote, type, NULL, NULL, fd, FALSE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_put_fd(GwObex *ctx,
+ gint fd,
+ const gchar *remote,
+ const gchar *type,
+ gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_put(ctx, NULL, remote, type, NULL, 0, -1, fd, FALSE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_put_file(GwObex *ctx,
+ const gchar *local,
+ const gchar *remote,
+ const gchar *type,
+ gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_put(ctx, local, remote, type, NULL, 0, -1, -1, FALSE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_get_buf(GwObex *ctx,
+ const gchar *remote,
+ const gchar *type,
+ gchar **buf,
+ gint *buf_size,
+ gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_get(ctx, NULL, remote, type, buf, buf_size, -1, FALSE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_put_buf(GwObex *ctx,
+ const gchar *remote,
+ const gchar *type,
+ const gchar *buf,
+ gint buf_size,
+ gint time,
+ gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_put(ctx, NULL, remote, type, buf, buf_size, time, -1, FALSE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_chdir(GwObex *ctx, const gchar *dir, gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_setpath(ctx, dir ? dir : "", 0);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_mkdir(GwObex *ctx, const gchar *dir, gint *error) {
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ if (!gw_obex_setpath(ctx, dir ? dir : "", SETPATH_CREATE)) {
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return FALSE;
+ }
+ (void) gw_obex_setpath(ctx, "..", 0);
+ GW_OBEX_UNLOCK(ctx);
+ return TRUE;
+}
+
+gboolean gw_obex_read_dir(GwObex *ctx, const gchar *dir,
+ gchar **buf, gint *buf_size, gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_get(ctx, NULL, dir ? dir : "", LST_TYPE, buf, buf_size, -1, FALSE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ else if (*buf_size > 0) {
+ /* Hack for some OBEX implementations which send nul's
+ * at the end of the listing */
+ int i;
+
+ for (i = *buf_size - 1; i > 0; i--) {
+ if ((*buf)[i] == '\0')
+ (*buf_size)--;
+ else
+ break;
+ }
+ }
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_delete(GwObex *ctx, const gchar *name, gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_put(ctx, NULL, name, NULL, NULL, 0, -1, -1, FALSE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_move(GwObex *ctx, const gchar *src, const gchar *dst, gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_action_op(ctx, src, dst, OBEX_ACTION_MOVE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_copy(GwObex *ctx, const gchar *src, const gchar *dst, gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_action_op(ctx, src, dst, OBEX_ACTION_COPY);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+gboolean gw_obex_get_capability(GwObex *ctx, gchar **cap, gint *cap_len, gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(FALSE, error, ctx);
+ ret = gw_obex_get(ctx, NULL, NULL, CAP_TYPE, cap, cap_len, -1, FALSE);
+ if (ret == FALSE) {
+ *cap = NULL;
+ *cap_len = 0;
+ gw_obex_get_error(ctx, error);
+ }
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+void gw_obex_set_disconnect_callback(GwObex *ctx, gw_obex_disconnect_cb_t callback, gpointer data) {
+ GW_OBEX_LOCK(ctx);
+ ctx->dc_cb = callback;
+ ctx->dc_data = data;
+ GW_OBEX_UNLOCK(ctx);
+}
+
+void gw_obex_set_progress_callback(GwObex *ctx, gw_obex_progress_cb_t callback, gpointer data) {
+ GW_OBEX_LOCK(ctx);
+ ctx->pr_cb = callback;
+ ctx->pr_data = data;
+ GW_OBEX_UNLOCK(ctx);
+}
+
+void gw_obex_set_cancel_callback(GwObex *ctx, gw_obex_cancel_cb_t callback, gpointer data) {
+ GW_OBEX_LOCK(ctx);
+ ctx->cancel_cb = callback;
+ ctx->cancel_data = data;
+ GW_OBEX_UNLOCK(ctx);
+}
+
+void gw_obex_close(GwObex *ctx) {
+ GW_OBEX_LOCK(ctx);
+ if (ctx->xfer) {
+ GwObexXfer *xfer = ctx->xfer;
+ GW_OBEX_UNLOCK(ctx);
+ gw_obex_xfer_close(ctx->xfer, NULL);
+ GW_OBEX_LOCK(ctx);
+ /* In the async case the caller of put/get_async owns the xfer object */
+ if (!xfer->async)
+ _gw_obex_xfer_free(xfer);
+ ctx->xfer = NULL;
+ }
+ if (ctx->conn_fd >= 0) {
+ if (!gw_obex_disconnect(ctx))
+ debug("OBEX Disconnect command failed\n");
+ OBEX_TransportDisconnect(ctx->handle);
+ close(ctx->conn_fd);
+ ctx->conn_fd = -1;
+ }
+ if (ctx->handle) {
+ OBEX_Cleanup(ctx->handle);
+ ctx->handle = NULL;
+ }
+ if (ctx->gio) {
+ g_io_channel_unref(ctx->gio);
+ ctx->gio = NULL;
+ }
+ if (ctx->gio_source) {
+ g_source_destroy(ctx->gio_source);
+ ctx->gio_source = NULL;
+ }
+ GW_OBEX_UNLOCK(ctx);
+#ifdef G_THREADS_ENABLED
+ g_mutex_free(ctx->mutex);
+ ctx->mutex = NULL;
+#endif
+ g_free(ctx);
+}
+
+GwObex *gw_obex_setup_fd(int fd, const gchar *uuid, gint uuid_len,
+ GMainContext *context, gint *error) {
+ obex_t *handle;
+ GwObex *ctx;
+
+ if (!gw_obex_transport_setup(fd, &handle)) {
+ debug("gw_obex_open() failed\n");
+ if (error)
+ *error = GW_OBEX_ERROR_CONNECT_FAILED;
+ return NULL;
+ }
+
+ debug("Transport connection opened.\n");
+
+ ctx = make_context(handle);
+
+#ifdef G_THREADS_ENABLED
+ if (!g_thread_supported())
+ g_thread_init(NULL);
+ ctx->mutex = g_mutex_new();
+#endif
+
+ OBEX_SetCustomData(handle, ctx);
+
+ debug("Connecting to OBEX service\n");
+ if (!gw_obex_connect(ctx, uuid, uuid_len)) {
+ debug("Unable to connect to OBEX service\n");
+#ifdef G_THREADS_ENABLED
+ g_mutex_free(ctx->mutex);
+ ctx->mutex = NULL;
+#endif
+ g_free(ctx);
+ OBEX_Cleanup(handle);
+ if (error)
+ *error = GW_OBEX_ERROR_NO_SERVICE;
+ return NULL;
+ }
+
+ debug("Connected (Connection ID: %#x)\n", ctx->conid);
+
+ ctx->gio = g_io_channel_unix_new(ctx->conn_fd);
+ ctx->gio_source = g_io_create_watch (ctx->gio,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL);
+ g_source_set_callback(ctx->gio_source, (GSourceFunc)gw_obex_cb, ctx, NULL);
+ (void) g_source_attach(ctx->gio_source, context);
+ g_source_unref(ctx->gio_source);
+
+ ctx->main_ctx = context;
+
+ return ctx;
+}
+
+GwObex *gw_obex_setup_dev(const char *dev, const gchar *uuid, gint uuid_len,
+ GMainContext *context, gint *error) {
+ GwObex *ctx;
+ int fd;
+
+ fd = open(dev, O_RDWR | O_NOCTTY | O_SYNC);
+ if (fd < 0) {
+ debug("open(\"%s\"): %s\n", dev, strerror(errno));
+ if (error)
+ *error = GW_OBEX_ERROR_CONNECT_FAILED;
+ return NULL;
+ }
+
+ if (!fd_raw_mode(fd)) {
+ debug("setting raw mode failed\n");
+ close(fd);
+ if (error)
+ *error = GW_OBEX_ERROR_CONNECT_FAILED;
+ return NULL;
+ }
+
+ ctx = gw_obex_setup_fd(fd, uuid, uuid_len, context, error);
+ if (ctx == NULL) {
+ close(fd);
+ }
+
+ return ctx;
+}
+
diff --git a/obexd/gwobex/gw-obex.h b/obexd/gwobex/gw-obex.h
new file mode 100644
index 0000000..c1be103
--- /dev/null
+++ b/obexd/gwobex/gw-obex.h
+/**
+ @file gw-obex.h
+
+ OSSO GW OBEX Connectivity Library and API
+
+ @author Johan Hedberg <johan.hedberg@nokia.com>
+
+ Copyright (C) 2004-2005 Nokia Corporation. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License, version 2.1, as published by the Free Software Foundation.
+
+ 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.
+
+*/
+#ifndef _GW_OBEX_H_
+#define _GW_OBEX_H_
+
+#include <glib.h>
+#include <openobex/obex.h>
+
+/**
+ * @name GW OBEX Error Codes
+ * The error codes returned by many of the functions refer either to an OBEX
+ * Protocol error or to a GW OBEX error. If the error code is less that 256, it
+ * refers to an OBEX error, othervice it refers to a GW_OBEX_ERROR_* error.
+ * @{
+*/
+
+/** Transport connection was disconnected */
+#define GW_OBEX_ERROR_DISCONNECT 256
+
+/** Operation was aborted */
+#define GW_OBEX_ERROR_ABORT 257
+
+/** GW OBEX internal error */
+#define GW_OBEX_ERROR_INTERNAL 258
+
+/** Unable to connecto to the specified service (UUID) */
+#define GW_OBEX_ERROR_NO_SERVICE 259
+
+/** Unable to create connection */
+#define GW_OBEX_ERROR_CONNECT_FAILED 260
+
+/** Timeout while waiting for data from the remote device */
+#define GW_OBEX_ERROR_TIMEOUT 261
+
+/** Remote device returned invalid/corrupted data */
+#define GW_OBEX_ERROR_INVALID_DATA 262
+
+/** Invalid parameters given to gwobex */
+#define GW_OBEX_ERROR_INVALID_PARAMS 263
+
+/** Local access error (e.g. read/write/open failed for local file) */
+#define GW_OBEX_ERROR_LOCAL_ACCESS 264
+
+/** Another operation is in progress */
+#define GW_OBEX_ERROR_BUSY 265
+
+/** No data currently available */
+#define GW_OBEX_ERROR_NO_DATA 266
+
+
+/** @} */
+
+/** Value used if target length for put or get is not known */
+#define GW_OBEX_UNKNOWN_LENGTH -1
+
+/** Standard folder browsing service UUID (give this as a parameter to
+ * gw_obex_setup_* to connect to folder browsing service */
+#define OBEX_FTP_UUID \
+ "\xF9\xEC\x7B\xC4\x95\x3C\x11\xD2\x98\x4E\x52\x54\x00\xDC\x9E\x09"
+/** Length of OBEX_FTP_UUID */
+#define OBEX_FTP_UUID_LEN 16
+
+/** Phone Book Access Profile UUID */
+#define OBEX_PBAP_UUID \
+ "\x79\x61\x35\xF0\xF0\xC5\x11\xD8\x09\x66\x08\x00\x20\x0C\x9A\x66"
+/** Length of OBEX_PBAP_UUID */
+#define OBEX_PBAP_UUID_LEN 16
+
+/** Struct containing the context of a gwobex connection */
+typedef struct gw_obex GwObex;
+
+/** Objecct transfer handle */
+typedef struct gw_obex_xfer GwObexXfer;
+
+/** Callback type for ongoing transfers
+ * @param ctx GwObexXfer pointer for the transfer
+ * @param data Optional pointer to user data
+ */
+typedef void (*gw_obex_xfer_cb_t) (GwObexXfer *xfer,
+ gpointer data);
+
+/** Callback type for transport connection loss
+ * @param ctx GwObex pointer for the connection
+ * @param data Optional pointer to user data
+ */
+typedef void (*gw_obex_disconnect_cb_t) (GwObex *ctx,
+ gpointer data);
+
+/** Callback type for progress information
+ * Only used for the synchronous transfer functions.
+ * @param ctx GwObex pointer for the connection
+ * @param obex_cmd eg. OBEX_CMD_PUT
+ * @param current Bytes transfered
+ * @param target Total length (or GW_OBEX_UNKNOWN_LENGTH)
+ * @param data Optional pointer to user data
+ */
+typedef void (*gw_obex_progress_cb_t) (GwObex *ctx, gint obex_cmd,
+ gint current, gint target,
+ gpointer data);
+
+/** Callback type for checking if the operation should be canceled.
+ * Only used for the synchronous functions.
+ * In the GNOME VFS case the callback function should be
+ * gnome_vfs_cancellation_check().
+ * @param data Optional pointer to user data
+ * @returns TRUE if the operation should be canceled, FALSE othervice
+ */
+typedef gboolean (*gw_obex_cancel_cb_t) (gpointer data);
+
+
+/**
+ * @name Functions for connecting and disconnecting
+ * With these functions you can create and and disconnect connections. You can
+ * either connect using a filename (e.g. "/dev/rfcomm0") or using a file
+ * descriptor (e.g. a RFCOMM socket).
+ * @{
+ */
+
+/** Open connection using a local device node and setup parameters.
+ * This function should be called before calling any other functions. The
+ * pointer returned by this function should be passed to the other functions.
+ *
+ * @param device The local device which should be opened for the connection
+ * @param uuid UUID of service to connect to. NULL for the default service
+ * (INBOX).
+ * @param uuid_len Length (in bytes) of UUID
+ * @param context GMainContext to attach to (or NULL for the default one)
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns A pointer, NULL on failure
+ * This pointer should be passed to the other obex_* functions.
+ **/
+GwObex *gw_obex_setup_dev(const gchar *device,
+ const gchar *uuid,
+ gint uuid_len,
+ GMainContext *context,
+ gint *error);
+
+
+/** Setup OBEX connection using an opened file descriptor
+ * This function should be called before calling any other functions. The
+ * pointer returned by this function should be passed to the other functions.
+ *
+ * @param fd Opened file descriptor to use for the connection
+ * @param uuid UUID of service to connect to. NULL for the default service
+ * (INBOX).
+ * @param uuid_len Length (in bytes) of UUID
+ * @param context GMainContext to attach to (or NULL for the default one)
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns A pointer, NULL on failure
+ * This pointer should be passed to the other obex_* functions.
+ **/
+GwObex *gw_obex_setup_fd(int fd,
+ const gchar *uuid,
+ gint uuid_len,
+ GMainContext *context,
+ gint *error);
+
+
+/** Close GW OBEX connection and free all memory associated with it.
+ *
+ * @param ctx Pointer returned by gw_obex_setup().
+ * Cannot be used anymore after this calling this function.
+ */
+void gw_obex_close(GwObex *ctx);
+
+/** @} */
+
+/**
+ * @name Registering callback functions
+ * With these functions you can register your own callback functions
+ * to gwobex to receive indications about special events.
+ * @{
+ */
+
+/** Set function to be called when a disconnection happens.
+ * You may (and probably should) call gw_obex_close() if this function is
+ * called.
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param callback Function to call
+ * @param data Optional data to pass to the callback function
+ */
+void gw_obex_set_disconnect_callback(GwObex *ctx,
+ gw_obex_disconnect_cb_t callback,
+ gpointer data);
+
+
+/** Set function to be called when progress for a put or get operation happens.
+ * Only used for the synchronous transfer functions.
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param callback Function to call
+ * @param data Optional data to pass to the callback function
+ */
+void gw_obex_set_progress_callback(GwObex *ctx,
+ gw_obex_progress_cb_t callback,
+ gpointer data);
+
+
+/** Set function to be called to check if the current operation should be
+ * canceled. In the GNOME VFS case the callback function should be
+ * gnome_vfs_cancellation_check(). The callback function should return TRUE if
+ * the operation should be canceled and FALSE othervice.
+ *
+ * Only used for the synchronous transfer functions.
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param callback Function to call
+ * @param data Pointer to pass to the callback function
+ */
+void gw_obex_set_cancel_callback(GwObex *ctx,
+ gw_obex_cancel_cb_t callback,
+ gpointer data);
+
+/** @} */
+
+/**
+ * @name Functions for performing synchronous remote operations
+ * Once you have setup a connection using one of the gw_obex_setup_* functions,
+ * you can perform different remote transactions using these functions.
+ * @{
+ */
+
+/** Get the capability object from the connected remote device.
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param cap Place to store the fetched object.
+ * g_free() when not needed anymore.
+ * @param cap_len Place to store the size of the fetched object
+ * @param error Place to store a possible error code
+ * (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_get_capability(GwObex *ctx,
+ gchar **cap,
+ gint *cap_len,
+ gint *error);
+
+
+/** Get a file from the remote device.
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param local Local filename (null terminated UTF-8)
+ * @param remote Remote filename (null terminated UTF-8)
+ * @param type MIME-type of the object
+ * @param error Place to store error code on failure
+ * (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_get_file(GwObex *ctx,
+ const gchar *local,
+ const gchar *remote,
+ const gchar *type,
+ gint *error);
+
+
+/** Send a file to the remote device.
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param local Local filename (null terminated UTF-8)
+ * @param remote Remote filename (null terminated UTF-8)
+ * @param type MIME-type of the object
+ * @param error Place to store error code on failure
+ * (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_put_file(GwObex *ctx,
+ const gchar *local,
+ const gchar *remote,
+ const gchar *type,
+ gint *error);
+
+
+/** Get a file from the remote device and write it to a file descriptor
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param fd File descriptor to write the file into
+ * @param remote Remote filename (null terminated UTF-8)
+ * @param type MIME-type of the object
+ * @param error Place to store error code on failure
+ * (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_get_fd(GwObex *ctx, gint fd,
+ const gchar *remote,
+ const gchar *type,
+ gint *error);
+
+/** Read data from a file descriptor and send it to the remote device
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param fd File descriptor to read the data from
+ * @param remote Remote filename (null terminated UTF-8)
+ * @param type MIME-type of the object
+ * @param error Place to store error code on failure
+ * (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_put_fd(GwObex *ctx, gint fd,
+ const gchar *remote,
+ const gchar *type,
+ gint *error);
+
+/** Get an object from the remote device and store it in a memory buffer.
+ * Either remote filename or type must be supplied (or both).
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param remote Remote filename (null terminated UTF-8)
+ * @param type MIME-type of the object
+ * @param buf Buffer to store the object in.
+ * g_free() when not needed anymore.
+ * @param buf_size Place to store length of fetched object
+ * @param error Place to store error code on failure
+ * (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_get_buf(GwObex *ctx, const gchar *remote, const gchar *type,
+ gchar **buf, gint *buf_size, gint *error);
+
+
+/** Send a object located in a memory buffer to the remote device.
+ * Either remote filename or type must be supplied (or both)
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param remote Remote filename (null terminated UTF-8)
+ * @param type MIME-type of the object
+ * @param buf Buffer containing the object
+ * @param buf_size Buffer (object) size
+ * @param time Last modification time of object (or -1 if not known)
+ * @param error Place to store error code on failure
+ * (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_put_buf(GwObex *ctx, const gchar *remote, const gchar *type,
+ const gchar *buf, gint buf_size, gint time, gint *error);
+
+
+/** Change directory (relative to the current one).
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param dir New directory to change to (null terminated UTF-8),
+ * ".." to go up, NULL to go to the root folder
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_chdir(GwObex *ctx, const gchar *dir, gint *error);
+
+
+/** Create a new directory.
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param dir Directory to create (null terminated UTF-8)
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_mkdir(GwObex *ctx, const gchar *dir, gint *error);
+
+
+/** Get folder listing for the specified directory.
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param dir Directory to list (null terminated UTF-8),
+ * NULL to list current directory
+ * @param buf Place to store the folder-listing object
+ * @param buf_size Place to store the size for the retrieved object
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_read_dir(GwObex *ctx, const gchar *dir,
+ gchar **buf, gint *buf_size, gint *error);
+
+
+/** Remove a file from the remote device.
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param name Filename to remove (null terminated UTF-8)
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_delete(GwObex *ctx, const gchar *name, gint *error);
+
+
+/** Move/Rename a file on the remote device.
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param src Source filename (null terminated UTF-8)
+ * @param dst Destination filename (null terminated UTF-8)
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_move(GwObex *ctx, const gchar *src, const gchar *dst,
+ gint *error);
+
+
+/** Copy a file on the remote device.
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param src Source filename (null terminated UTF-8)
+ * @param dst Destination filename (null terminated UTF-8)
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_copy(GwObex *ctx, const gchar *src, const gchar *dst,
+ gint *error);
+
+/** @} */
+
+/**
+ * @name Functions for performing transfers in an asynchronous manner
+ * With these functions you can do transfers in smaller steps. The steps
+ * are split up in a open, read/write, close manner.
+ * @{
+ */
+
+/** Start a PUT operation asynchronously
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param name Name of the object (null terminated UTF-8)
+ * @param type Type of the object (null terminated UTF-8), or NULL
+ * @param size Size of the object (GW_OBEX_UNKNOWN_LENGTH if not known)
+ * @param time Last modification time of the object (-1 if not known)
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns a new GwObexXfer object on success, NULL on failure
+ */
+GwObexXfer *gw_obex_put_async(GwObex *ctx, const char *name, const char *type,
+ gint size, time_t time, gint *error);
+
+
+/** Start a GET operation asynchronously
+ *
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param name Name of the object (null terminated UTF-8)
+ * @param type Type of the object (null terminated UTF-8), or NULL
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns a new GwObexXfer object on success, NULL on failure
+ */
+GwObexXfer *gw_obex_get_async(GwObex *ctx, const char *name, const char *type, gint *error);
+
+
+/** Set a callback function for a GwObexXfer object
+ * The callback function will be called in the following situations:
+ * <ul>
+ * <li>Data can be written (i.e. xfer_write will succeed)</li>
+ * <li>Data can be read (i.e. xfer_read will succees)</li>
+ * <li>An error ocured</li>
+ * <li>The transfer is finished</li>
+ * </ul>
+ *
+ * @param xfer Pointer returned by gw_obex_put_async or gw_obex_get_async
+ * @param cb Pointer to the callback function
+ * @param user_data Optional user data which will be passed to the callback function
+ *
+ * @returns a new GwObexXfer object on success, NULL on failure
+ */
+void gw_obex_xfer_set_callback(GwObexXfer *xfer, gw_obex_xfer_cb_t cb, gpointer user_data);
+
+
+/** Get the last modification time of the object being transfered
+ *
+ * @param xfer Pointer returned by gw_obex_put_async or gw_obex_get_async
+ *
+ * @returns The modification time or -1 if it is not known.
+ */
+time_t gw_obex_xfer_object_time(GwObexXfer *xfer);
+
+
+/** Get the size of the object being transfered
+ *
+ * @param xfer Pointer returned by gw_obex_put_async or gw_obex_get_async
+ *
+ * @returns The size or GW_OBEX_UNKNOWN_LENGTH if it is not known.
+ */
+gint gw_obex_xfer_object_size(GwObexXfer *xfer);
+
+
+/** Supply more data to a transfer
+ *
+ * @param xfer Pointer returned by gw_obex_put_async or gw_obex_get_async
+ * @param buf Buffer containing the data
+ * @param buf_size Size of the buffer
+ * @param bytes_written Return value for the number of bytes that were written
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_xfer_write(GwObexXfer *xfer, const char *buf, gint buf_size,
+ gint *bytes_written, gint *error);
+
+/** Read data from a transfer
+ *
+ * The function will report EOF by returning success with zero bytes read.
+ *
+ * @param xfer Pointer returned by gw_obex_put_async or gw_obex_get_async
+ * @param buf Buffer where the data should be stored
+ * @param buf_size Size of the buffer
+ * @param bytes_read Return value for the number of bytes that were read
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_xfer_read(GwObexXfer *xfer, char *buf, gint buf_size,
+ gint *bytes_read, gint *error);
+
+
+/** Force all data remaining in buffers to be sent
+ *
+ * @param xfer Pointer returned by gw_obex_put_async or gw_obex_get_async
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_xfer_flush(GwObexXfer *xfer, gint *error);
+
+
+/** Close an ongoing transfer
+ *
+ * You still need to call gw_obex_xfer_free after this to free the actual
+ * memory allocated for the GwObexXfer object.
+ *
+ * @param xfer Pointer returned by gw_obex_put_async or gw_obex_get_async
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_xfer_close(GwObexXfer *xfer, gint *error);
+
+
+/** Abort an ongoing transfer
+ *
+ * You still need to call gw_obex_xfer_free after this to free the actual
+ * memory allocated for the GwObexXfer object. xfer_close and xfer_abort are
+ * mutually exclusive (only call one of them for a transfer).
+ *
+ * @param xfer Pointer returned by gw_obex_put_async or gw_obex_get_async
+ * @param error Place to store error code on failure (NULL if not interested)
+ *
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_xfer_abort(GwObexXfer *xfer, gint *error);
+
+
+/** Free the data allocated for a GwObexXfer object
+ *
+ * @param xfer Pointer returned by gw_obex_put_async or gw_obex_get_async
+ */
+void gw_obex_xfer_free(struct gw_obex_xfer *xfer);
+
+
+/** Set blocking behaviour for a GwObexXfer object when calling xfer_read and xfer_write
+ *
+ * When blocking is enabled xfer_read will return only after it has been able
+ * to read some data (i.e. GW_OBEX_ERROR_NO_DATA will not be returned). For xfer_write
+ * blocking guarantees that *some* data will be written.
+ *
+ * @param xfer Pointer returned by gw_obex_put_async or gw_obex_get_async
+ * @param block TRUE to enable blocking behaviour
+ */
+void gw_obex_xfer_set_blocking(GwObexXfer *xfer, gboolean block);
+
+/** @} */
+
+#endif /* _GW_OBEX_H_ */
+
diff --git a/obexd/gwobex/log.h b/obexd/gwobex/log.h
new file mode 100644
index 0000000..41be01a
--- /dev/null
+++ b/obexd/gwobex/log.h
+/**
+ @file log.h
+
+ @author Johan Hedberg <johan.hedberg@nokia.com>
+
+ Copyright (C) 2004 Nokia Corporation. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License, version 2.1, as published by the Free Software Foundation.
+
+ 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.
+
+*/
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#ifdef DEBUG
+# ifdef DEBUG_STDOUT
+# include <glib.h>
+# define debug(...) g_print(__VA_ARGS__)
+# else
+# include <syslog.h>
+# define debug(fmt, arg...) syslog(LOG_DEBUG, "gwobex: " fmt, ## arg)
+# endif
+#else
+# define debug(...) ((void)(0))
+#endif
+
+#endif /* _LOG_H_ */
diff --git a/obexd/gwobex/obex-priv.c b/obexd/gwobex/obex-priv.c
new file mode 100644
index 0000000..792cb4f
--- /dev/null
+++ b/obexd/gwobex/obex-priv.c
+/**
+ @file obex-priv.c
+
+ Private functions for the GW OBEX Library
+
+ @author Johan Hedberg <johan.hedberg@nokia.com>
+
+ Copyright (C) 2004-2006 Nokia Corporation. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License, version 2.1, as published by the Free Software Foundation.
+
+ 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 <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <utime.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <glib.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <openobex/obex.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "log.h"
+#include "gw-obex.h"
+#include "utils.h"
+#include "obex-xfer.h"
+#include "obex-priv.h"
+
+#define MAX_TIMEOUTS 20
+#define MAX_TIMEOUTS_FIRST 60
+
+static gboolean file_is_dir(const char *filename) {
+ struct stat st;
+
+ if (stat(filename, &st) < 0)
+ return FALSE;
+
+ if (S_ISDIR(st.st_mode))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void idle_callback(obex_t *handle, obex_object_t *object, int mode,
+ int event, int obex_cmd, int obex_rsp) {
+ debug("idle_callback() called\n");
+ sleep(1);
+}
+
+static gboolean gw_obex_request_async(GwObex *ctx, obex_object_t *object) {
+ ctx->done = FALSE;
+
+ /* If this is a put, the first request can only be sent after we
+ * have some data in the outgoing buffer */
+ if (ctx->obex_op == OBEX_CMD_PUT) {
+ ctx->xfer->object = object;
+ return TRUE;
+ }
+
+ if (OBEX_Request(ctx->handle, object) < 0) {
+ debug("OBEX_Request() failed\n");
+ ctx->error = GW_OBEX_ERROR_INTERNAL;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean gw_obex_request_sync(GwObex *ctx, obex_object_t *object) {
+ int timeouts = 0, first = 1;
+
+ /* Set sensible start values */
+ ctx->done = FALSE;
+
+ if (OBEX_Request(ctx->handle, object) < 0) {
+ debug("OBEX_Request() failed\n");
+ ctx->error = GW_OBEX_ERROR_INTERNAL;
+ return FALSE;
+ }
+
+ while (TRUE) {
+ int ret, max_timeouts;
+
+ ret = OBEX_HandleInput(ctx->handle, 1);
+
+ if (ctx->done)
+ break;
+
+ if (ctx->cancel_cb && ctx->cancel_cb(ctx->cancel_data)) {
+ debug("cancel_cb() returned TRUE, aborting.\n");
+ if (ctx->xfer->abort)
+ continue;
+ if (!gw_obex_xfer_do_abort(ctx->xfer))
+ break;
+ /* Must continue to receive the abort reply */
+ continue;
+ }
+
+ if (ret < 0) {
+ debug("OBEX_HandleInput() failed\n");
+ ctx->error = GW_OBEX_ERROR_INTERNAL;
+ return FALSE;
+ }
+
+ /* Timeout */
+ if (ret == 0)
+ timeouts++;
+ else {
+ timeouts = 0;
+ if (first)
+ first = 0;
+ }
+
+ max_timeouts = first ? MAX_TIMEOUTS_FIRST : MAX_TIMEOUTS;
+
+ if (timeouts > max_timeouts) {
+ debug("OBEX_HandleInput(): timeout\n");
+ ctx->error = GW_OBEX_ERROR_TIMEOUT;
+ obex_link_error(ctx);
+ return FALSE;
+ }
+
+ debug("gw_obex_request_sync(): looping\n");
+ }
+
+ gw_obex_set_error(ctx);
+
+ if (ctx->error == OBEX_RSP_SUCCESS) {
+ /* It is possible that a EV_PROGRESS doesn't arrive after all data has
+ * been transfered. Call pr_cb here to ensure app gets 100% progress */
+ if (ctx->report_progress && ctx->pr_cb)
+ ctx->pr_cb(ctx, ctx->obex_op, ctx->xfer->counter, ctx->xfer->counter, ctx->pr_data);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+#ifdef DEBUG
+static const char *optostr(uint8_t op) {
+ switch (op) {
+ case OBEX_CMD_CONNECT:
+ return "Connect";
+ case OBEX_CMD_DISCONNECT:
+ return "Disconnect";
+ case OBEX_CMD_PUT:
+ return "Put";
+ case OBEX_CMD_GET:
+ return "Get";
+ case OBEX_CMD_SETPATH:
+ return "SetPath";
+ case OBEX_CMD_ABORT:
+ return "Abort";
+ case OBEX_CMD_ACTION:
+ return "Action";
+ default:
+ return "(unknown)";
+ }
+}
+#endif
+
+static void obex_connect_done(GwObex *ctx, obex_object_t *object, int obex_rsp) {
+ obex_headerdata_t hv;
+ uint8_t hi;
+ unsigned int hlen;
+ uint8_t *ptr;
+
+ if (OBEX_ObjectGetNonHdrData(object, &ptr)
+ != sizeof(obex_connect_hdr_t)) {
+ debug("Invalid packet content.\n");
+ }
+ else {
+ obex_connect_hdr_t *nonhdrdata = (obex_connect_hdr_t *)ptr;
+ uint16_t mtu = g_ntohs(nonhdrdata->mtu);
+ int new_size;
+ debug("Version: 0x%02x. Flags: 0x%02x OBEX packet length: %d\n",
+ nonhdrdata->version, nonhdrdata->flags, mtu);
+ /* Leave space for headers */
+ new_size = mtu - 200;
+ if (new_size < ctx->tx_max) {
+ debug("Resizing stream chunks to %d\n", new_size);
+ ctx->tx_max = new_size;
+ }
+ }
+
+ while (OBEX_ObjectGetNextHeader(ctx->handle, object, &hi, &hv, &hlen)) {
+ switch (hi) {
+#ifdef DEBUG
+ case OBEX_HDR_WHO:
+ {
+ char *str;
+ str = bytestr(hv.bs, hlen);
+ debug("WHO header (UUID): %s\n", str);
+ g_free(str);
+ }
+ break;
+#endif
+ case OBEX_HDR_CONNECTION:
+ ctx->conid = hv.bq4;
+ debug("got Conection ID: %#x\n", hv.bq4);
+ break;
+ default:
+ debug("Skipped header %02x\n", hi);
+ break;
+ }
+ }
+}
+
+#ifdef DEBUG
+static void show_headers(obex_t *handle, obex_object_t *object) {
+ obex_headerdata_t hv;
+ uint8_t hi;
+ unsigned int hlen;
+
+ while (OBEX_ObjectGetNextHeader(handle, object, &hi, &hv, &hlen)) {
+ char *str;
+
+ switch (hi) {
+ case OBEX_HDR_WHO:
+ debug("OBEX_HDR_WHO\n");
+ break;
+ case OBEX_HDR_CONNECTION:
+ debug("OBEX_HDR_CONNECTION: %#x\n", hv.bq4);
+ break;
+ case OBEX_HDR_LENGTH:
+ debug("OBEX_HDR_LENGTH: %d\n", hv.bq4);
+ break;
+ case OBEX_HDR_NAME:
+ str = g_utf16_to_utf8((gunichar2 *)hv.bs, hlen, NULL, NULL, NULL);
+ if (str) {
+ debug("OBEX_HDR_NAME: %s\n", str);
+ g_free(str);
+ }
+ break;
+ case OBEX_HDR_AUTHCHAL:
+ str = bytestr(hv.bs, hlen);
+ debug("OBEX_HDR_AUTHCHAL: %s\n", str);
+ g_free(str);
+ break;
+ case OBEX_HDR_TIME:
+ str = g_new0(char, hlen + 1);
+ memcpy(str, hv.bs, hlen);
+ debug("OBEX_HDR_TIME: %s\n", str);
+ g_free(str);
+ break;
+ case OBEX_HDR_TYPE:
+ debug("OBEX_HDR_TYPE: %s\n", hv.bs);
+ break;
+ default:
+ debug("Skipped header 0x%02x\n", hi);
+ break;
+ }
+ }
+
+ OBEX_ObjectReParseHeaders(handle, object);
+}
+#else
+static inline void show_headers(obex_t *handle, obex_object_t *object) {}
+#endif
+
+static void obex_abort_done(GwObex *ctx, obex_object_t *object,
+ int obex_cmd, int obex_rsp) {
+ ctx->done = TRUE;
+ if (ctx->xfer)
+ ctx->xfer->do_cb = TRUE;
+
+ if (obex_rsp != OBEX_RSP_SUCCESS)
+ debug("ABORT of %s command (0x%02x) failed: %s (0x%02x)\n",
+ optostr((uint8_t)obex_cmd), (uint8_t)obex_cmd,
+ OBEX_ResponseToString(obex_rsp), (uint8_t)obex_rsp);
+ else
+ debug("ABORT of %s command (0x%02x) succeeded.\n",
+ optostr((uint8_t)obex_cmd), (uint8_t)obex_cmd);
+}
+
+static void obex_request_done(GwObex *ctx, obex_object_t *object,
+ int obex_cmd, int obex_rsp) {
+ ctx->done = TRUE;
+ if (ctx->xfer)
+ ctx->xfer->do_cb = TRUE;
+
+ ctx->obex_rsp = obex_rsp;
+
+ if (obex_rsp != OBEX_RSP_SUCCESS) {
+ debug("%s command (0x%02x) failed: %s (0x%02x)\n",
+ optostr((uint8_t)obex_cmd), (uint8_t)obex_cmd,
+ OBEX_ResponseToString(obex_rsp), (uint8_t)obex_rsp);
+#ifdef DEBUG
+ if (obex_rsp == OBEX_RSP_UNAUTHORIZED) {
+ debug("Showing headers..\n");
+ show_headers(ctx->handle, object);
+ }
+#endif
+ return;
+ }
+
+ debug("%s command (0x%02x) succeeded.\n", optostr((uint8_t)obex_cmd),
+ (uint8_t)obex_cmd);
+
+ switch (obex_cmd) {
+ case OBEX_CMD_CONNECT:
+ obex_connect_done(ctx, object, obex_rsp);
+ break;
+ default:
+ break;
+ }
+}
+
+static void get_target_size_and_time(obex_t *handle, obex_object_t *object,
+ gint *size, time_t *time) {
+ obex_headerdata_t hv;
+ uint8_t hi;
+ unsigned int hlen;
+
+ *size = GW_OBEX_UNKNOWN_LENGTH;
+ *time = -1;
+
+ while (OBEX_ObjectGetNextHeader(handle, object, &hi, &hv, &hlen)) {
+ switch (hi) {
+ case OBEX_HDR_LENGTH:
+ *size = hv.bq4; //(gint) g_ntohl(hv.bq4);
+ break;
+ case OBEX_HDR_TIME:
+ *time = parse_iso8601((char *)hv.bs, hlen);
+ break;
+ default:
+ break;
+ }
+ }
+
+ OBEX_ObjectReParseHeaders(handle, object);
+}
+
+static void obex_readstream(GwObex *ctx, obex_object_t *object) {
+ struct gw_obex_xfer *xfer = ctx->xfer;
+ const uint8_t *buf;
+ int actual;
+
+ if (!xfer) {
+ debug("Incomming data even though no xfer active!\n");
+ /* Flush incomming stream */
+ actual = OBEX_ObjectReadStream(ctx->handle, object, &buf);
+ if (actual > 0)
+ debug("Ignored %d bytes\n", actual);
+ return;
+ }
+
+ if (ctx->xfer->counter == 0) {
+ get_target_size_and_time(ctx->handle, object,
+ &xfer->target_size, &xfer->modtime);
+ show_headers(ctx->handle, object);
+ }
+
+ actual = OBEX_ObjectReadStream(ctx->handle, object, &buf);
+ if (actual > 0) {
+ xfer->counter += actual;
+
+ debug("obex_readstream: got %d bytes (%zd in total)\n", actual, xfer->counter);
+
+ if (xfer->async) {
+ gint free_space = xfer->buf_size - (xfer->data_start + xfer->data_length);
+ if (actual > free_space) {
+ /* This should never happen */
+ debug("Out of buffer space: actual=%d, free=%d\n", actual, free_space);
+ return;
+ }
+
+ memcpy(&xfer->buf[xfer->data_start], buf, actual);
+ xfer->data_length += actual;
+
+ debug("OBEX_SuspendRequest at %s:%d (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ OBEX_SuspendRequest(ctx->handle, object);
+
+ xfer->do_cb = TRUE;
+ }
+ else if (xfer->stream_fd >= 0) {
+ int written = 0;
+
+ while (written < actual) {
+ int ret;
+
+ ret = write(xfer->stream_fd, buf + written, actual - written);
+ if (ret < 0 && errno == EINTR)
+ continue;
+
+ if (ret < 0) {
+ debug("Could not write: %s (%d)", g_strerror(errno), errno);
+ break;
+ }
+
+ written += ret;
+ }
+ }
+ else {
+ xfer->buf = g_realloc(xfer->buf, xfer->counter);
+ memcpy(&xfer->buf[xfer->buf_size], buf, actual);
+ xfer->buf_size = xfer->counter;
+ }
+ }
+ else
+ debug("Error or no data on OBEX stream\n");
+}
+
+static void obex_writestream(GwObex *ctx, obex_object_t *object) {
+ struct gw_obex_xfer *xfer = ctx->xfer;
+ obex_headerdata_t hv;
+ int actual = -1;
+
+ if (!xfer) {
+ debug("Request to provide data even though no active xfer!");
+ hv.bs = NULL;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_BODY,
+ hv, 0, OBEX_FL_STREAM_DATAEND);
+ return;
+ }
+
+ if (xfer->async) {
+ if (xfer->data_length > 0) {
+ gint send_size = xfer->data_length > ctx->tx_max ? ctx->tx_max : xfer->data_length;
+
+ hv.bs = &xfer->buf[xfer->data_start];
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_BODY,
+ hv, send_size, OBEX_FL_STREAM_DATA);
+ actual = send_size;
+ xfer->data_length -= send_size;
+ if (xfer->data_length == 0)
+ xfer->data_start = 0;
+ else
+ xfer->data_start += send_size;
+
+ xfer->do_cb = TRUE;
+ if (!xfer->close) {
+ debug("OBEX_SuspendRequest at %s:%d (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ OBEX_SuspendRequest(ctx->handle, object);
+ }
+ }
+ else {
+ hv.bs = NULL;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_BODY,
+ hv, 0, OBEX_FL_STREAM_DATAEND);
+ }
+ }
+ else if (xfer->stream_fd >= 0) {
+ actual = read(xfer->stream_fd, xfer->buf, ctx->tx_max);
+ hv.bs = xfer->buf;
+#ifdef TEST_ABORT
+ if (xfer->counter > 4000)
+ actual = -1;
+#endif
+ if (actual > 0)
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_BODY,
+ hv, actual, OBEX_FL_STREAM_DATA);
+ else if (actual == 0) /* EOF */
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_BODY,
+ hv, 0, OBEX_FL_STREAM_DATAEND);
+ else { /* error reading file */
+ debug("read(): %s\n", strerror(errno));
+ gw_obex_xfer_do_abort(xfer);
+ }
+ }
+ else {
+ if (xfer->counter < xfer->buf_size) {
+ if (xfer->buf_size > xfer->counter + ctx->tx_max)
+ actual = ctx->tx_max;
+ else
+ actual = xfer->buf_size - xfer->counter;
+ hv.bs = &xfer->buf[xfer->counter];
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_BODY,
+ hv, actual, OBEX_FL_STREAM_DATA);
+ }
+ else {
+ hv.bs = NULL;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_BODY,
+ hv, 0, OBEX_FL_STREAM_DATAEND);
+ }
+ }
+
+ if (actual > 0)
+ xfer->counter += actual;
+}
+
+static void obex_event_handler(obex_t *handle, obex_object_t *object, int mode,
+ int event, int obex_cmd, int obex_rsp) {
+ GwObex *ctx = OBEX_GetCustomData(handle);
+ switch (event) {
+ case OBEX_EV_ABORT:
+ debug("OBEX_EV_ABORT\n");
+ obex_abort_done(ctx, object, obex_cmd, obex_rsp);
+ break;
+ case OBEX_EV_PROGRESS:
+ debug("OBEX_EV_PROGRESS\n");
+ if (ctx->report_progress && ctx->pr_cb)
+ ctx->pr_cb(ctx, ctx->obex_op, ctx->xfer->counter, ctx->xfer->target_size, ctx->pr_data);
+ break;
+ case OBEX_EV_REQDONE:
+ debug("OBEX_EV_REQDONE\n");
+ obex_request_done(ctx, object, obex_cmd, obex_rsp);
+ break;
+ case OBEX_EV_REQ:
+ debug("OBEX_EV_REQ: %s (0x%02x)\n",
+ optostr((uint8_t)obex_cmd), (uint8_t)obex_cmd);
+ OBEX_ObjectSetRsp(object, OBEX_RSP_NOT_IMPLEMENTED,
+ OBEX_RSP_NOT_IMPLEMENTED);
+ break;
+ case OBEX_EV_REQHINT:
+ debug("OBEX_EV_REQHINT: %s (0x%02x)\n",
+ optostr((uint8_t)obex_cmd), (uint8_t)obex_cmd);
+ OBEX_ObjectSetRsp(object, OBEX_RSP_NOT_IMPLEMENTED,
+ OBEX_RSP_NOT_IMPLEMENTED);
+ break;
+ case OBEX_EV_LINKERR:
+ debug("OBEX_EV_LINKERR\n");
+ obex_link_error(ctx);
+ break;
+ case OBEX_EV_STREAMEMPTY:
+ debug("OBEX_EV_STREAMEMPTY\n");
+ obex_writestream(ctx, object);
+ break;
+ case OBEX_EV_STREAMAVAIL:
+ debug("OBEX_EV_STREAMAVAIL\n");
+ obex_readstream(ctx, object);
+ break;
+ case OBEX_EV_PARSEERR:
+ debug("OBEX_EV_PARSEERR\n");
+ break;
+ default:
+ debug("Unknown event %d\n", event);
+ break;
+ }
+}
+
+gboolean gw_obex_set_error(GwObex *ctx) {
+ ctx->error = 0;
+
+ if (!ctx->done)
+ return FALSE;
+
+ if (ctx->xfer && ctx->xfer->abort)
+ ctx->error = GW_OBEX_ERROR_ABORT;
+ else if (ctx->conn_fd < 0 || ctx->link_err)
+ ctx->error = GW_OBEX_ERROR_DISCONNECT;
+ else
+ ctx->error = (gint)ctx->obex_rsp;
+
+ if (ctx->error == OBEX_RSP_SUCCESS)
+ return FALSE;
+
+ return TRUE;
+}
+
+void obex_link_error(GwObex *ctx) {
+ if (ctx->link_err)
+ return;
+ ctx->link_err = TRUE;
+ OBEX_SetUserCallBack(ctx->handle, idle_callback, NULL);
+ ctx->done = TRUE;
+ ctx->conid = CONID_INVALID;
+ if (ctx->conn_fd >= 0) {
+ OBEX_TransportDisconnect(ctx->handle);
+ close(ctx->conn_fd);
+ ctx->conn_fd = -1;
+ }
+ if (ctx->gio) {
+ g_io_channel_unref(ctx->gio);
+ ctx->gio = NULL;
+ }
+ if (ctx->gio_source) {
+ g_source_destroy(ctx->gio_source);
+ ctx->gio_source = NULL;
+ }
+ if (ctx->xfer) {
+ /* Check that buffer is owned by us */
+ if (!(ctx->obex_op == OBEX_CMD_PUT && ctx->xfer->stream_fd < 0))
+ g_free(ctx->xfer->buf);
+ ctx->xfer->buf = NULL;
+ ctx->xfer->buf_size = 0;
+ ctx->xfer->do_cb = TRUE;
+ }
+}
+
+gboolean gw_obex_transport_setup(int fd, obex_t **handle) {
+ *handle = OBEX_Init(OBEX_TRANS_FD, obex_event_handler, 0);
+ if (*handle == NULL) {
+ debug("OBEX_Init() failed\n");
+ return FALSE;
+ }
+
+ (void) OBEX_SetTransportMTU(*handle, GW_OBEX_RX_MTU, GW_OBEX_TX_MTU);
+
+ if (FdOBEX_TransportSetup(*handle, fd, fd, 0) < 0) {
+ debug("FdOBEX_TransportSetup() failed\n");
+ OBEX_Cleanup(*handle);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void gw_obex_get_error(GwObex *ctx, gint *error) {
+ if (error)
+ *error = ctx->error;
+ ctx->error = OBEX_RSP_SUCCESS;
+}
+
+gboolean gw_obex_cb(GIOChannel *chan, GIOCondition cond, gpointer data) {
+ GwObex *ctx = (GwObex *)data;
+
+ debug("gw_obex_cb(): entered\n");
+
+ GW_OBEX_LOCK(ctx);
+
+ if (ctx->conn_fd < 0 || (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))) {
+ debug("gw_obex_cb: error or connection closed\n");
+ obex_link_error(ctx);
+ GW_OBEX_UNLOCK(ctx);
+ if (ctx->xfer && ctx->xfer->cb)
+ ctx->xfer->cb(ctx->xfer, ctx->xfer->cb_data);
+ if (ctx->dc_cb)
+ ctx->dc_cb(ctx, ctx->dc_data);
+ return FALSE;
+ }
+
+ debug("Calling OBEX_HandleInput\n");
+ OBEX_HandleInput(ctx->handle, 0);
+ debug("Returned from OBEX_HandleInput\n");
+
+ if (ctx->xfer && ctx->xfer->cb && ctx->xfer->do_cb) {
+ ctx->xfer->do_cb = FALSE;
+ GW_OBEX_UNLOCK(ctx);
+ ctx->xfer->cb(ctx->xfer, ctx->xfer->cb_data);
+ GW_OBEX_LOCK(ctx);
+ }
+
+ GW_OBEX_UNLOCK(ctx);
+
+ return TRUE;
+}
+
+gboolean gw_obex_disconnect(GwObex *ctx) {
+ obex_object_t *object;
+
+ g_assert(!ctx->xfer);
+
+ if (!ctx->done) {
+ ctx->error = GW_OBEX_ERROR_BUSY;
+ return FALSE;
+ }
+
+ object = OBEX_ObjectNew(ctx->handle, OBEX_CMD_DISCONNECT);
+
+ if (ctx->conid != CONID_INVALID) {
+ obex_headerdata_t hv;
+ hv.bq4 = ctx->conid;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_CONNECTION, hv, 4, 0);
+ }
+
+ return gw_obex_request_sync(ctx, object);
+}
+
+gboolean gw_obex_connect(GwObex *ctx, const char *target, size_t target_len) {
+ gboolean ret;
+ obex_object_t *object;
+
+ g_assert(ctx->done && !ctx->xfer);
+
+ ctx->obex_op = OBEX_CMD_CONNECT;
+
+ object = OBEX_ObjectNew(ctx->handle, OBEX_CMD_CONNECT);
+ if (target) {
+ obex_headerdata_t hv;
+ hv.bs = (const unsigned char *)target;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_TARGET, hv, target_len, OBEX_FL_FIT_ONE_PACKET);
+ }
+
+ ret = gw_obex_request_sync(ctx, object);
+ ctx->obex_op = OBEX_CMD_NONE;
+ return ret;
+}
+
+GwObex *make_context(obex_t *handle) {
+ GwObex *context;
+
+ context = g_new0(GwObex, 1);
+
+ context->handle = handle;
+ context->conn_fd = OBEX_GetFD(handle);
+ context->conid = CONID_INVALID;
+ context->tx_max = GW_OBEX_TX_MTU - 200;
+ context->rx_max = GW_OBEX_RX_MTU;
+ context->obex_op = OBEX_CMD_NONE;
+ context->obex_rsp = OBEX_RSP_SUCCESS;
+ context->done = TRUE;
+
+ return context;
+}
+
+gboolean gw_obex_action_op(GwObex *ctx, const gchar *src, const gchar *dst,
+ uint8_t action) {
+ gboolean ret = FALSE;
+ obex_object_t *object;
+ obex_headerdata_t hv;
+ gunichar2 *uname;
+ glong uname_len;
+
+ g_assert(src && dst);
+
+ if (!ctx->done || ctx->xfer) {
+ ctx->error = GW_OBEX_ERROR_BUSY;
+ return FALSE;
+ }
+
+ ctx->obex_op = OBEX_CMD_ACTION;
+
+ object = OBEX_ObjectNew(ctx->handle, OBEX_CMD_ACTION);
+
+ if (ctx->conid != CONID_INVALID) {
+ hv.bq4 = ctx->conid;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_CONNECTION, hv, 4, 0);
+ }
+
+ hv.bq1 = action;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_ACTION_ID, hv, 1, 0);
+
+ uname_len = get_uname(&uname, src);
+ if (uname_len < 0) {
+ OBEX_ObjectDelete(ctx->handle, object);
+ goto out;
+ }
+ hv.bs = (unsigned char *)uname;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_NAME, hv, uname_len, 0);
+ g_free(uname);
+
+ uname_len = get_uname(&uname, dst);
+ if (uname_len < 0) {
+ OBEX_ObjectDelete(ctx->handle, object);
+ goto out;
+ }
+ hv.bs = (unsigned char *)uname;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_DESTNAME, hv, uname_len, 0);
+ g_free(uname);
+
+ ret = gw_obex_request_sync(ctx, object);
+
+out:
+ ctx->obex_op = OBEX_CMD_NONE;
+ return ret;
+}
+
+gboolean gw_obex_setpath(GwObex *ctx, const gchar *path, int flags) {
+ gboolean ret = FALSE;
+ obex_headerdata_t hv;
+ obex_object_t *object;
+ obex_setpath_hdr_t nonhdrdata;
+ gunichar2 *uname;
+ glong uname_len;
+
+ if (!ctx->done || ctx->xfer) {
+ ctx->error = GW_OBEX_ERROR_BUSY;
+ return FALSE;
+ }
+
+ ctx->obex_op = OBEX_CMD_SETPATH;
+
+ nonhdrdata.flags = 0x02;
+ nonhdrdata.constants = 0;
+
+ if (strcmp(path, "..") == 0) {
+ /* move up one directory */
+ nonhdrdata.flags = 0x03;
+ uname_len = -1;
+ }
+ else {
+ /* normal directory change */
+ uname_len = get_uname(&uname, path);
+ if (uname_len < 0) {
+ ctx->error = GW_OBEX_ERROR_INVALID_PARAMS;
+ goto out;
+ }
+ }
+
+ if (flags & SETPATH_CREATE)
+ nonhdrdata.flags &= ~0x02;
+
+ object = OBEX_ObjectNew(ctx->handle, OBEX_CMD_SETPATH);
+ OBEX_ObjectSetNonHdrData(object, (uint8_t*)&nonhdrdata, 2);
+
+ if (ctx->conid != CONID_INVALID) {
+ hv.bq4 = ctx->conid;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_CONNECTION, hv, 4, 0);
+ }
+
+ if (uname_len >= 0) {
+ hv.bs = (unsigned char *) (uname ? (char *)uname : "");
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_NAME, hv, uname_len, 0);
+ g_free(uname);
+ }
+
+ ret = gw_obex_request_sync(ctx, object);
+
+out:
+ ctx->obex_op = OBEX_CMD_NONE;
+ return ret;
+}
+
+gboolean gw_obex_get(GwObex *ctx,
+ const gchar *local, const gchar *remote, const gchar *type,
+ gchar **buf, gint *buf_size, int stream_fd,
+ gboolean async) {
+ gboolean ret = FALSE;
+ obex_headerdata_t hv;
+ obex_object_t *object;
+
+ g_assert(local || buf || stream_fd > 0 || async);
+ g_assert(remote || type);
+
+ if (!ctx->done || ctx->xfer) {
+ ctx->error = GW_OBEX_ERROR_BUSY;
+ return ret;
+ }
+
+ ctx->obex_op = OBEX_CMD_GET;
+
+ ctx->xfer = gw_obex_xfer_new(ctx, async, stream_fd);
+
+ object = OBEX_ObjectNew(ctx->handle, OBEX_CMD_GET);
+
+ if (ctx->conid != CONID_INVALID) {
+ hv.bq4 = ctx->conid;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_CONNECTION, hv, 4, 0);
+ }
+
+ if (type) {
+ hv.bs = (unsigned char *)type;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_TYPE, hv, strlen(type) + 1, 0);
+ }
+
+ if (remote) {
+ gunichar2 *uname;
+ glong uname_len;
+
+ uname_len = get_uname(&uname, remote);
+ if (uname_len < 0) {
+ OBEX_ObjectDelete(ctx->handle, object);
+ ctx->error = GW_OBEX_ERROR_INVALID_PARAMS;
+ goto out;
+ }
+
+ /* OpenOBEX is buggy and won't append the header unless hv.bs != NULL */
+ hv.bs = (unsigned char *) (uname ? (char *)uname : "");
+
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_NAME, hv, uname_len, 0);
+ g_free(uname);
+ }
+
+ if (local) {
+ ctx->xfer->stream_fd = open(local, O_WRONLY | O_CREAT, 0600);
+ if (ctx->xfer->stream_fd < 0) {
+ if (errno == ENOENT || errno == ENODEV)
+ ctx->error = GW_OBEX_ERROR_INVALID_PARAMS;
+ else
+ ctx->error = GW_OBEX_ERROR_LOCAL_ACCESS;
+ debug("open(%s): %s", local, strerror(errno));
+ OBEX_ObjectDelete(ctx->handle, object);
+ goto out;
+ }
+ }
+
+ OBEX_ObjectReadStream(ctx->handle, object, NULL);
+
+ if (async) {
+ ret = gw_obex_request_async(ctx, object);
+ if (ret)
+ return ret;
+ }
+ else {
+ ctx->report_progress = TRUE;
+ ret = gw_obex_request_sync(ctx, object);
+ }
+
+ if (ctx->xfer->stream_fd >= 0 && stream_fd < 0)
+ close(ctx->xfer->stream_fd);
+
+ if (ret == FALSE) {
+ if (local)
+ unlink(local);
+ }
+ else {
+ if (local) {
+ debug("%s stored in %s\n", remote ? remote : type, local);
+ if (ctx->xfer->modtime != -1) {
+ struct utimbuf ubuf;
+ ubuf.actime = time(NULL);
+ ubuf.modtime = ctx->xfer->modtime;
+ if (utime(local, &ubuf) < 0)
+ debug("utime(%s): %s\n", local, g_strerror(errno));
+ }
+ }
+ if (buf) {
+ *buf = (gchar *)ctx->xfer->buf;
+ *buf_size = ctx->xfer->buf_size;
+ /* Make sure gw_obex_xfer_free doesn't free the buffer */
+ ctx->xfer->buf = NULL;
+ }
+ }
+
+out:
+ _gw_obex_xfer_free(ctx->xfer);
+ ctx->xfer = NULL;
+
+ ctx->report_progress = FALSE;
+ ctx->obex_op = OBEX_CMD_NONE;
+
+ return ret;
+}
+
+gboolean gw_obex_put(GwObex *ctx,
+ const gchar *local, const gchar *remote, const gchar *type,
+ const gchar *buf, gint object_size, time_t object_time,
+ int stream_fd, gboolean async) {
+ gboolean ret = FALSE;
+ obex_headerdata_t hv;
+ obex_object_t *object;
+ gunichar2 *uname = NULL;
+ glong uname_len = 0;
+
+ g_assert(remote || type);
+
+ if (!ctx->done || ctx->xfer) {
+ ctx->error = GW_OBEX_ERROR_BUSY;
+ return FALSE;
+ }
+
+ ctx->obex_op = OBEX_CMD_PUT;
+
+ if (remote) {
+ uname_len = get_uname(&uname, remote);
+ if (uname_len < 0) {
+ ctx->error = GW_OBEX_ERROR_INVALID_PARAMS;
+ goto out;
+ }
+ }
+
+ ctx->xfer = gw_obex_xfer_new(ctx, async, stream_fd);
+
+ if (local) {
+ if (file_is_dir(local)) {
+ debug("Trying to PUT a directory\n");
+ ctx->error = GW_OBEX_ERROR_INVALID_PARAMS;
+ goto out;
+ }
+
+ ctx->xfer->stream_fd = open(local, O_RDONLY);
+ if (ctx->xfer->stream_fd < 0) {
+ if (errno == ENOENT || errno == ENODEV)
+ ctx->error = GW_OBEX_ERROR_INVALID_PARAMS;
+ else
+ ctx->error = GW_OBEX_ERROR_LOCAL_ACCESS;
+ debug("open(%s): %s", local, strerror(errno));
+ goto out;
+ }
+ ctx->xfer->buf = g_malloc(ctx->tx_max);
+ ctx->xfer->buf_size = ctx->tx_max;
+ debug("Sending %s to %s\n", local, remote ? remote : type);
+ }
+ else if (buf) {
+ ctx->xfer->buf = (unsigned char *)buf;
+ ctx->xfer->buf_size = object_size;
+ debug("Sending to %s\n", remote ? remote : type);
+ }
+ else if (stream_fd < 0 && !async) { /* Delete */
+ ctx->report_progress = FALSE;
+ debug("Deleting %s\n", remote ? remote : type);
+ }
+
+ object = OBEX_ObjectNew(ctx->handle, OBEX_CMD_PUT);
+
+ if (ctx->conid != CONID_INVALID) {
+ hv.bq4 = ctx->conid;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_CONNECTION, hv, 4, 0);
+ }
+
+ if (uname) {
+ hv.bs = (unsigned char *)uname;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_NAME, hv, uname_len, 0);
+ g_free(uname);
+ uname = NULL;
+ }
+
+ if (type) {
+ hv.bs = (unsigned char *)type;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_TYPE, hv, strlen(type) + 1, 0);
+ }
+
+ /* Try to figure out modification time if none was given */
+ if (ctx->xfer->stream_fd >= 0) {
+ struct stat stats;
+ if (fstat(ctx->xfer->stream_fd, &stats) == 0) {
+ object_size = stats.st_size;
+ if (object_time < 0)
+ object_time = stats.st_mtime;
+ }
+ }
+
+ /* Add a time header if possible */
+ if (object_time >= 0) {
+ char tstr[17];
+ int len;
+
+ len = make_iso8601(object_time, tstr, sizeof(tstr));
+
+ if (len >= 0) {
+ debug("Adding time header: %s\n", tstr);
+ hv.bs = (unsigned char *)tstr;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_TIME, hv, len, 0);
+ }
+ }
+
+ /* Add a length header if possible */
+ if (object_size > 0) {
+ ctx->xfer->target_size = object_size;
+ debug("Adding size header: %d\n", object_size);
+ hv.bq4 = (uint32_t)object_size;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_LENGTH, hv, 4, 0);
+ }
+ else
+ ctx->xfer->target_size = GW_OBEX_UNKNOWN_LENGTH;
+
+ if (ctx->xfer->stream_fd >= 0 || buf || async) {
+ hv.bs = NULL;
+ OBEX_ObjectAddHeader(ctx->handle, object, OBEX_HDR_BODY, hv, 0, OBEX_FL_STREAM_START);
+ }
+
+ if (async) {
+ ret = gw_obex_request_async(ctx, object);
+ if (ret)
+ return ret;
+ }
+ else {
+ ctx->report_progress = TRUE;
+ ret = gw_obex_request_sync(ctx, object);
+ }
+
+out:
+ g_free(uname);
+
+ if (ctx->xfer->stream_fd >= 0 && stream_fd < 0)
+ close(ctx->xfer->stream_fd);
+
+ if (buf)
+ ctx->xfer->buf = NULL;
+
+ _gw_obex_xfer_free(ctx->xfer);
+ ctx->xfer = NULL;
+
+ ctx->report_progress = FALSE;
+ ctx->obex_op = OBEX_CMD_NONE;
+
+ return ret;
+}
+
diff --git a/obexd/gwobex/obex-priv.h b/obexd/gwobex/obex-priv.h
new file mode 100644
index 0000000..051c67a
--- /dev/null
+++ b/obexd/gwobex/obex-priv.h
+/**
+ @file obex-priv.h
+
+ @author Johan Hedberg <johan.hedberg@nokia.com>
+
+ Copyright (C) 2004-2006 Nokia Corporation. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License, version 2.1, as published by the Free Software Foundation.
+
+ 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.
+
+*/
+#ifndef _OBEX_PRIV_H_
+#define _OBEX_PRIV_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <time.h>
+#include <glib.h>
+
+#include <openobex/obex.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gw-obex.h"
+#include "obex-xfer.h"
+
+#define CHECK_DISCONNECT(ret,err,ctx) do { \
+ if ((ctx)->conn_fd < 0) { \
+ if (err) \
+ *(err) = GW_OBEX_ERROR_DISCONNECT; \
+ GW_OBEX_UNLOCK(ctx); \
+ return (ret); \
+ } \
+ } while (0)
+
+#ifndef OBEX_CMD_ACTION
+# define OBEX_CMD_ACTION 0x06
+
+# define OBEX_HDR_ACTION_ID 0x94
+# define OBEX_HDR_DESTNAME 0x15
+# define OBEX_HDR_PERMISSIONS 0xD6
+
+# define OBEX_ACTION_COPY 0x00
+# define OBEX_ACTION_MOVE 0x01
+# define OBEX_ACTION_SETPERM 0x02
+
+#endif /* OBEX_CMD_ACTION */
+
+#define CONID_INVALID 0xFFFFFFFF
+
+#define OBEX_CMD_NONE 0x10
+
+#define GW_OBEX_RX_MTU 4096
+#define GW_OBEX_TX_MTU 32767
+
+#define SETPATH_CREATE 0x0001
+
+#define CAP_TYPE "x-obex/capability"
+#define OBP_TYPE "x-obex/object-profile"
+#define LST_TYPE "x-obex/folder-listing"
+
+#ifdef G_THREADS_ENABLED
+# ifdef DEBUG
+# define GW_OBEX_LOCK(ctx) do { \
+ debug("Attempting GW_OBEX_LOCK at %s:%d (%s)...", __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ fflush(stdout); \
+ g_mutex_lock((ctx)->mutex); \
+ debug("got it!\n"); \
+ } while (0)
+# define GW_OBEX_UNLOCK(ctx) do { \
+ debug("Unlocking GW_OBEX_LOCK at %s:%d (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ g_mutex_unlock((ctx)->mutex); \
+ } while (0)
+# else
+# define GW_OBEX_LOCK(ctx) g_mutex_lock((ctx)->mutex)
+# define GW_OBEX_UNLOCK(ctx) g_mutex_unlock((ctx)->mutex)
+# endif
+#else
+# define GW_OBEX_LOCK(ctx) ((void)(0))
+# define GW_OBEX_UNLOCK(ctx) ((void)(0))
+#endif
+
+typedef struct obex_setpath_hdr {
+ uint8_t flags;
+ uint8_t constants;
+} __attribute__ ((packed)) obex_setpath_hdr_t;
+
+typedef struct obex_connect_hdr {
+ uint8_t version;
+ uint8_t flags;
+ uint16_t mtu;
+} __attribute__ ((packed)) obex_connect_hdr_t;
+
+struct gw_obex {
+#ifdef G_THREADS_ENABLED
+ /* To get rid of race conditions in multithreaded apps */
+ GMutex *mutex;
+#endif
+
+ /* Main OpenOBEX handle */
+ obex_t *handle;
+
+ /* Exception callback and associated data */
+ gw_obex_disconnect_cb_t dc_cb;
+ gpointer dc_data;
+
+ /* Progress callback and associated data */
+ gw_obex_progress_cb_t pr_cb;
+ gpointer pr_data;
+
+ /* Whether calling pr_cb is necessary or not */
+ gboolean report_progress;
+
+ /* Cancel callback and associated data */
+ gw_obex_cancel_cb_t cancel_cb;
+ gpointer cancel_data;
+
+ /* For checking if the current operation is finished */
+ gboolean done;
+
+ /* TRUE if a link error has hapened */
+ gboolean link_err;
+
+ /* FD for the transport connection */
+ int conn_fd;
+
+ GMainContext *main_ctx;
+
+ /* The transport connection's GIOChannel */
+ GIOChannel *gio;
+
+ /* The transport connection's GSource */
+ GSource *gio_source;
+
+ /* OBEX Connection ID */
+ uint32_t conid;
+
+ /* The last OBEX response code */
+ uint8_t obex_rsp;
+
+ /* The current OBEX operation */
+ uint8_t obex_op;
+
+ /* This is set if some operation fails */
+ gint error;
+
+ /* Bytes to read at a time when doing a put */
+ uint16_t tx_max;
+
+ /* How many bytes to allocate for incomming object data */
+ uint16_t rx_max;
+
+ /* Current object transfer handle */
+ struct gw_obex_xfer *xfer;
+};
+
+GwObex *make_context(obex_t *handle);
+
+gboolean gw_obex_set_error(GwObex *ctx);
+
+void gw_obex_get_error(GwObex *ctx, gint *error);
+
+void obex_link_error(GwObex *ctx);
+
+gboolean gw_obex_cb(GIOChannel *chan, GIOCondition cond, gpointer data);
+
+gboolean gw_obex_connect(GwObex *ctx, const char *target, size_t target_len);
+
+gboolean gw_obex_disconnect(GwObex *ctx);
+
+gboolean gw_obex_transport_setup(int fd, obex_t **handle);
+
+gboolean gw_obex_action_op(GwObex *ctx, const gchar *src, const gchar *dst,
+ uint8_t action);
+
+gboolean gw_obex_setpath(GwObex *ctx, const gchar *path, int flags);
+
+/** Get an object from the server
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param local Local filename which contains the object
+ * @param remote Remote filename to store the object in
+ * @param type MIME-type of the object (NULL if not known)
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_get(GwObex *ctx,
+ const gchar *local, const gchar *remote, const gchar *type,
+ gchar **buf, gint *buf_size, int stream_fd,
+ gboolean async);
+
+/** Send an object to the server
+ * @param ctx Pointer returned by gw_obex_setup()
+ * @param local Local filename to store the objec in
+ * @param remote Remote filename which contains the object
+ * @param type MIME-type of the object (NULL if not known)
+ * @returns TRUE on success, FALSE on failure
+ */
+gboolean gw_obex_put(GwObex *ctx,
+ const gchar *local, const gchar *remote, const gchar *type,
+ const gchar *buf, gint buf_size, time_t object_time,
+ int stream_fd, gboolean async);
+
+#endif /* _OBEX_PRIV_H_ */
diff --git a/obexd/gwobex/obex-xfer.c b/obexd/gwobex/obex-xfer.c
new file mode 100644
index 0000000..1cce005
--- /dev/null
+++ b/obexd/gwobex/obex-xfer.c
+/**
+ @file obex-xfer.c
+
+ Object transfer related functions for the GW OBEX Library
+
+ @author Johan Hedberg <johan.hedberg@nokia.com>
+
+ Copyright (C) 2004-2006 Nokia Corporation. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License, version 2.1 as published by the Free Software Foundation.
+
+ 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <openobex/obex.h>
+
+#include "obex-priv.h"
+#include "obex-xfer.h"
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "log.h"
+#include "obex-priv.h"
+#include "obex-xfer.h"
+#include "gw-obex.h"
+
+static gboolean handle_input(GwObex *ctx, gint *err) {
+ gboolean ret = TRUE;
+ int r;
+
+ r = OBEX_HandleInput(ctx->handle, 10);
+
+ if (r < 0) {
+ debug("OBEX_HandleInput() failed\n");
+ obex_link_error(ctx);
+ if (err)
+ *err = GW_OBEX_ERROR_INTERNAL;
+ ret = FALSE;
+ }
+ else if (r == 0) { /* Timeout */
+ debug("OBEX_HandleInput(): timeout\n");
+ if (err)
+ *err = GW_OBEX_ERROR_TIMEOUT;
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+struct gw_obex_xfer *gw_obex_xfer_new(struct gw_obex *ctx, gboolean async, int stream_fd) {
+ struct gw_obex_xfer *xfer;
+ size_t buf_size = (ctx->obex_op == OBEX_CMD_GET) ? ctx->rx_max : ctx->tx_max;
+
+ xfer = g_new0(struct gw_obex_xfer, 1);
+
+ xfer->ctx = ctx;
+ xfer->async = async;
+ xfer->stream_fd = stream_fd;
+ xfer->target_size = GW_OBEX_UNKNOWN_LENGTH;
+ xfer->modtime = -1;
+
+ if (async || (stream_fd >= 0 && ctx->obex_op == OBEX_CMD_PUT)) {
+ xfer->buf = g_malloc(buf_size);
+ xfer->buf_size = buf_size;
+ }
+
+ if (async && ctx->obex_op == OBEX_CMD_PUT)
+ xfer->do_cb = TRUE;
+
+ return xfer;
+}
+
+gboolean gw_obex_xfer_do_abort(struct gw_obex_xfer *xfer) {
+ debug("gw_obex_xfer_do_abort()\n");
+
+ if (xfer->ctx->conn_fd < 0 || xfer->ctx->xfer == NULL || xfer->ctx->done)
+ return FALSE;
+
+ if (xfer->abort)
+ return TRUE;
+
+ xfer->abort = TRUE;
+
+#ifdef USE_NICE_ABORT
+ debug("Performing nice abort\n");
+ if (OBEX_CancelRequest(xfer->ctx->handle, TRUE) != 0)
+ return FALSE;
+ return TRUE;
+#else
+ debug("Performing abort through disconnection (without ABORT command)\n");
+ xfer->ctx->done = TRUE;
+ OBEX_CancelRequest(xfer->ctx->handle, FALSE);
+ obex_link_error(xfer->ctx);
+ return FALSE;
+#endif
+}
+
+GwObexXfer *gw_obex_put_async(GwObex *ctx, const char *name, const char *type,
+ gint size, time_t time, gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(NULL, error, ctx);
+ ret = gw_obex_put(ctx, NULL, name, type, NULL, size, time, -1, TRUE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret ? ctx->xfer : NULL;
+}
+
+GwObexXfer *gw_obex_get_async(GwObex *ctx, const char *name, const char *type, gint *error) {
+ gboolean ret;
+ GW_OBEX_LOCK(ctx);
+ CHECK_DISCONNECT(NULL, error, ctx);
+ ret = gw_obex_get(ctx, NULL, name, type, NULL, NULL, -1, TRUE);
+ if (ret == FALSE)
+ gw_obex_get_error(ctx, error);
+ GW_OBEX_UNLOCK(ctx);
+ return ret ? ctx->xfer : NULL;
+}
+
+static gboolean gw_obex_put_idle(GwObexXfer *xfer) {
+ struct gw_obex *ctx = xfer->ctx;
+
+ g_source_destroy(xfer->idle_source);
+ xfer->idle_source = NULL;
+
+ if (!ctx)
+ return FALSE;
+
+ GW_OBEX_LOCK(ctx);
+
+ if (xfer->cb && xfer->do_cb) {
+ xfer->do_cb = FALSE;
+ GW_OBEX_UNLOCK(ctx);
+ xfer->cb(xfer, xfer->cb_data);
+ GW_OBEX_LOCK(ctx);
+ }
+
+ GW_OBEX_UNLOCK(ctx);
+
+ return FALSE;
+}
+
+void gw_obex_xfer_set_callback(GwObexXfer *xfer, gw_obex_xfer_cb_t cb, gpointer user_data) {
+ GwObex *ctx = xfer->ctx;
+
+ GW_OBEX_LOCK(ctx);
+
+ xfer->cb = cb;
+ xfer->cb_data = user_data;
+
+ if (xfer->do_cb && xfer->idle_source == NULL) {
+ xfer->idle_source = g_idle_source_new();
+ g_source_set_callback(xfer->idle_source, (GSourceFunc)gw_obex_put_idle, xfer, NULL);
+ (void) g_source_attach(xfer->idle_source, ctx->main_ctx);
+ g_source_unref(xfer->idle_source);
+ }
+
+ GW_OBEX_UNLOCK(ctx);
+}
+
+time_t gw_obex_xfer_object_time(GwObexXfer *xfer) {
+ return xfer->modtime;
+}
+
+gint gw_obex_xfer_object_size(GwObexXfer *xfer) {
+ return xfer->target_size;
+}
+
+gboolean gw_obex_xfer_write(GwObexXfer *xfer, const char *buf, gint buf_size,
+ gint *bytes_written, gint *err) {
+ GwObex *ctx = xfer->ctx;
+ gboolean ret = TRUE;
+ gint free_space;
+
+ debug("gw_obex_xfer_write(buf_size=%d): entered\n", buf_size);
+
+ if (!ctx) {
+ if (err)
+ *err = GW_OBEX_ERROR_INVALID_PARAMS;
+ return FALSE;
+ }
+
+ GW_OBEX_LOCK(ctx);
+
+ if (ctx->obex_op != OBEX_CMD_PUT) {
+ ret = FALSE;
+ if (err)
+ *err = GW_OBEX_ERROR_INVALID_PARAMS;
+ goto out;
+ }
+
+ if (gw_obex_set_error(ctx)) {
+ gw_obex_get_error(ctx, err);
+ ret = FALSE;
+ goto out;
+ }
+
+ free_space = xfer->buf_size - (xfer->data_start + xfer->data_length);
+
+ *bytes_written = buf_size > free_space ? free_space : buf_size;
+
+ memcpy(&xfer->buf[xfer->data_start + xfer->data_length], buf, *bytes_written);
+
+ xfer->data_length += *bytes_written;
+ free_space -= *bytes_written;
+
+ if (xfer->object) {
+ if (OBEX_Request(ctx->handle, xfer->object) < 0) {
+ debug("OBEX_Request() failed\n");
+ xfer->data_length -= *bytes_written;
+ ret = FALSE;
+ goto out;
+ }
+
+ xfer->object = NULL;
+
+ /* Recalculate free space */
+ free_space = xfer->buf_size - (xfer->data_start + xfer->data_length);
+ }
+
+ if (xfer->data_length >= ctx->tx_max || !free_space) {
+ gint old_length = xfer->data_length;
+
+ debug("OBEX_ResumeRequest at %s:%d (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ OBEX_ResumeRequest(ctx->handle);
+
+ if (!xfer->block)
+ goto out;
+
+ /* Call OBEX_HandleInput if the xfer is blocking and no data could be sent */
+ while (old_length == xfer->data_length) {
+ if (gw_obex_set_error(ctx)) {
+ gw_obex_get_error(ctx, err);
+ ret = FALSE;
+ goto out;
+ }
+
+ if (!handle_input(ctx, err)) {
+ ret = FALSE;
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (xfer->cb && xfer->do_cb && xfer->idle_source == NULL) {
+ xfer->idle_source = g_idle_source_new();
+ g_source_set_callback(xfer->idle_source, (GSourceFunc)gw_obex_put_idle, xfer, NULL);
+ (void) g_source_attach(xfer->idle_source, ctx->main_ctx);
+ g_source_unref(xfer->idle_source);
+ }
+
+ GW_OBEX_UNLOCK(ctx);
+ if (ret)
+ debug("gw_obex_xfer_write(): returning, %d bytes written\n", *bytes_written);
+ else
+ debug("gw_obex_xfer_write(): returning, failed (%d)\n", err ? *err : 0);
+ return ret;
+}
+
+gboolean gw_obex_xfer_read(GwObexXfer *xfer, char *buf, gint buf_size,
+ gint *bytes_read, gint *err) {
+ GwObex *ctx = xfer->ctx;
+ gboolean ret = TRUE;
+
+ debug("gw_obex_xfer_read(buf_size=%d): entered\n", buf_size);
+
+ if (!ctx) {
+ if (err)
+ *err = GW_OBEX_ERROR_INVALID_PARAMS;
+ return FALSE;
+ }
+
+ GW_OBEX_LOCK(ctx);
+
+ if (ctx->obex_op != OBEX_CMD_GET) {
+ ret = FALSE;
+ if (err)
+ *err = GW_OBEX_ERROR_INVALID_PARAMS;
+ goto out;
+ }
+
+ while (TRUE) {
+ if (gw_obex_set_error(ctx)) {
+ gw_obex_get_error(ctx, err);
+ ret = FALSE;
+ goto out;
+ }
+
+ if (xfer->data_length)
+ break;
+
+ if (ctx->done) {
+ *bytes_read = 0;
+ goto out;
+ }
+
+ if (xfer->block) {
+ if (!handle_input(ctx, err)) {
+ ret = FALSE;
+ goto out;
+ }
+ }
+ else {
+ ret = FALSE;
+ if (err)
+ *err = GW_OBEX_ERROR_NO_DATA;
+ goto out;
+ }
+ }
+
+ *bytes_read = buf_size < xfer->data_length ? buf_size : xfer->data_length;
+
+ memcpy(buf, &xfer->buf[xfer->data_start], *bytes_read);
+
+ xfer->data_length -= *bytes_read;
+
+ if (xfer->data_length)
+ xfer->data_start += *bytes_read;
+ else {
+ xfer->data_start = 0;
+ debug("OBEX_ResumeRequest at %s:%d (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ OBEX_ResumeRequest(ctx->handle);
+ }
+
+out:
+ GW_OBEX_UNLOCK(ctx);
+ if (ret)
+ debug("gw_obex_xfer_read(): returning, %d bytes read\n", *bytes_read);
+ else
+ debug("gw_obex_xfer_read(): returning, failed (%d)\n", err ? *err : 0);
+ return ret;
+}
+
+gboolean gw_obex_xfer_flush(GwObexXfer *xfer, gint *err) {
+ gboolean ret = TRUE;
+ struct gw_obex *ctx = xfer->ctx;
+
+ if (!ctx) {
+ if (err)
+ *err = GW_OBEX_ERROR_INVALID_PARAMS;
+ return FALSE;
+ }
+
+ GW_OBEX_LOCK(ctx);
+
+ if (ctx->obex_op != OBEX_CMD_PUT)
+ goto out;
+
+ if (gw_obex_set_error(ctx)) {
+ gw_obex_get_error(ctx, err);
+ ret = FALSE;
+ goto out;
+ }
+
+ while (xfer->data_length) {
+ debug("OBEX_ResumeRequest at %s:%d (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ OBEX_ResumeRequest(ctx->handle);
+
+ if (gw_obex_set_error(ctx)) {
+ gw_obex_get_error(ctx, err);
+ ret = FALSE;
+ goto out;
+ }
+
+ if (xfer->data_length) {
+ if (!handle_input(ctx, err)) {
+ ret = FALSE;
+ goto out;
+ }
+ }
+ }
+
+out:
+ GW_OBEX_UNLOCK(ctx);
+ return ret;
+}
+
+void _gw_obex_xfer_free(struct gw_obex_xfer *xfer) {
+ g_free(xfer->buf);
+ g_free(xfer);
+}
+
+void gw_obex_xfer_free(struct gw_obex_xfer *xfer) {
+ if (xfer->ctx)
+ gw_obex_xfer_close(xfer, NULL);
+ _gw_obex_xfer_free(xfer);
+}
+
+gboolean gw_obex_xfer_close(GwObexXfer *xfer, gint *err) {
+ gboolean ret = TRUE;
+ struct gw_obex *ctx = xfer->ctx;
+
+ /* If previous close() failed, just signal success so caller can continue */
+ if (!ctx)
+ return TRUE;
+
+ GW_OBEX_LOCK(ctx);
+
+ xfer->close = TRUE;
+
+ if (ctx->obex_op == OBEX_CMD_GET && !ctx->done)
+ gw_obex_xfer_do_abort(xfer);
+
+ if (ctx->obex_op == OBEX_CMD_PUT) {
+ if (xfer->object) {
+ if (OBEX_Request(ctx->handle, xfer->object) < 0) {
+ debug("OBEX_Request() failed\n");
+ ctx->done = TRUE;
+ }
+ xfer->object = NULL;
+ }
+ else {
+ debug("OBEX_ResumeRequest at %s:%d (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
+ OBEX_ResumeRequest(ctx->handle);
+ }
+ }
+
+ while (!ctx->done) {
+ if (!handle_input(ctx, err)) {
+ ret = FALSE;
+ break;
+ }
+ }
+
+ /* Check for error but ignore ERROR_ABORT since we can still do a proper
+ * xfer_close() in that case */
+ if (gw_obex_set_error(ctx) && ctx->error != GW_OBEX_ERROR_ABORT) {
+ gw_obex_get_error(ctx, err);
+ ret = FALSE;
+ }
+
+ /* Remove the idle function related to this transfer (if there is one) */
+ if (xfer->idle_source) {
+ g_source_destroy(xfer->idle_source);
+ xfer->idle_source = NULL;
+ }
+
+ /* Disassociate from the GwObex object */
+ ctx->xfer = NULL;
+ xfer->ctx = NULL;
+
+ GW_OBEX_UNLOCK(ctx);
+
+ return ret;
+}
+
+gboolean gw_obex_xfer_abort(GwObexXfer *xfer, gint *err) {
+ GwObex *ctx = xfer->ctx;
+ gboolean ret = TRUE;
+
+ /* If previous call failed just signal success so caller can continue */
+ if (!ctx)
+ return TRUE;
+
+ GW_OBEX_LOCK(ctx);
+
+ /* Return if abort has already been sent */
+ if (xfer->abort)
+ goto out;
+
+ /* Return if actual request hasn't been sent */
+ if (xfer->object) {
+ OBEX_ObjectDelete(ctx->handle, xfer->object);
+ xfer->object = NULL;
+ ctx->done = TRUE;
+ goto out;
+ }
+
+ if (!gw_obex_xfer_do_abort(xfer)) {
+ ret = FALSE;
+ if (err)
+ *err = GW_OBEX_ERROR_INTERNAL;
+ goto out;
+ }
+
+out:
+ GW_OBEX_UNLOCK(ctx);
+
+ gw_obex_xfer_close(xfer, err);
+
+ return ret;
+}
+
+void gw_obex_xfer_set_blocking(GwObexXfer *xfer, gboolean block) {
+ GW_OBEX_LOCK(xfer->ctx);
+ xfer->block = block;
+ GW_OBEX_UNLOCK(xfer->ctx);
+}
diff --git a/obexd/gwobex/obex-xfer.h b/obexd/gwobex/obex-xfer.h
new file mode 100644
index 0000000..9771f02
--- /dev/null
+++ b/obexd/gwobex/obex-xfer.h
+/**
+ @file obex-xfer.h
+
+ @author Johan Hedberg <johan.hedberg@nokia.com>
+
+ Copyright (C) 2004-2006 Nokia Corporation. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License, version 2.1, as published by the Free Software Foundation.
+
+ 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.
+
+*/
+#ifndef _OBEX_XFER_H_
+#define _OBEX_XFER_H_
+
+#include <stdint.h>
+#include <time.h>
+#include <glib.h>
+
+#include <openobex/obex.h>
+
+#include "gw-obex.h"
+#include "obex-priv.h"
+
+struct gw_obex_xfer {
+ /* Pointer to parent gw_obex struct */
+ struct gw_obex *ctx;
+
+ /* Used only for async PUT transfers */
+ obex_object_t *object;
+
+ /* Sync or async transfer */
+ gboolean async;
+
+ /* If read and write operations should block for an async transfer */
+ gboolean block;
+
+ /* When doing a get or put for a local file */
+ int stream_fd;
+
+ /* TRUE if the current operation was aborted */
+ gboolean abort;
+
+ /* Transfer should be closed when no more data to send */
+ gboolean close;
+
+ /* Temporary buffer when doing a put or get */
+ unsigned char *buf;
+ size_t buf_size;
+
+ /* These two elements are only used for async transfers */
+ size_t data_start;
+ size_t data_length;
+
+ /* Bytes read or written for the current get/put operation */
+ size_t counter;
+
+ /* Target length of the current get/put operation */
+ gint target_size;
+
+ /* Modification time of last file transfered */
+ time_t modtime;
+
+ gboolean do_cb;
+ gw_obex_xfer_cb_t cb;
+ gpointer cb_data;
+
+ GSource *idle_source;
+};
+
+struct gw_obex_xfer *gw_obex_xfer_new(struct gw_obex *ctx, gboolean async, int stream_fd);
+
+void _gw_obex_xfer_free(struct gw_obex_xfer *xfer);
+
+gboolean gw_obex_xfer_do_abort(struct gw_obex_xfer *xfer);
+
+#endif /* _OBEX_XFER_H_ */
diff --git a/obexd/gwobex/utils.c b/obexd/gwobex/utils.c
new file mode 100644
index 0000000..02afa2e
--- /dev/null
+++ b/obexd/gwobex/utils.c
+/**
+ @file utils.c
+
+ @author Johan Hedberg <johan.hedberg@nokia.com>
+
+ Copyright (C) 2004-2006 Nokia Corporation. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License, version 2.1, as published by the Free Software Foundation.
+
+ 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 <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <glib.h>
+#include <termios.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "log.h"
+#include "utils.h"
+
+#ifdef DEBUG
+char *bytestr(const uint8_t *uuid, int len) {
+ int i;
+ char *str = g_malloc((len << 1) + 1);
+
+ for (i = 0; i < len; i++)
+ sprintf(str + (2*i), "%02X", uuid[i]);
+
+ return str;
+}
+#endif
+
+gboolean fd_raw_mode(int fd) {
+ struct termios mode;
+
+ memset(&mode, 0, sizeof (mode));
+ if (tcgetattr(fd, &mode) < 0) {
+ debug("tcgetattr(%d, &mode): %s", fd, strerror(errno));
+ return FALSE;
+ }
+
+ mode.c_iflag = 0;
+ mode.c_oflag &= ~OPOST;
+ mode.c_lflag &= ~(ISIG | ICANON | ECHO
+#ifdef XCASE
+ | XCASE
+#endif
+ );
+ mode.c_cc[VMIN] = 1;
+ mode.c_cc[VTIME] = 0;
+
+ if (tcsetattr(fd, TCSADRAIN, &mode) < 0) {
+ debug("tcsetattr(%d, TCSADRAIN, &mode): %s", fd, strerror(errno));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+glong get_uname(gunichar2 **uname, const gchar *name) {
+ glong uname_len;
+
+ if (*name == '\0') {
+ *uname = NULL;
+ return 0;
+ }
+
+ *uname = g_utf8_to_utf16(name, -1, NULL, &uname_len, NULL);
+
+ if (*uname == NULL)
+ uname_len = -1;
+ else {
+ int i;
+ /* g_utf8_to_utf16 produces host-byteorder UTF-16,
+ * but OBEX requires network byteorder (big endian) */
+ for (i = 0; i < uname_len; i++)
+ (*uname)[i] = g_htons((*uname)[i]);
+ uname_len = (uname_len + 1) << 1;
+ }
+
+ return uname_len;
+}
+
+int make_iso8601(time_t time, char *str, int len) {
+ struct tm tm;
+#if defined(HAVE_TIMEZONE) && defined(USE_LOCALTIME)
+ time_t tz_offset = 0;
+
+ tz_offset = -timezone;
+ if (daylight > 0)
+ tz_offset += 3600;
+ time += tz_offset;
+#endif
+
+ if (gmtime_r(&time, &tm) == NULL)
+ return -1;
+
+ tm.tm_year += 1900;
+ tm.tm_mon++;
+
+ return snprintf(str, len,
+#ifdef USE_LOCALTIME
+ "%04u%02u%02uT%02u%02u%02u",
+#else
+ "%04u%02u%02uT%02u%02u%02uZ",
+#endif
+ tm.tm_year, tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
+/* From Imendio's GnomeVFS OBEX module (om-utils.c) */
+time_t parse_iso8601(const gchar *str, int len) {
+ gchar *tstr;
+ struct tm tm;
+ gint nr;
+ gchar tz;
+ time_t time;
+ time_t tz_offset = 0;
+
+ memset (&tm, 0, sizeof (struct tm));
+
+ /* According to spec the time doesn't have to be null terminated */
+ if (str[len - 1] != '\0') {
+ tstr = g_malloc(len + 1);
+ strncpy(tstr, str, len);
+ tstr[len] = '\0';
+ }
+ else
+ tstr = g_strdup(str);
+
+ nr = sscanf (tstr, "%04u%02u%02uT%02u%02u%02u%c",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
+ &tz);
+
+ g_free(tstr);
+
+ /* Fixup the tm values */
+ tm.tm_year -= 1900; /* Year since 1900 */
+ tm.tm_mon--; /* Months since January, values 0-11 */
+ tm.tm_isdst = -1; /* Daylight savings information not avail */
+
+ if (nr < 6) {
+ /* Invalid time format */
+ return -1;
+ }
+
+ time = mktime (&tm);
+
+#if defined(HAVE_TM_GMTOFF)
+ tz_offset = tm.tm_gmtoff;
+#elif defined(HAVE_TIMEZONE)
+ tz_offset = -timezone;
+ if (tm.tm_isdst > 0) {
+ tz_offset += 3600;
+ }
+#endif
+
+ if (nr == 7) { /* Date/Time was in localtime (to remote device)
+ * already. Since we don't know anything about the
+ * timezone on that one we won't try to apply UTC offset
+ */
+ time += tz_offset;
+ }
+
+ return time;
+}
+
diff --git a/obexd/gwobex/utils.h b/obexd/gwobex/utils.h
new file mode 100644
index 0000000..de1ba05
--- /dev/null
+++ b/obexd/gwobex/utils.h
+/**
+ @file utils.h
+
+ @author Johan Hedberg <johan.hedberg@nokia.com>
+
+ Copyright (C) 2004-2006 Nokia Corporation. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License, version 2.1, as published by the Free Software Foundation.
+
+ 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.
+
+*/
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <time.h>
+
+/** Create ISO8601 time format string from time_t
+ * @param time Time to convert
+ * @param str Pointer where result is stored
+ * @param len Maximum amount of chars written
+ * @returns length of created string.
+ */
+int make_iso8601(time_t time, char *str, int len);
+
+/** Convert a time string in ISO8601 format to time_t
+ * @param str Time string in ISO8601 format
+ * @param len Length of string
+ * @returns time as time_t format
+ */
+time_t parse_iso8601(const gchar *str, int len);
+
+#ifdef DEBUG
+char *bytestr(const uint8_t *uuid, int len);
+#endif
+
+/** Convert an UTF-8 string to UTF-16 (Network byte order)
+ * @param uname, Place to store the new UTF-16 string
+ * @param name, Original UTF-8 string
+ * @returns Size in bytes allocated for the UTF-16 string (uname)
+ */
+glong get_uname(gunichar2 **uname, const gchar *name);
+
+gboolean fd_raw_mode(int fd);
+
+#endif /* _UTILS_H */