diff --git a/obexd/plugins/ebook.c b/obexd/plugins/ebook.c
index 9faecab..a5b43c8 100644
--- a/obexd/plugins/ebook.c
+++ b/obexd/plugins/ebook.c
#endif
#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <bluetooth/bluetooth.h>
-#include <plugin.h>
-#include <logging.h>
-#include <phonebook.h>
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
#include <libebook/e-book.h>
+#include "plugin.h"
+#include "logging.h"
+#include "obex.h"
+#include "service.h"
+
+#define PHONEBOOK_TYPE "x-bt/phonebook"
+#define VCARDLISTING_TYPE "x-bt/vcard-listing"
+#define VCARDENTRY_TYPE "x-bt/vcard"
+
+#define ORDER_TAG 0x01
+#define SEARCHVALUE_TAG 0x02
+#define SEARCHATTRIB_TAG 0x03
+#define MAXLISTCOUNT_TAG 0x04
+#define LISTSTARTOFFSET_TAG 0x05
+#define FILTER_TAG 0x06
+#define FORMAT_TAG 0X07
+#define PHONEBOOKSIZE_TAG 0X08
+#define NEWMISSEDCALLS_TAG 0X09
+
+/* The following length is in the unit of byte */
+#define ORDER_LEN 1
+#define SEARCHATTRIB_LEN 1
+#define MAXLISTCOUNT_LEN 2
+#define LISTSTARTOFFSET_LEN 2
+#define FILTER_LEN 8
+#define FORMAT_LEN 1
+#define PHONEBOOKSIZE_LEN 2
+#define NEWMISSEDCALLS_LEN 1
+
+#define MCH "telecom/mch.vcf"
+#define SIM1_MCH "SIM1/telecom/mch.vcf"
+
#define DEFAULT_COUNT 65535
#define EOL_CHARS "\n"
#define QUERY_GIVEN_NAME "(contains \"given_name\" \"%s\")"
#define QUERY_PHONE "(contains \"phone\" \"%s\")"
+#define APPARAM_HDR_SIZE 2
+
+#define get_be64(val) GUINT64_FROM_BE(bt_get_unaligned((guint64 *) val))
+#define get_be16(val) GUINT16_FROM_BE(bt_get_unaligned((guint16 *) val))
+
+#define put_be16(val, ptr) bt_put_unaligned(GUINT16_TO_BE(val), (guint16 *) ptr)
+
+#define PBAP_CHANNEL 15
+
+#define PBAP_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+<record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"0x112f\"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0003\"/> \
+ <uint8 value=\"%u\" name=\"channel\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0008\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x1130\"/> \
+ <uint16 value=\"0x0100\" name=\"version\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0100\"> \
+ <text value=\"%s\" name=\"name\"/> \
+ </attribute> \
+ \
+ <attribute id=\"0x0314\"> \
+ <uint8 value=\"0x01\"/> \
+ </attribute> \
+</record>"
+
+struct apparam_hdr {
+ uint8_t tag;
+ uint8_t len;
+ uint8_t val[0];
+} __attribute__ ((packed));
+
+struct apparam_field {
+ guint64 filter;
+ guint16 maxlistcount;
+ guint16 liststartoffset;
+ guint8 format;
+ guint8 order;
+ guint8 searchattrib;
+ guint8 *searchval;
+};
+
struct phonebook_data {
- struct phonebook_context *context;
- guint64 filter;
- guint8 format;
- guint16 maxlistcount;
- guint16 liststartoffset;
- guint16 index;
+ obex_t *obex;
+ obex_object_t *obj;
+ struct apparam_field params;
};
+static const guint8 PBAP_TARGET[TARGET_SIZE] = {
+ 0x79, 0x61, 0x35, 0xF0, 0xF0, 0xC5, 0x11, 0xD8,
+ 0x09, 0x66, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66 };
+
static char *vcard_attribs[29] = { EVC_VERSION, EVC_FN, EVC_N, EVC_PHOTO,
EVC_BDAY, EVC_ADR, EVC_LABEL, EVC_TEL,
EVC_EMAIL, EVC_MAILER, NULL, EVC_GEO,
EVC_UID, EVC_KEY, EVC_NICKNAME, EVC_CATEGORIES,
EVC_PRODID, NULL, NULL, NULL };
-static int ebook_create(struct phonebook_context *context)
-{
- DBG("context %p", context);
-
- return 0;
-}
-
-static void ebook_destroy(struct phonebook_context *context)
-{
- DBG("context %p", context);
-}
-
static void ebookpull_cb(EBook *book, EBookStatus status, GList *list,
gpointer user_data)
{
struct phonebook_data *pb_data = user_data;
- struct phonebook_context *context = pb_data->context;
- guint64 filter = pb_data->filter;
- guint8 format = pb_data->format;
- guint16 liststartoffset = pb_data->liststartoffset, offset = 0;
- guint16 maxlistcount = pb_data->maxlistcount, count = 0;
+ struct apparam_field *params = &pb_data->params;
+ struct obex_session *session = OBEX_GetUserData(pb_data->obex);
+ guint16 offset = 0, count = 0;
GList *contacts = list;
GString *pb;
gchar *result;
- gint32 str_len;
+ gint32 size;
pb = g_string_new(NULL);
/* Mandatory attributes for vCard 3.0 are VERSION, N, FN and TEL */
- if (filter != 0 && format == EVC_FORMAT_VCARD_30)
- filter = filter | 0x87;
+ if (params->filter != 0 && params->format == EVC_FORMAT_VCARD_30)
+ params->filter |= 0x87;
for (; contacts != NULL; contacts = g_list_next(contacts)) {
EContact *contact = NULL;
GList *attrib_list = NULL, *l;
char *vcard;
- if (offset < liststartoffset) {
+ if (offset < params->liststartoffset) {
offset++;
continue;
}
- if (count < maxlistcount)
+ if (count < params->maxlistcount)
count++;
else
break;
evcard = E_VCARD(contact);
attrib_list = e_vcard_get_attributes(evcard);
- if (!filter) {
- vcard = e_vcard_to_string(evcard, format);
+ if (!params->filter) {
+ vcard = e_vcard_to_string(evcard, params->format);
goto done;
}
int mask;
mask = 1 << i;
- if (!(filter & mask))
+ if (!(params->filter & mask))
continue;
if (g_strcmp0(vcard_attribs[i], attrib_name))
continue;
break;
}
}
- vcard = e_vcard_to_string(evcard_filtered, format);
+ vcard = e_vcard_to_string(evcard_filtered, params->format);
g_object_unref(evcard_filtered);
done: g_string_append_printf(pb, "%s\n", vcard);
}
result = g_string_free(pb, FALSE);
- str_len = strlen(result);
- phonebook_return(context, result, str_len);
+ size = strlen(result);
+
+ if (size != 0) {
+ session->buf = g_realloc(session->buf, session->size + size);
+ memcpy(session->buf + session->size, result, size);
+ session->size += size;
+ }
- if (str_len != 0)
- phonebook_return(context, NULL, 0);
+ session->finished = 1;
+ OBEX_ResumeRequest(session->obex);
g_free(result);
g_free(pb_data);
- phonebook_unref(context);
g_object_unref(book);
}
-static int ebook_pullphonebook(struct phonebook_context *context,
- gchar *objname, guint64 filter, guint8 format,
- guint16 maxlistcount, guint16 liststartoffset,
- guint16 *phonebooksize, guint8 *newmissedcalls)
+static int ebook_pullphonebook(obex_t *obex, obex_object_t *obj,
+ struct apparam_field params)
{
struct phonebook_data *pb_data;
EBook *book;
EBookQuery *query;
- DBG("context %p", context);
-
- if (maxlistcount == 0) {
- *phonebooksize = DEFAULT_COUNT;
- return 0;
- }
-
- if (format != EVC_FORMAT_VCARD_30) {
+ if (params.format != EVC_FORMAT_VCARD_30) {
DBG("libebook does not support e_vcard_to_string_vcard_21()");
return -1;
}
- phonebook_ref(context);
-
pb_data = g_new0(struct phonebook_data, 1);
- pb_data->context = context;
- pb_data->filter = filter;
- pb_data->format = format;
- pb_data->maxlistcount = maxlistcount;
- pb_data->liststartoffset = liststartoffset;
+ pb_data->obex = obex;
+ pb_data->obj = obj;
+ pb_data->params = params;
book = e_book_new_default_addressbook(NULL);
e_book_query_unref(query);
+ OBEX_SuspendRequest(obex, obj);
+
return 0;
}
gpointer user_data)
{
struct phonebook_data *pb_data = user_data;
- struct phonebook_context *context = pb_data->context;
- guint16 liststartoffset = pb_data->liststartoffset, offset = 0;
- guint16 maxlistcount = pb_data->maxlistcount, count = 0;
+ struct apparam_field *params = &pb_data->params;
+ struct obex_session *session = OBEX_GetUserData(pb_data->obex);
+ guint16 offset = 0, count = 0;
GString *listing;
GList *contacts = list;
gchar *result;
- gint32 str_len;
+ gint32 size;
listing = g_string_new(VL_VERSION);
listing = g_string_append(listing, VL_TYPE);
GList *name_values = NULL;
gchar *name = NULL, *name_part = NULL, *element = NULL;
- if (offset < liststartoffset) {
+ if (offset < params->liststartoffset) {
offset++;
continue;
}
- if (count < maxlistcount)
+ if (count < params->maxlistcount)
count++;
else
break;
listing = g_string_append(listing, VL_BODY_END);
result = g_string_free(listing, FALSE);
- str_len = strlen(result);
- phonebook_return(context, result, str_len);
+ size = strlen(result);
+
+ if (size != 0) {
+ session->buf = g_realloc(session->buf, session->size + size);
+ memcpy(session->buf + session->size, result, size);
+ session->size += size;
+ }
- if (str_len != 0)
- phonebook_return(context, NULL, 0);
+ session->finished = 1;
+ OBEX_ResumeRequest(session->obex);
g_free(result);
g_free(pb_data);
- phonebook_unref(context);
g_object_unref(book);
}
-static int ebook_pullvcardlisting(struct phonebook_context *context,
- gchar *objname, guint8 order, guint8 *searchval,
- guint8 searchattrib, guint16 maxlistcount,
- guint16 liststartoffset, guint16 *phonebooksize,
- guint8 *newmissedcalls)
+static int ebook_pullvcardlisting(obex_t *obex, obex_object_t *obj,
+ struct apparam_field params)
{
struct phonebook_data *pb_data;
EBook *book;
gchar *str1 = NULL, *str2 = NULL;
gchar **value_list = NULL;
- DBG("context %p", context);
-
- if (maxlistcount == 0) {
- *phonebooksize = DEFAULT_COUNT;
- return 0;
- }
-
- /* libebook does not support sound attribute */
- if (searchattrib >= 2) {
- DBG("libebook does not support sound attribute");
- return -1;
- }
-
- phonebook_ref(context);
-
pb_data = g_new0(struct phonebook_data, 1);
- pb_data->context = context;
- pb_data->maxlistcount = maxlistcount;
- pb_data->liststartoffset = liststartoffset;
+ pb_data->obex = obex;
+ pb_data->obj = obj;
+ pb_data->params = params;
book = e_book_new_default_addressbook(NULL);
/* All the vCards shall be returned if SearchValue header is
* not specified */
- if (!searchval || !strlen((char *)searchval)) {
+ if (!params.searchval || !strlen((char *) params.searchval)) {
query = e_book_query_any_field_contains("");
goto done;
}
- if (searchattrib == 0) {
- value_list = g_strsplit((gchar *)searchval, ";", 5);
+ if (params.searchattrib == 0) {
+ value_list = g_strsplit((gchar *) params.searchval, ";", 5);
if (value_list[0])
str1 = g_strdup_printf(QUERY_FAMILY_NAME,
else
query = query1;
} else {
- str1 = g_strdup_printf(QUERY_PHONE, searchval);
- query = e_book_query_from_string((const char *)searchval);
+ str1 = g_strdup_printf(QUERY_PHONE, params.searchval);
+ query = e_book_query_from_string((char *) params.searchval);
}
done:
e_book_query_unref(query);
g_strfreev(value_list);
+ OBEX_SuspendRequest(obex, obj);
+
return 0;
}
gpointer user_data)
{
struct phonebook_data *pb_data = user_data;
- struct phonebook_context *context = pb_data->context;
- guint64 filter = pb_data->filter;
- guint8 format = pb_data->format;
- guint16 index = pb_data->index, i = 0;
+ struct apparam_field *params = &pb_data->params;
+ struct obex_session *session = OBEX_GetUserData(pb_data->obex);
+ guint16 i = 0, index;
GList *contacts = list, *attrib_list = NULL, *l;
EContact *contact = NULL;
EVCard *evcard = NULL, *evcard_filtered = NULL;
- gint32 str_len = 0;
+ gint32 size = 0;
char *vcard = NULL;
- if (filter != 0 && format == EVC_FORMAT_VCARD_30)
- filter = filter | 0x87;
+ if (params->filter != 0 && params->format == EVC_FORMAT_VCARD_30)
+ params->filter |= 0x87;
+
+ sscanf(session->name, "%hu.vcf", &index);
for (; contacts != NULL; contacts = g_list_next(contacts)) {
if (i < index) {
contact = E_CONTACT(contacts->data);
evcard = E_VCARD(contact);
- if (!filter) {
- vcard = e_vcard_to_string(evcard, format);
+ if (!params->filter) {
+ vcard = e_vcard_to_string(evcard, params->format);
break;
}
int mask;
mask = 1 << i;
- if (!(filter & mask))
+ if (!(params->filter & mask))
continue;
if (g_strcmp0(vcard_attribs[i], attrib_name))
continue;
break;
}
}
- vcard = e_vcard_to_string(evcard_filtered, format);
+ vcard = e_vcard_to_string(evcard_filtered, params->format);
g_object_unref(evcard_filtered);
break;
}
- if (vcard)
- str_len = strlen(vcard);
-
- phonebook_return(context, vcard, str_len);
+ if (vcard) {
+ size = strlen(vcard);
+ session->buf = g_realloc(session->buf, session->size + size);
+ memcpy(session->buf + session->size, vcard, size);
+ session->size += size;
+ }
- if (str_len != 0)
- phonebook_return(context, NULL, 0);
+ session->finished = 1;
+ OBEX_ResumeRequest(session->obex);
g_free(vcard);
g_free(pb_data);
- phonebook_unref(context);
g_object_unref(book);
}
-static int ebook_pullvcardentry(struct phonebook_context *context,
- gchar *objname, guint64 filter, guint8 format)
+static int ebook_pullvcardentry(obex_t *obex, obex_object_t *obj,
+ struct apparam_field params)
{
struct phonebook_data *pb_data;
EBook *book;
EBookQuery *query;
- gint index;
- gchar *ptr = NULL;
-
- DBG("context %p", context);
- if (format != EVC_FORMAT_VCARD_30) {
+ if (params.format != EVC_FORMAT_VCARD_30) {
DBG("libebook does not support e_vcard_to_string_vcard_21()");
return -1;
}
- phonebook_ref(context);
-
- ptr = g_strrstr(objname, "/");
- sscanf(ptr, "/%d.vcf", &index);
pb_data = g_new0(struct phonebook_data, 1);
- pb_data->context = context;
- pb_data->filter = filter;
- pb_data->format = format;
- pb_data->index = index;
+ pb_data->obex = obex;
+ pb_data->obj = obj;
+ pb_data->params = params;
book = e_book_new_default_addressbook(NULL);
e_book_async_get_contacts(book, query, ebookpullentry_cb, pb_data);
+ OBEX_SuspendRequest(obex, obj);
+
return 0;
}
-static struct phonebook_driver ebook_driver = {
- .name = "ebook",
- .create = ebook_create,
- .destroy = ebook_destroy,
- .pullphonebook = ebook_pullphonebook,
- .pullvcardlisting = ebook_pullvcardlisting,
- .pullvcardentry = ebook_pullvcardentry,
+static int pbap_parse_apparam_header(obex_t *obex, obex_object_t *obj,
+ struct apparam_field *apparam)
+{
+ obex_headerdata_t hd;
+ guint8 hi;
+ guint32 hlen;
+
+ while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
+ void *ptr = (void *) hd.bs;
+ uint32_t len = hlen;
+
+ if (hi != OBEX_HDR_APPARAM)
+ continue;
+
+ if (hlen < APPARAM_HDR_SIZE) {
+ g_free(apparam->searchval);
+ error("PBAP pullphonebook app parameters header"
+ " is too short: %d", hlen);
+ return -1;
+ }
+
+ while (len > APPARAM_HDR_SIZE) {
+ struct apparam_hdr *hdr = ptr;
+
+ if (hdr->len > len - APPARAM_HDR_SIZE) {
+ g_free(apparam->searchval);
+ error("Unexpected PBAP pullphonebook app"
+ " length, tag %d, len %d",
+ hdr->tag, hdr->len);
+ return -1;
+ }
+
+ switch (hdr->tag) {
+ case ORDER_TAG:
+ if (hdr->len == ORDER_LEN)
+ apparam->order = hdr->val[0];
+ break;
+ case SEARCHATTRIB_TAG:
+ if (hdr->len == SEARCHATTRIB_LEN)
+ apparam->searchattrib = hdr->val[0];
+ break;
+ case SEARCHVALUE_TAG:
+ apparam->searchval = g_try_malloc(hdr->len + 1);
+ if (apparam->searchval != NULL) {
+ memcpy(apparam->searchval, hdr->val,
+ hdr->len);
+ apparam->searchval[hdr->len] = '\0';
+ }
+ break;
+ case FILTER_TAG:
+ if (hdr->len == FILTER_LEN) {
+ guint64 val;
+ memcpy(&val, hdr->val, sizeof(val));
+ apparam->filter = get_be64(&val);
+ }
+ break;
+ case FORMAT_TAG:
+ if (hdr->len == FORMAT_LEN)
+ apparam->format = hdr->val[0];
+ break;
+ case MAXLISTCOUNT_TAG:
+ if (hdr->len == MAXLISTCOUNT_LEN) {
+ guint16 val;
+ memcpy(&val, hdr->val, sizeof(val));
+ apparam->maxlistcount = get_be16(&val);
+ }
+ break;
+ case LISTSTARTOFFSET_TAG:
+ if (hdr->len == LISTSTARTOFFSET_LEN) {
+ guint16 val;
+ memcpy(&val, hdr->val, sizeof(val));
+ apparam->liststartoffset = get_be16(&val);
+ }
+ break;
+ default:
+ g_free(apparam->searchval);
+ error("Unexpected PBAP pullphonebook app"
+ " parameter, tag %d, len %d",
+ hdr->tag, hdr->len);
+ return -1;
+ }
+
+ ptr += APPARAM_HDR_SIZE + hdr->len;
+ len -= APPARAM_HDR_SIZE + hdr->len;
+ }
+
+ /* Ignore multiple app param headers */
+ break;
+ }
+
+ return 0;
+}
+
+/* Add app parameter header, that is sent back to PBAP client */
+static int pbap_add_result_apparam_header(obex_t *obex, obex_object_t *obj,
+ guint16 maxlistcount, gchar *path_name,
+ guint16 phonebooksize,
+ guint8 newmissedcalls, gboolean *addbody)
+{
+ guint8 rspsize = 0;
+ gboolean addmissedcalls = FALSE;
+ obex_headerdata_t hd;
+
+ if (maxlistcount == 0) {
+ rspsize += APPARAM_HDR_SIZE + PHONEBOOKSIZE_LEN;
+ *addbody = FALSE;
+ }
+
+ if (g_str_equal(path_name, SIM1_MCH) == TRUE ||
+ g_str_equal(path_name, MCH) == TRUE) {
+ rspsize += APPARAM_HDR_SIZE + NEWMISSEDCALLS_LEN;
+ addmissedcalls = TRUE;
+ }
+
+ if (rspsize > 0) {
+ void *buf, *ptr;
+
+ buf = g_try_malloc0(rspsize);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ ptr = buf;
+
+ if (maxlistcount == 0) {
+ struct apparam_hdr *hdr = ptr;
+ guint16 val = GUINT16_TO_BE(phonebooksize);
+
+ hdr->tag = PHONEBOOKSIZE_TAG;
+ hdr->len = PHONEBOOKSIZE_LEN;
+ memcpy(hdr->val, &val, sizeof(val));
+
+ ptr += APPARAM_HDR_SIZE + PHONEBOOKSIZE_LEN;
+ }
+
+ if (addmissedcalls == TRUE) {
+ struct apparam_hdr *hdr = ptr;
+
+ hdr->tag = NEWMISSEDCALLS_TAG;
+ hdr->len = NEWMISSEDCALLS_LEN;
+ hdr->val[0] = newmissedcalls;
+
+ ptr += APPARAM_HDR_SIZE + NEWMISSEDCALLS_LEN;
+ }
+
+ hd.bs = buf;
+ OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_APPARAM,
+ hd, rspsize, 0);
+ g_free(buf);
+ }
+
+ return 0;
+}
+
+static int pbap_pullphonebook(obex_t *obex, obex_object_t *obj,
+ gboolean *addbody)
+{
+ struct obex_session *session = OBEX_GetUserData(obex);
+ struct apparam_field params;
+ guint8 newmissedcalls = 0;
+ guint16 phonebooksize = 0;
+ int err;
+
+ memset(¶ms, 0, sizeof(struct apparam_field));
+
+ err = pbap_parse_apparam_header(obex, obj, ¶ms);
+ if (err < 0)
+ return err;
+
+ if (params.maxlistcount == 0) {
+ phonebooksize = DEFAULT_COUNT;
+ goto done;
+ }
+
+ err = ebook_pullphonebook(obex, obj, params);
+ if (err < 0)
+ return err;
+
+done:
+ return pbap_add_result_apparam_header(obex, obj, params.maxlistcount,
+ session->name, phonebooksize,
+ newmissedcalls, addbody);
+}
+
+static int pbap_pullvcardlisting(obex_t *obex, obex_object_t *obj,
+ gboolean *addbody)
+{
+ struct obex_session *session = OBEX_GetUserData(obex);
+ gchar *fullname;
+ struct apparam_field params;
+ guint8 newmissedcalls = 0;
+ guint16 phonebooksize = 0;
+ int err;
+
+ memset(¶ms, 0, sizeof(struct apparam_field));
+
+ err = pbap_parse_apparam_header(obex, obj, ¶ms);
+ if (err < 0)
+ return err;
+
+ if (params.maxlistcount == 0) {
+ phonebooksize = DEFAULT_COUNT;
+ goto proceed;
+ }
+
+ /* libebook does not support sound attribute */
+ if (params.searchattrib >= 2) {
+ DBG("libebook does not support sound attribute");
+ goto done;
+ }
+
+ err = ebook_pullvcardlisting(obex, obj, params);
+ if (err < 0)
+ goto done;
+
+proceed:
+
+ fullname = g_build_filename(session->current_folder, session->name,
+ NULL);
+ if (fullname != NULL)
+ fullname = g_strconcat(fullname, ".vcf", NULL);
+
+ err = pbap_add_result_apparam_header(obex, obj, params.maxlistcount,
+ fullname, phonebooksize,
+ newmissedcalls, addbody);
+ g_free(fullname);
+
+done:
+ g_free(params.searchval);
+ return err;
+}
+
+static int pbap_pullvcardentry(obex_t *obex, obex_object_t *obj)
+{
+ struct apparam_field params;
+ int err;
+
+ memset(¶ms, 0, sizeof(struct apparam_field));
+ err = pbap_parse_apparam_header(obex, obj, ¶ms);
+ if (err < 0)
+ return err;
+
+ err = ebook_pullvcardentry(obex, obj, params);
+
+ g_free(params.searchval);
+ return err;
+}
+
+static void pbap_get(obex_t *obex, obex_object_t *obj)
+{
+ struct obex_session *session = OBEX_GetUserData(obex);
+ obex_headerdata_t hd;
+ gboolean addbody = TRUE;
+ int err;
+
+ if (session == NULL)
+ return;
+
+ if (session->type == NULL)
+ goto fail;
+
+ if (g_str_equal(session->type, VCARDLISTING_TYPE) == FALSE
+ && session->name == NULL)
+ goto fail;
+
+ OBEX_ObjectReParseHeaders(obex, obj);
+
+ if (g_str_equal(session->type, PHONEBOOK_TYPE) == TRUE)
+ err = pbap_pullphonebook(obex, obj, &addbody);
+ else if (g_str_equal(session->type, VCARDLISTING_TYPE) == TRUE)
+ err = pbap_pullvcardlisting(obex, obj, &addbody);
+ else if (g_str_equal(session->type, VCARDENTRY_TYPE) == TRUE)
+ err = pbap_pullvcardentry(obex, obj);
+ else
+ goto fail;
+
+ if (err < 0)
+ goto fail;
+
+ if (addbody == TRUE) {
+ OBEX_SuspendRequest(obex, obj);
+ session->size = 0;
+
+ /* Add body header */
+ hd.bs = NULL;
+ OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_BODY,
+ hd, 0, OBEX_FL_STREAM_START);
+ }
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
+
+ return;
+
+fail:
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+}
+
+static gboolean pbap_is_valid_folder(struct obex_session *session)
+{
+ if (session->current_folder == NULL) {
+ if (g_str_equal(session->name, "telecom") == TRUE ||
+ g_str_equal(session->name, "SIM1") == TRUE)
+ return TRUE;
+ } else if (g_str_equal(session->current_folder, "SIM1") == TRUE) {
+ if (g_str_equal(session->name, "telecom") == TRUE)
+ return TRUE;
+ } else if (g_str_equal(session->current_folder, "telecom") == TRUE ||
+ g_str_equal(session->current_folder, "SIM1/telecom") == TRUE) {
+ if (g_str_equal(session->name, "pb") == TRUE ||
+ g_str_equal(session->name, "ich") == TRUE ||
+ g_str_equal(session->name, "och") == TRUE ||
+ g_str_equal(session->name, "mch") == TRUE ||
+ g_str_equal(session->name, "cch") == TRUE)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void pbap_setpath(obex_t *obex, obex_object_t *obj)
+{
+ struct obex_session *session = OBEX_GetUserData(obex);
+ guint8 *nonhdr;
+ gchar *fullname;
+
+ if (OBEX_ObjectGetNonHdrData(obj, &nonhdr) != 2) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE,
+ OBEX_RSP_PRECONDITION_FAILED);
+ error("Set path failed: flag and constants not found!");
+ return;
+ }
+
+ /* Check "Backup" flag */
+ if ((nonhdr[0] & 0x01) == 0x01) {
+ debug("Set to parent path");
+
+ if (session->current_folder == NULL) {
+ /* we are already in top level folder */
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN,
+ OBEX_RSP_FORBIDDEN);
+ return;
+ }
+
+ fullname = g_path_get_dirname(session->current_folder);
+ g_free(session->current_folder);
+
+ if (strlen(fullname) == 1 && *fullname == '.')
+ session->current_folder = NULL;
+ else
+ session->current_folder = g_strdup(fullname);
+
+ g_free(fullname);
+
+ debug("Set to parent path: %s", session->current_folder);
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+ return;
+ }
+
+ if (!session->name) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_BAD_REQUEST);
+ error("Set path failed: name missing!");
+ return;
+ }
+
+ if (strlen(session->name) == 0) {
+ debug("Set to root");
+
+ g_free(session->current_folder);
+ session->current_folder = NULL;
+
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+ return;
+ }
+
+ /* Check and set to name path */
+ if (strstr(session->name, "/")) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
+ error("Set path failed: name incorrect!");
+ return;
+ }
+
+ if (pbap_is_valid_folder(session) == FALSE) {
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_FOUND, OBEX_RSP_NOT_FOUND);
+ return;
+ }
+
+ if (session->current_folder == NULL)
+ fullname = g_build_filename("", session->name, NULL);
+ else
+ fullname = g_build_filename(session->current_folder, session->name, NULL);
+
+ debug("Fullname: %s", fullname);
+
+ g_free(session->current_folder);
+ session->current_folder = fullname;
+ OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
+}
+
+struct obex_service_driver driver = {
+ .name = "Phonebook Access server",
+ .service = OBEX_PBAP,
+ .channel = PBAP_CHANNEL,
+ .record = PBAP_RECORD,
+ .target = PBAP_TARGET,
+ .get = pbap_get,
+ .setpath = pbap_setpath
};
static int ebook_init(void)
{
- return phonebook_driver_register(&ebook_driver);
+ return obex_service_driver_register(&driver);
}
static void ebook_exit(void)
{
- phonebook_driver_unregister(&ebook_driver);
+ obex_service_driver_unregister(&driver);
}
OBEX_PLUGIN_DEFINE("ebook", ebook_init, ebook_exit)
diff --git a/obexd/plugins/filesystem.c b/obexd/plugins/filesystem.c
index e275bf2..4a06c98 100644
--- a/obexd/plugins/filesystem.c
+++ b/obexd/plugins/filesystem.c
#include "logging.h"
#include "mimetype.h"
#include "obex.h"
+#include "service.h"
#define EOL_CHARS "\n"
if (os->finished)
return 0;
- pcsuite = os->server->services & OBEX_PCSUITE ? TRUE : FALSE;
+ pcsuite = os->service->service & OBEX_PCSUITE ? TRUE : FALSE;
listing = g_string_new(FL_VERSION);
listing = g_string_append(listing, pcsuite ? FL_TYPE_PCSUITE : FL_TYPE);
diff --git a/obexd/src/ftp.c b/obexd/plugins/ftp.c
similarity index 56%
rename from obexd/src/ftp.c
rename to obexd/plugins/ftp.c
index ffbcf73..82f16f2 100644
--- a/obexd/src/ftp.c
+++ b/obexd/plugins/ftp.c
#include <openobex/obex.h>
#include <openobex/obex_const.h>
+#include "plugin.h"
#include "logging.h"
#include "obex.h"
#include "dbus.h"
#include "mimetype.h"
+#include "service.h"
#define LST_TYPE "x-obex/folder-listing"
#define CAP_TYPE "x-obex/capability"
+#define FTP_CHANNEL 10
+#define FTP_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+<record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"0x1106\"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0003\"/> \
+ <uint8 value=\"%u\" name=\"channel\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0008\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x1106\"/> \
+ <uint16 value=\"0x0100\" name=\"version\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0100\"> \
+ <text value=\"%s\" name=\"name\"/> \
+ </attribute> \
+</record>"
+
+#define PCSUITE_CHANNEL 24
+#define PCSUITE_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+<record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"00005005-0000-1000-8000-0002ee000001\"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0003\"/> \
+ <uint8 value=\"%u\" name=\"channel\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0008\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"00005005-0000-1000-8000-0002ee000001\"/> \
+ <uint16 value=\"0x0100\" name=\"version\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0100\"> \
+ <text value=\"%s\" name=\"name\"/> \
+ </attribute> \
+</record>"
+
+static const guint8 FTP_TARGET[TARGET_SIZE] = {
+ 0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2,
+ 0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 };
+
static gint folder_listing(struct obex_session *os, size_t *size)
{
return os_prepare_get(os, os->current_folder, size);
return os_prepare_get(os, file, size);
}
-void ftp_get(obex_t *obex, obex_object_t *obj)
+static void ftp_get(obex_t *obex, obex_object_t *obj)
{
obex_headerdata_t hv;
struct obex_session *os;
return ret;
}
-gint ftp_chkput(obex_t *obex, obex_object_t *obj)
+static gint ftp_chkput(obex_t *obex, obex_object_t *obj)
{
struct obex_session *os;
return os_prepare_put(os);
}
-void ftp_put(obex_t *obex, obex_object_t *obj)
+static void ftp_put(obex_t *obex, obex_object_t *obj)
{
struct obex_session *os;
int ret = 0;
}
}
-void ftp_setpath(obex_t *obex, obex_object_t *obj)
+static void ftp_setpath(obex_t *obex, obex_object_t *obj)
{
struct obex_session *os;
guint8 *nonhdr;
done:
g_free(fullname);
}
+
+struct obex_service_driver pcsuite = {
+ .name = "Nokia OBEX PC Suite Services",
+ .service = OBEX_PCSUITE,
+ .channel = PCSUITE_CHANNEL,
+ .record = PCSUITE_RECORD,
+ .target = FTP_TARGET,
+ .get = ftp_get,
+ .put = ftp_put,
+ .chkput = ftp_chkput,
+ .setpath = ftp_setpath
+};
+
+struct obex_service_driver ftp = {
+ .name = "File Transfer server",
+ .service = OBEX_FTP,
+ .channel = FTP_CHANNEL,
+ .record = FTP_RECORD,
+ .target = FTP_TARGET,
+ .get = ftp_get,
+ .put = ftp_put,
+ .chkput = ftp_chkput,
+ .setpath = ftp_setpath
+};
+
+static int ftp_init(void)
+{
+ int err;
+
+ err = obex_service_driver_register(&ftp);
+ if (err < 0)
+ return err;
+
+ return obex_service_driver_register(&pcsuite);
+}
+
+static void ftp_exit(void)
+{
+ obex_service_driver_unregister(&ftp);
+ obex_service_driver_unregister(&pcsuite);
+}
+
+OBEX_PLUGIN_DEFINE("ftp", ftp_init, ftp_exit)
diff --git a/obexd/src/opp.c b/obexd/plugins/opp.c
similarity index 63%
rename from obexd/src/opp.c
rename to obexd/plugins/opp.c
index a8dce13..7d5a735 100644
--- a/obexd/src/opp.c
+++ b/obexd/plugins/opp.c
#include <glib.h>
+#include "plugin.h"
+#include "service.h"
#include "logging.h"
#include "obex.h"
#include "dbus.h"
#define VCARD_TYPE "text/x-vcard"
#define VCARD_FILE CONFIGDIR "/vcard.vcf"
-gint opp_chkput(obex_t *obex, obex_object_t *obj)
+#define OPP_CHANNEL 9
+#define OPP_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+<record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"0x1105\"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0003\"/> \
+ <uint8 value=\"%u\" name=\"channel\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0008\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x1105\"/> \
+ <uint16 value=\"0x0100\" name=\"version\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0100\"> \
+ <text value=\"%s\" name=\"name\"/> \
+ </attribute> \
+ \
+ <attribute id=\"0x0303\"> \
+ <sequence> \
+ <uint8 value=\"0x01\"/> \
+ <uint8 value=\"0x01\"/> \
+ <uint8 value=\"0x02\"/> \
+ <uint8 value=\"0x03\"/> \
+ <uint8 value=\"0x04\"/> \
+ <uint8 value=\"0x05\"/> \
+ <uint8 value=\"0x06\"/> \
+ <uint8 value=\"0xff\"/> \
+ </sequence> \
+ </attribute> \
+</record>"
+
+static gint opp_chkput(obex_t *obex, obex_object_t *obj)
{
struct obex_session *os;
gchar *new_folder, *new_name;
return os_prepare_put(os);
}
-void opp_put(obex_t *obex, obex_object_t *obj)
+static void opp_put(obex_t *obex, obex_object_t *obj)
{
struct obex_session *os;
OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
}
-void opp_get(obex_t *obex, obex_object_t *obj)
+static void opp_get(obex_t *obex, obex_object_t *obj)
{
struct obex_session *os;
obex_headerdata_t hv;
fail:
OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
}
+
+struct obex_service_driver driver = {
+ .name = "Object Push server",
+ .service = OBEX_OPP,
+ .channel = OPP_CHANNEL,
+ .record = OPP_RECORD,
+ .get = opp_get,
+ .put = opp_put,
+ .chkput = opp_chkput,
+};
+
+static int opp_init(void)
+{
+ return obex_service_driver_register(&driver);
+}
+
+static void opp_exit(void)
+{
+ obex_service_driver_unregister(&driver);
+}
+
+OBEX_PLUGIN_DEFINE("opp", opp_init, opp_exit)
diff --git a/obexd/src/bluetooth.c b/obexd/src/bluetooth.c
index 9cf2acf..7cbfda1 100644
--- a/obexd/src/bluetooth.c
+++ b/obexd/src/bluetooth.c
#include "obex.h"
#include "dbus.h"
#include "btio.h"
+#include "service.h"
#define BT_RX_MTU 32767
#define BT_TX_MTU 32767
static void confirm_event(GIOChannel *io, gpointer user_data)
{
struct server *server = user_data;
+ struct obex_service_driver *driver;
GError *err = NULL;
char address[18];
guint8 channel;
info("New connection from: %s, channel %u", address, channel);
- if (server->services != OBEX_OPP) {
+ driver = (struct obex_service_driver *) server->drivers->data;
+
+ if (driver->service != OBEX_OPP) {
if (request_service_authorization(server, io, address) < 0)
goto drop;
static gint server_start(struct server *server)
{
GError *err = NULL;
+ struct obex_service_driver *driver;
+
+ driver = (struct obex_service_driver *) server->drivers->data;
/* Listen */
if (server->secure)
server->io = bt_io_listen(BT_IO_RFCOMM, NULL, confirm_event,
server, NULL, &err,
- BT_IO_OPT_CHANNEL, server->channel,
+ BT_IO_OPT_CHANNEL, driver->channel,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_INVALID);
else
server->io = bt_io_listen(BT_IO_RFCOMM, NULL, confirm_event,
server, NULL, &err,
- BT_IO_OPT_CHANNEL, server->channel,
+ BT_IO_OPT_CHANNEL, driver->channel,
BT_IO_OPT_INVALID);
if (!server->io)
goto failed;
return 0;
}
-static gint server_register(guint16 service, const gchar *name, guint8 channel,
- const gchar *folder, gboolean secure,
- gboolean auto_accept, gboolean symlinks,
- const gchar *capability)
+static gint server_register(guint16 service, const gchar *folder,
+ gboolean secure, gboolean auto_accept,
+ gboolean symlinks, const gchar *capability)
{
struct server *server;
+ GSList *drivers;
+
+ drivers = obex_service_driver_list(service);
+ if (drivers == NULL)
+ return -EINVAL;
server = g_new0(struct server, 1);
- server->services = service;
- server->name = g_strdup(name);
+ server->drivers = drivers;
server->folder = g_strdup(folder);
server->auto_accept = auto_accept;
server->symlinks = symlinks;
server->capability = g_strdup(capability);
- server->channel = channel;
server->secure = secure;
server->rx_mtu = BT_RX_MTU;
server->tx_mtu = BT_TX_MTU;
return 0;
}
-gint bluetooth_init(guint service, const gchar *name, const gchar *folder,
- guint8 channel, gboolean secure,
+gint bluetooth_init(guint service, const gchar *folder, gboolean secure,
gboolean auto_accept, gboolean symlinks,
const gchar *capability)
{
- return server_register(service, name, channel, folder,
- secure, auto_accept, symlinks,
+ return server_register(service, folder, secure, auto_accept, symlinks,
capability);
}
diff --git a/obexd/src/bluetooth.h b/obexd/src/bluetooth.h
index 7871177..2c16bd9 100644
--- a/obexd/src/bluetooth.h
+++ b/obexd/src/bluetooth.h
#include <config.h>
#endif
-gint bluetooth_init(guint service, const gchar *name, const gchar *folder,
- guint8 channel, gboolean secure,
+gint bluetooth_init(guint service, const gchar *folder, gboolean secure,
gboolean auto_accept, gboolean symlinks,
const gchar *capability);
void bluetooth_exit(void);
diff --git a/obexd/src/main.c b/obexd/src/main.c
index abd2bcc..dcf13d1 100644
--- a/obexd/src/main.c
+++ b/obexd/src/main.c
#include "bluetooth.h"
#include "obexd.h"
#include "obex.h"
+#include "service.h"
#define OPP_CHANNEL 9
#define FTP_CHANNEL 10
}
server = g_new0(struct server, 1);
- server->services = services;
+ server->drivers = obex_service_driver_list(services);
server->folder = g_strdup(root_path);
server->auto_accept = TRUE;
server->capability = g_strdup(capability);
if (option_opp == TRUE) {
services |= OBEX_OPP;
- bluetooth_init(OBEX_OPP, "Object Push server", option_root,
- OPP_CHANNEL, FALSE, option_autoaccept,
- option_symlinks, NULL);
+ bluetooth_init(OBEX_OPP, option_root, FALSE,
+ option_autoaccept, option_symlinks,
+ NULL);
}
if (option_ftp == TRUE) {
services |= OBEX_FTP;
- bluetooth_init(OBEX_FTP, "File Transfer server", option_root,
- FTP_CHANNEL, TRUE, option_autoaccept,
- option_symlinks, option_capability);
+ bluetooth_init(OBEX_FTP, option_root, TRUE,
+ option_autoaccept, option_symlinks,
+ option_capability);
}
if (option_pbap == TRUE) {
services |= OBEX_PBAP;
- bluetooth_init(OBEX_PBAP, "Phonebook Access server", NULL,
- PBAP_CHANNEL, TRUE, FALSE, FALSE, NULL);
+ bluetooth_init(OBEX_PBAP, NULL, TRUE, FALSE, FALSE, NULL);
}
if (option_pcsuite == TRUE) {
services |= OBEX_PCSUITE;
- bluetooth_init(OBEX_PCSUITE, "Nokia OBEX PC Suite Services",
- option_root, PCSUITE_CHANNEL, TRUE,
+ bluetooth_init(OBEX_PCSUITE, option_root, TRUE,
option_autoaccept, option_symlinks,
option_capability);
}
diff --git a/obexd/src/manager.c b/obexd/src/manager.c
index 95b8701..7ac0eb8 100644
--- a/obexd/src/manager.c
+++ b/obexd/src/manager.c
#include "dbus.h"
#include "logging.h"
#include "btio.h"
-
-static const gchar *opp_record = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
-<record> \
- <attribute id=\"0x0001\"> \
- <sequence> \
- <uuid value=\"0x1105\"/> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0004\"> \
- <sequence> \
- <sequence> \
- <uuid value=\"0x0100\"/> \
- </sequence> \
- <sequence> \
- <uuid value=\"0x0003\"/> \
- <uint8 value=\"%u\" name=\"channel\"/> \
- </sequence> \
- <sequence> \
- <uuid value=\"0x0008\"/> \
- </sequence> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0009\"> \
- <sequence> \
- <sequence> \
- <uuid value=\"0x1105\"/> \
- <uint16 value=\"0x0100\" name=\"version\"/> \
- </sequence> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0100\"> \
- <text value=\"%s\" name=\"name\"/> \
- </attribute> \
- \
- <attribute id=\"0x0303\"> \
- <sequence> \
- <uint8 value=\"0x01\"/> \
- <uint8 value=\"0x01\"/> \
- <uint8 value=\"0x02\"/> \
- <uint8 value=\"0x03\"/> \
- <uint8 value=\"0x04\"/> \
- <uint8 value=\"0x05\"/> \
- <uint8 value=\"0x06\"/> \
- <uint8 value=\"0xff\"/> \
- </sequence> \
- </attribute> \
-</record>";
-
-static const gchar *ftp_record = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
-<record> \
- <attribute id=\"0x0001\"> \
- <sequence> \
- <uuid value=\"0x1106\"/> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0004\"> \
- <sequence> \
- <sequence> \
- <uuid value=\"0x0100\"/> \
- </sequence> \
- <sequence> \
- <uuid value=\"0x0003\"/> \
- <uint8 value=\"%u\" name=\"channel\"/> \
- </sequence> \
- <sequence> \
- <uuid value=\"0x0008\"/> \
- </sequence> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0009\"> \
- <sequence> \
- <sequence> \
- <uuid value=\"0x1106\"/> \
- <uint16 value=\"0x0100\" name=\"version\"/> \
- </sequence> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0100\"> \
- <text value=\"%s\" name=\"name\"/> \
- </attribute> \
-</record>";
-
-static const gchar *pbap_record = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
-<record> \
- <attribute id=\"0x0001\"> \
- <sequence> \
- <uuid value=\"0x112f\"/> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0004\"> \
- <sequence> \
- <sequence> \
- <uuid value=\"0x0100\"/> \
- </sequence> \
- <sequence> \
- <uuid value=\"0x0003\"/> \
- <uint8 value=\"%u\" name=\"channel\"/> \
- </sequence> \
- <sequence> \
- <uuid value=\"0x0008\"/> \
- </sequence> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0009\"> \
- <sequence> \
- <sequence> \
- <uuid value=\"0x1130\"/> \
- <uint16 value=\"0x0100\" name=\"version\"/> \
- </sequence> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0100\"> \
- <text value=\"%s\" name=\"name\"/> \
- </attribute> \
- \
- <attribute id=\"0x0314\"> \
- <uint8 value=\"0x01\"/> \
- </attribute> \
-</record>";
-
-static const gchar *pcsuite_record =
-"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
-<record> \
- <attribute id=\"0x0001\"> \
- <sequence> \
- <uuid value=\"00005005-0000-1000-8000-0002ee000001\"/> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0004\"> \
- <sequence> \
- <sequence> \
- <uuid value=\"0x0100\"/> \
- </sequence> \
- <sequence> \
- <uuid value=\"0x0003\"/> \
- <uint8 value=\"%u\" name=\"channel\"/> \
- </sequence> \
- <sequence> \
- <uuid value=\"0x0008\"/> \
- </sequence> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0005\"> \
- <sequence> \
- <uuid value=\"0x1002\"/> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0009\"> \
- <sequence> \
- <sequence> \
- <uuid value=\"00005005-0000-1000-8000-0002ee000001\"/> \
- <uint16 value=\"0x0100\" name=\"version\"/> \
- </sequence> \
- </sequence> \
- </attribute> \
- \
- <attribute id=\"0x0100\"> \
- <text value=\"%s\" name=\"name\"/> \
- </attribute> \
-</record>";
+#include "service.h"
#define TRANSFER_INTERFACE OPENOBEX_SERVICE ".Transfer"
#define SESSION_INTERFACE OPENOBEX_SERVICE ".Session"
return dbus_message_new_method_return(msg);
}
+static char *target2str(const uint8_t *t)
+{
+ if (!t)
+ return NULL;
+
+ return g_strdup_printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-"
+ "%02X%02X-%02X%02X%02X%02X%02X%02X",
+ t[0], t[1], t[2], t[3], t[4], t[5], t[6],t[7],
+ t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15]);
+}
+
static DBusMessage *get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
DBusMessage *reply;
DBusMessageIter iter;
DBusMessageIter dict;
- gchar uuid[37];
- const gchar *ptr = uuid;
- const uint8_t *t = os->target;
+ gchar *uuid;
reply = dbus_message_new_method_return(msg);
if (!reply)
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
/* Target */
- sprintf(uuid, "%02X%02X%02X%02X-%02X%02X-%02X%02X-"
- "%02X%02X-%02X%02X%02X%02X%02X%02X",
- t[0], t[1], t[2], t[3], t[4], t[5], t[6],t[7],
- t[8], t[9], t[10], t[11], t[12], t[13], t[14], t[15]);
+ uuid = target2str(os->service->target);
dbus_message_iter_append_dict_entry(&dict, "Target",
- DBUS_TYPE_STRING, &ptr);
+ DBUS_TYPE_STRING, &uuid);
+ g_free(uuid);
+
/* Root folder */
dbus_message_iter_append_dict_entry(&dict, "Root",
DBUS_TYPE_STRING, &os->server->folder);
{ }
};
-static gchar *create_xml_record(const char *name,
- uint16_t service, uint8_t channel)
-{
- gchar *xml;
-
- switch (service) {
- case OBEX_OPP:
- xml = g_markup_printf_escaped(opp_record, channel, name);
- break;
- case OBEX_FTP:
- xml = g_markup_printf_escaped(ftp_record, channel, name);
- break;
- case OBEX_PBAP:
- xml = g_markup_printf_escaped(pbap_record, channel, name);
- break;
- case OBEX_PCSUITE:
- xml = g_markup_printf_escaped(pcsuite_record, channel, name);
- break;
- default:
- xml = NULL;
- break;
- }
-
- return xml;
-}
-
static void add_record_reply(DBusPendingCall *call, gpointer user_data)
{
struct server *server = user_data;
dbus_error_free(&derr);
handle = 0;
} else {
+ struct obex_service_driver *driver;
+
dbus_message_get_args(reply, NULL,
DBUS_TYPE_UINT32, &handle,
DBUS_TYPE_INVALID);
server->handle = handle;
+ driver = (struct obex_service_driver *) server->drivers->data;
+
debug("Registered: %s, handle: 0x%x, folder: %s",
- server->name, handle, server->folder);
+ driver->name, handle, server->folder);
}
dbus_message_unref(reply);
void register_record(struct server *server, gpointer user_data)
{
+ struct obex_service_driver *driver;
gchar *xml;
gint ret;
return;
}
- xml = create_xml_record(server->name, server->services, server->channel);
+ driver = (struct obex_service_driver *) server->drivers->data;
+ xml = g_markup_printf_escaped(driver->record, driver->channel,
+ driver->name);
ret = add_record(any->path, xml, server);
g_free(xml);
}
static void find_adapter_any_reply(DBusPendingCall *call, gpointer user_data)
{
DBusMessage *reply = dbus_pending_call_steal_reply(call);
- struct server *server;
const char *path;
gchar *xml;
GSList *l;
any->path = g_strdup(path);
for (l = any->servers; l; l = l->next) {
- server = l->data;
- xml = create_xml_record(server->name,
- server->services, server->channel);
+ struct server *server = l->data;
+ struct obex_service_driver *driver;
+
+ driver = (struct obex_service_driver *) server->drivers->data;
+ xml = g_markup_printf_escaped(driver->record, driver->channel,
+ driver->name);
add_record(path, xml, server);
g_free(xml);
}
g_free(path);
}
+
+
diff --git a/obexd/src/obex.c b/obexd/src/obex.c
index 212c369..9c5cec6 100644
--- a/obexd/src/obex.c
+++ b/obexd/src/obex.c
#include "obex.h"
#include "dbus.h"
#include "mimetype.h"
+#include "service.h"
/* Default MTU's */
#define DEFAULT_RX_MTU 32767
#define DEFAULT_TX_MTU 32767
-#define TARGET_SIZE 16
-
-static const guint8 FTP_TARGET[TARGET_SIZE] = {
- 0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2,
- 0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 };
-
-static const guint8 PBAP_TARGET[TARGET_SIZE] = {
- 0x79, 0x61, 0x35, 0xF0, 0xF0, 0xC5, 0x11, 0xD8,
- 0x09, 0x66, 0x08, 0x00, 0x20, 0x0C, 0x9A, 0x66 };
-
/* Connection ID */
static guint32 cid = 0x0000;
guint16 mtu;
} __attribute__ ((packed)) obex_connect_hdr_t;
-struct obex_commands opp = {
- .get = opp_get,
- .put = opp_put,
- .chkput = opp_chkput,
-};
-
-struct obex_commands ftp = {
- .get = ftp_get,
- .put = ftp_put,
- .chkput = ftp_chkput,
- .setpath = ftp_setpath,
-};
-
-struct obex_commands pbap = {
- .get = pbap_get,
- .setpath = pbap_setpath,
-};
-
static void os_reset_session(struct obex_session *os)
{
if (os->object) {
if (os->io)
g_io_channel_unref(os->io);
- if (os->target && !memcmp(os->target, PBAP_TARGET, TARGET_SIZE))
- pbap_phonebook_context_destroy(os);
-
g_free(os);
}
if (hi != OBEX_HDR_TARGET || hlen != TARGET_SIZE)
continue;
- if (memcmp(hd.bs, FTP_TARGET, TARGET_SIZE) == 0 &&
- os->server->services &
- (OBEX_FTP | OBEX_PCSUITE)) {
- os->target = FTP_TARGET;
- os->services = OBEX_FTP | OBEX_PCSUITE;
- os->cmds = &ftp;
- break;
- }
-
- if (memcmp(hd.bs, PBAP_TARGET, TARGET_SIZE) == 0 &&
- os->server->services & OBEX_PBAP) {
- os->target = PBAP_TARGET;
- os->services = OBEX_PBAP;
- os->cmds = &pbap;
- pbap_phonebook_context_create(os);
+ os->service = obex_service_driver_find(os->server->drivers, hd.bs);
+ if (os->service)
break;
- }
error("Connect attempt to a non-supported target");
OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
return;
}
- if (os->target == NULL) {
- if (os->server->services & OBEX_OPP) {
+ if (os->service == NULL) {
+ os->service = obex_service_driver_find(os->server->drivers, NULL);
+ if (os->service) {
register_transfer(os->cid, os);
/* OPP doesn't contains target or connection id. */
OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
emit_session_created(cid);
/* Append received UUID in WHO header */
- hd.bs = os->target;
+ hd.bs = os->service->target;
OBEX_ObjectAddHeader(obex, obj,
OBEX_HDR_WHO, hd, TARGET_SIZE,
OBEX_FL_FIT_ONE_PACKET);
os = OBEX_GetUserData(obex);
/* Object Push doesn't provide a connection id. */
- if (os->target == NULL)
+ if (os->service == NULL)
return TRUE;
while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
guint hlen;
guint8 hi;
- if (!os->cmds) {
+ if (!os->service) {
OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
return;
- } else if (!os->cmds->get) {
+ } else if (!os->service->get) {
OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
OBEX_RSP_NOT_IMPLEMENTED);
return;
os->type = g_strndup((const gchar *) hd.bs, hlen);
debug("OBEX_HDR_TYPE: %s", os->type);
- os->driver = obex_mime_type_driver_find(os->target, os->type);
+ os->driver = obex_mime_type_driver_find(os->service->target, os->type);
break;
}
}
if (!os->driver) {
- os->driver = obex_mime_type_driver_find(os->target, NULL);
+ os->driver = obex_mime_type_driver_find(os->service->target, NULL);
if (!os->driver) {
error("No driver found");
OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
}
}
- os->cmds->get(obex, obj);
+ os->service->get(obex, obj);
}
static void cmd_setpath(struct obex_session *os,
guint32 hlen;
guint8 hi;
- if (!os->cmds) {
+ if (!os->service) {
OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
return;
- } else if (!os->cmds->setpath) {
+ } else if (!os->service->setpath) {
OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
OBEX_RSP_NOT_IMPLEMENTED);
return;
break;
}
- os->cmds->setpath(obex, obj);
+ os->service->setpath(obex, obj);
}
int os_prepare_get(struct obex_session *os, gchar *filename, size_t *size)
gint err;
gpointer object;
- if (!os->driver) {
- error("No driver to handle %s", os->type);
- return -ENOENT;
- }
-
object = os->driver->open(filename, O_RDONLY, 0, size);
if (object == NULL) {
err = -errno;
if (os->aborted)
return -EPERM;
-
if (os->object == NULL) {
if (os->buf == NULL && os->finished == FALSE)
return -EIO;
OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_BODY, hd, len,
OBEX_FL_STREAM_DATA);
- if (!memcmp(os->target, PBAP_TARGET, TARGET_SIZE))
- if (os->offset == os->size && os->finished == FALSE)
- OBEX_SuspendRequest(obex, obj);
-
return len;
}
g_free(path);
- if (os->target == NULL)
+ if (os->service == NULL)
emit_transfer_started(os->cid);
if (!os->buf) {
os->type = g_strndup((const gchar *) hd.bs, hlen);
debug("OBEX_HDR_TYPE: %s", os->type);
- os->driver = obex_mime_type_driver_find(os->target, os->type);
+ os->driver = obex_mime_type_driver_find(os->service->target, os->type);
break;
case OBEX_HDR_BODY:
}
if (!os->driver) {
- os->driver = obex_mime_type_driver_find(os->target, NULL);
+ os->driver = obex_mime_type_driver_find(os->service->target, NULL);
if (!os->driver) {
error("No driver found");
OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
}
}
- if (!os->cmds || !os->cmds->chkput)
+ if (!os->service || !os->service->chkput)
goto done;
- ret = os->cmds->chkput(obex, obj);
+ ret = os->service->chkput(obex, obj);
switch (ret) {
case 0:
break;
static void cmd_put(struct obex_session *os, obex_t *obex, obex_object_t *obj)
{
- if (!os->cmds) {
+ if (!os->service) {
OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
return;
- } else if (!os->cmds->put) {
+ } else if (!os->service->put) {
OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_IMPLEMENTED,
OBEX_RSP_NOT_IMPLEMENTED);
return;
return;
}
- os->cmds->put(obex, obj);
+ os->service->put(obex, obj);
}
static void obex_event(obex_t *obex, obex_object_t *obj, gint mode,
switch (evt) {
case OBEX_EV_PROGRESS:
/* Just emit progress for Object Push */
- if (os->target == NULL)
+ if (os->service == NULL)
emit_transfer_progress(os->cid, os->size, os->offset);
break;
case OBEX_EV_ABORT:
os->aborted = TRUE;
- if (os->target == NULL)
+ if (os->service == NULL)
emit_transfer_completed(os->cid, FALSE);
os_reset_session(os);
OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
case OBEX_CMD_PUT:
case OBEX_CMD_GET:
os_session_mark_aborted(os);
- if (os->target == NULL)
+ if (os->service == NULL)
emit_transfer_completed(os->cid, !os->aborted);
os_reset_session(os);
break;
case OBEX_EV_REQCHECK:
switch (cmd) {
case OBEX_CMD_PUT:
- if (os->cmds && os->cmds->put)
+ if (os->service && os->service->put)
check_put(obex, obj);
break;
default:
void server_free(struct server *server)
{
- g_free(server->name);
g_free(server->folder);
g_free(server->capability);
g_free(server->devnode);
os = OBEX_GetUserData(obex);
- if (os->target == NULL) {
+ if (os->service == NULL) {
/* Got an error during a transfer. */
if (os->object)
emit_transfer_completed(os->cid, os->offset == os->size);
static gboolean tty_reinit(gpointer data)
{
struct server *server = data;
+ GSList *l;
+ guint services = 0;
+
+ for (l = server->drivers; l; l = l->next) {
+ struct obex_service_driver *driver = l->data;
+
+ services |= driver->service;
+ }
- tty_init(server->services, server->folder, server->capability,
- server->symlinks, server->devnode);
+ tty_init(services, server->folder, server->capability,
+ server->symlinks, server->devnode);
server_free(server);
os = g_new0(struct obex_session, 1);
- os->target = NULL;
+ os->service = NULL;
- if (server->services & OBEX_OPP)
- os->cmds = &opp;
+ os->service = obex_service_driver_find(server->drivers, NULL);
os->current_folder = g_strdup(server->folder);
os->server = server;
diff --git a/obexd/src/obex.h b/obexd/src/obex.h
index 13ecf81..eb4cb1a 100644
--- a/obexd/src/obex.h
+++ b/obexd/src/obex.h
#include <glib.h>
-#include "phonebook.h"
-
#define OBJECT_SIZE_UNKNOWN -1
#define OBJECT_SIZE_DELETE -2
#define OBEX_PBAP (1 << 4)
#define OBEX_PCSUITE (1 << 5)
-struct obex_mime_type_driver;
+#define TARGET_SIZE 16
-struct obex_commands {
- void (*get) (obex_t *obex, obex_object_t *obj);
- void (*put) (obex_t *obex, obex_object_t *obj);
- gint (*chkput) (obex_t *obex, obex_object_t *obj);
- void (*setpath) (obex_t *obex, obex_object_t *obj);
-};
+struct obex_service_driver;
+struct obex_mime_type_driver;
struct server {
- guint16 services;
gboolean auto_accept;
- gchar *name;
gchar *folder;
gboolean symlinks;
gchar *capability;
guint32 handle;
- uint8_t channel;
gchar *devnode;
gboolean secure;
GIOChannel *io;
guint watch;
guint16 tx_mtu;
guint16 rx_mtu;
+ GSList *drivers;
};
struct obex_session {
GIOChannel *io;
guint32 cid;
- guint16 services;
guint16 tx_mtu;
guint16 rx_mtu;
- uint8_t cmd;
+ guint8 cmd;
gchar *name;
gchar *type;
time_t time;
gint32 size;
gpointer object;
gboolean aborted;
- const guint8 *target;
- struct obex_commands *cmds;
+ struct obex_service_driver *service;
struct server *server;
gboolean checked;
obex_t *obex;
- struct phonebook_context *pbctx;
struct obex_mime_type_driver *driver;
gboolean finished;
};
struct obex_session *obex_get_session(gpointer object);
gint obex_tty_session_stop(void);
-void opp_get(obex_t *obex, obex_object_t *obj);
-void opp_put(obex_t *obex, obex_object_t *obj);
-gint opp_chkput(obex_t *obex, obex_object_t *obj);
-
-void ftp_get(obex_t *obex, obex_object_t *obj);
-void ftp_put(obex_t *obex, obex_object_t *obj);
-gint ftp_chkput(obex_t *obex, obex_object_t *obj);
-void ftp_setpath(obex_t *obex, obex_object_t *obj);
-
-void pbap_get(obex_t *obex, obex_object_t *obj);
-void pbap_setpath(obex_t *obex, obex_object_t *obj);
-gboolean pbap_phonebook_context_create(struct obex_session *session);
-void pbap_phonebook_context_destroy(struct obex_session *session);
-struct obex_session *pbap_get_session(struct phonebook_context *context);
-
gint os_prepare_get(struct obex_session *os, gchar *file, size_t *size);
gint os_prepare_put(struct obex_session *os);
diff --git a/obexd/src/pbap.c b/obexd/src/pbap.c
deleted file mode 100644
index 5f1fb73..0000000
--- a/obexd/src/pbap.c
+++ /dev/null
-/*
- *
- * OBEX Server
- *
- * Copyright (C) 2007-2009 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <glib.h>
-#include <bluetooth/bluetooth.h>
-
-#include <openobex/obex.h>
-#include <openobex/obex_const.h>
-
-#include "logging.h"
-#include "obex.h"
-
-#define PHONEBOOK_TYPE "x-bt/phonebook"
-#define VCARDLISTING_TYPE "x-bt/vcard-listing"
-#define VCARDENTRY_TYPE "x-bt/vcard"
-
-#define ORDER_TAG 0x01
-#define SEARCHVALUE_TAG 0x02
-#define SEARCHATTRIB_TAG 0x03
-#define MAXLISTCOUNT_TAG 0x04
-#define LISTSTARTOFFSET_TAG 0x05
-#define FILTER_TAG 0x06
-#define FORMAT_TAG 0X07
-#define PHONEBOOKSIZE_TAG 0X08
-#define NEWMISSEDCALLS_TAG 0X09
-
-/* The following length is in the unit of byte */
-#define ORDER_LEN 1
-#define SEARCHATTRIB_LEN 1
-#define MAXLISTCOUNT_LEN 2
-#define LISTSTARTOFFSET_LEN 2
-#define FILTER_LEN 8
-#define FORMAT_LEN 1
-#define PHONEBOOKSIZE_LEN 2
-#define NEWMISSEDCALLS_LEN 1
-
-#define MCH "telecom/mch.vcf"
-#define SIM1_MCH "SIM1/telecom/mch.vcf"
-
-struct apparam_hdr {
- uint8_t tag;
- uint8_t len;
- uint8_t val[0];
-} __attribute__ ((packed));
-#define APPARAM_HDR_SIZE 2
-
-#define get_be64(val) GUINT64_FROM_BE(bt_get_unaligned((guint64 *) val))
-#define get_be16(val) GUINT16_FROM_BE(bt_get_unaligned((guint16 *) val))
-
-#define put_be16(val, ptr) bt_put_unaligned(GUINT16_TO_BE(val), (guint16 *) ptr)
-
-struct apparam_field {
- guint64 filter;
- guint16 maxlistcount;
- guint16 liststartoffset;
- guint8 format;
- guint8 order;
- guint8 searchattrib;
- guint8 *searchval;
-};
-
-static GSList *session_list = NULL;
-
-static int pbap_parse_apparam_header(obex_t *obex, obex_object_t *obj,
- struct apparam_field *apparam)
-{
- obex_headerdata_t hd;
- guint8 hi;
- guint32 hlen;
-
- while (OBEX_ObjectGetNextHeader(obex, obj, &hi, &hd, &hlen)) {
- void *ptr = (void *) hd.bs;
- uint32_t len = hlen;
-
- if (hi != OBEX_HDR_APPARAM)
- continue;
-
- if (hlen < APPARAM_HDR_SIZE) {
- g_free(apparam->searchval);
- error("PBAP pullphonebook app parameters header"
- " is too short: %d", hlen);
- return -1;
- }
-
- while (len > APPARAM_HDR_SIZE) {
- struct apparam_hdr *hdr = ptr;
-
- if (hdr->len > len - APPARAM_HDR_SIZE) {
- g_free(apparam->searchval);
- error("Unexpected PBAP pullphonebook app"
- " length, tag %d, len %d",
- hdr->tag, hdr->len);
- return -1;
- }
-
- switch (hdr->tag) {
- case ORDER_TAG:
- if (hdr->len == ORDER_LEN)
- apparam->order = hdr->val[0];
- break;
- case SEARCHATTRIB_TAG:
- if (hdr->len == SEARCHATTRIB_LEN)
- apparam->searchattrib = hdr->val[0];
- break;
- case SEARCHVALUE_TAG:
- apparam->searchval = g_try_malloc(hdr->len + 1);
- if (apparam->searchval != NULL) {
- memcpy(apparam->searchval, hdr->val,
- hdr->len);
- apparam->searchval[hdr->len] = '\0';
- }
- break;
- case FILTER_TAG:
- if (hdr->len == FILTER_LEN) {
- guint64 val;
- memcpy(&val, hdr->val, sizeof(val));
- apparam->filter = get_be64(&val);
- }
- break;
- case FORMAT_TAG:
- if (hdr->len == FORMAT_LEN)
- apparam->format = hdr->val[0];
- break;
- case MAXLISTCOUNT_TAG:
- if (hdr->len == MAXLISTCOUNT_LEN) {
- guint16 val;
- memcpy(&val, hdr->val, sizeof(val));
- apparam->maxlistcount = get_be16(&val);
- }
- break;
- case LISTSTARTOFFSET_TAG:
- if (hdr->len == LISTSTARTOFFSET_LEN) {
- guint16 val;
- memcpy(&val, hdr->val, sizeof(val));
- apparam->liststartoffset = get_be16(&val);
- }
- break;
- default:
- g_free(apparam->searchval);
- error("Unexpected PBAP pullphonebook app"
- " parameter, tag %d, len %d",
- hdr->tag, hdr->len);
- return -1;
- }
-
- ptr += APPARAM_HDR_SIZE + hdr->len;
- len -= APPARAM_HDR_SIZE + hdr->len;
- }
-
- /* Ignore multiple app param headers */
- break;
- }
-
- return 0;
-}
-
-/* Add app parameter header, that is sent back to PBAP client */
-static int pbap_add_result_apparam_header(obex_t *obex, obex_object_t *obj,
- guint16 maxlistcount, gchar *path_name,
- guint16 phonebooksize,
- guint8 newmissedcalls, gboolean *addbody)
-{
- guint8 rspsize = 0;
- gboolean addmissedcalls = FALSE;
- obex_headerdata_t hd;
-
- if (maxlistcount == 0) {
- rspsize += APPARAM_HDR_SIZE + PHONEBOOKSIZE_LEN;
- *addbody = FALSE;
- }
-
- if (g_str_equal(path_name, SIM1_MCH) == TRUE ||
- g_str_equal(path_name, MCH) == TRUE) {
- rspsize += APPARAM_HDR_SIZE + NEWMISSEDCALLS_LEN;
- addmissedcalls = TRUE;
- }
-
- if (rspsize > 0) {
- void *buf, *ptr;
-
- buf = g_try_malloc0(rspsize);
- if (buf == NULL)
- return -ENOMEM;
-
- ptr = buf;
-
- if (maxlistcount == 0) {
- struct apparam_hdr *hdr = ptr;
- guint16 val = GUINT16_TO_BE(phonebooksize);
-
- hdr->tag = PHONEBOOKSIZE_TAG;
- hdr->len = PHONEBOOKSIZE_LEN;
- memcpy(hdr->val, &val, sizeof(val));
-
- ptr += APPARAM_HDR_SIZE + PHONEBOOKSIZE_LEN;
- }
-
- if (addmissedcalls == TRUE) {
- struct apparam_hdr *hdr = ptr;
-
- hdr->tag = NEWMISSEDCALLS_TAG;
- hdr->len = NEWMISSEDCALLS_LEN;
- hdr->val[0] = newmissedcalls;
-
- ptr += APPARAM_HDR_SIZE + NEWMISSEDCALLS_LEN;
- }
-
- hd.bs = buf;
- OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_APPARAM,
- hd, rspsize, 0);
- g_free(buf);
- }
-
- return 0;
-}
-
-static int pbap_pullphonebook(obex_t *obex, obex_object_t *obj,
- gboolean *addbody)
-{
- struct obex_session *session = OBEX_GetUserData(obex);
- struct apparam_field apparam;
- guint8 newmissedcalls = 0;
- guint16 phonebooksize = 0;
- int err;
-
- memset(&apparam, 0, sizeof(struct apparam_field));
-
- err = pbap_parse_apparam_header(obex, obj, &apparam);
- if (err < 0)
- return err;
-
- err = phonebook_pullphonebook(session->pbctx, session->name,
- apparam.filter, apparam.format,
- apparam.maxlistcount,
- apparam.liststartoffset,
- &phonebooksize, &newmissedcalls);
- if (err < 0)
- return err;
-
- return pbap_add_result_apparam_header(obex, obj, apparam.maxlistcount,
- session->name, phonebooksize,
- newmissedcalls, addbody);
-}
-
-static int pbap_pullvcardlisting(obex_t *obex, obex_object_t *obj,
- gboolean *addbody)
-{
- struct obex_session *session = OBEX_GetUserData(obex);
- gchar *fullname;
- struct apparam_field apparam;
- guint8 newmissedcalls = 0;
- guint16 phonebooksize = 0;
- int err;
-
- memset(&apparam, 0, sizeof(struct apparam_field));
-
- err = pbap_parse_apparam_header(obex, obj, &apparam);
- if (err < 0)
- return err;
-
- fullname = g_build_filename(session->current_folder,
- session->name, NULL);
- err = phonebook_pullvcardlisting(session->pbctx, fullname,
- apparam.order, apparam.searchval,
- apparam.searchattrib,
- apparam.maxlistcount,
- apparam.liststartoffset,
- &phonebooksize, &newmissedcalls);
- if (err < 0)
- goto done;
-
- g_free(fullname);
-
- fullname = g_build_filename(session->current_folder, session->name,
- NULL);
- if (fullname != NULL)
- fullname = g_strconcat(fullname, ".vcf", NULL);
-
- err = pbap_add_result_apparam_header(obex, obj, apparam.maxlistcount,
- fullname, phonebooksize,
- newmissedcalls, addbody);
-
-done:
- g_free(apparam.searchval);
- g_free(fullname);
- return err;
-}
-
-static int pbap_pullvcardentry(obex_t *obex, obex_object_t *obj)
-{
- struct obex_session *session = OBEX_GetUserData(obex);
- gchar *fullname;
- struct apparam_field apparam;
- int err;
-
- memset(&apparam, 0, sizeof(struct apparam_field));
- err = pbap_parse_apparam_header(obex, obj, &apparam);
- if (err < 0)
- return err;
-
- fullname = g_build_filename(session->current_folder,
- session->name, NULL);
- err = phonebook_pullvcardentry(session->pbctx, fullname,
- apparam.filter, apparam.format);
-
- g_free(apparam.searchval);
- g_free(fullname);
- return err;
-}
-
-void pbap_get(obex_t *obex, obex_object_t *obj)
-{
- struct obex_session *session = OBEX_GetUserData(obex);
- obex_headerdata_t hd;
- gboolean addbody = TRUE;
- int err;
-
- if (session == NULL)
- return;
-
- if (session->type == NULL)
- goto fail;
-
- if (g_str_equal(session->type, VCARDLISTING_TYPE) == FALSE
- && session->name == NULL)
- goto fail;
-
- OBEX_ObjectReParseHeaders(obex, obj);
-
- if (g_str_equal(session->type, PHONEBOOK_TYPE) == TRUE)
- err = pbap_pullphonebook(obex, obj, &addbody);
- else if (g_str_equal(session->type, VCARDLISTING_TYPE) == TRUE)
- err = pbap_pullvcardlisting(obex, obj, &addbody);
- else if (g_str_equal(session->type, VCARDENTRY_TYPE) == TRUE)
- err = pbap_pullvcardentry(obex, obj);
- else
- goto fail;
-
- if (err < 0)
- goto fail;
-
- if (addbody == TRUE) {
- OBEX_SuspendRequest(obex, obj);
- session->size = 0;
-
- /* Add body header */
- hd.bs = NULL;
- OBEX_ObjectAddHeader(obex, obj, OBEX_HDR_BODY,
- hd, 0, OBEX_FL_STREAM_START);
- }
-
- OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS);
-
- return;
-
-fail:
- OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
-}
-
-static gboolean pbap_is_valid_folder(struct obex_session *session)
-{
- if (session->current_folder == NULL) {
- if (g_str_equal(session->name, "telecom") == TRUE ||
- g_str_equal(session->name, "SIM1") == TRUE)
- return TRUE;
- } else if (g_str_equal(session->current_folder, "SIM1") == TRUE) {
- if (g_str_equal(session->name, "telecom") == TRUE)
- return TRUE;
- } else if (g_str_equal(session->current_folder, "telecom") == TRUE ||
- g_str_equal(session->current_folder, "SIM1/telecom") == TRUE) {
- if (g_str_equal(session->name, "pb") == TRUE ||
- g_str_equal(session->name, "ich") == TRUE ||
- g_str_equal(session->name, "och") == TRUE ||
- g_str_equal(session->name, "mch") == TRUE ||
- g_str_equal(session->name, "cch") == TRUE)
- return TRUE;
- }
-
- return FALSE;
-}
-
-void pbap_setpath(obex_t *obex, obex_object_t *obj)
-{
- struct obex_session *session = OBEX_GetUserData(obex);
- guint8 *nonhdr;
- gchar *fullname;
-
- if (OBEX_ObjectGetNonHdrData(obj, &nonhdr) != 2) {
- OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE,
- OBEX_RSP_PRECONDITION_FAILED);
- error("Set path failed: flag and constants not found!");
- return;
- }
-
- /* Check "Backup" flag */
- if ((nonhdr[0] & 0x01) == 0x01) {
- debug("Set to parent path");
-
- if (session->current_folder == NULL) {
- /* we are already in top level folder */
- OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN,
- OBEX_RSP_FORBIDDEN);
- return;
- }
-
- fullname = g_path_get_dirname(session->current_folder);
- g_free(session->current_folder);
-
- if (strlen(fullname) == 1 && *fullname == '.')
- session->current_folder = NULL;
- else
- session->current_folder = g_strdup(fullname);
-
- g_free(fullname);
-
- debug("Set to parent path: %s", session->current_folder);
-
- OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
- return;
- }
-
- if (!session->name) {
- OBEX_ObjectSetRsp(obj, OBEX_RSP_CONTINUE, OBEX_RSP_BAD_REQUEST);
- error("Set path failed: name missing!");
- return;
- }
-
- if (strlen(session->name) == 0) {
- debug("Set to root");
-
- g_free(session->current_folder);
- session->current_folder = NULL;
-
- OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
- return;
- }
-
- /* Check and set to name path */
- if (strstr(session->name, "/")) {
- OBEX_ObjectSetRsp(obj, OBEX_RSP_FORBIDDEN, OBEX_RSP_FORBIDDEN);
- error("Set path failed: name incorrect!");
- return;
- }
-
- if (pbap_is_valid_folder(session) == FALSE) {
- OBEX_ObjectSetRsp(obj, OBEX_RSP_NOT_FOUND, OBEX_RSP_NOT_FOUND);
- return;
- }
-
- if (session->current_folder == NULL)
- fullname = g_build_filename("", session->name, NULL);
- else
- fullname = g_build_filename(session->current_folder, session->name, NULL);
-
- debug("Fullname: %s", fullname);
-
- g_free(session->current_folder);
- session->current_folder = fullname;
- OBEX_ObjectSetRsp(obj, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS);
-}
-
-gboolean pbap_phonebook_context_create(struct obex_session *session)
-{
- struct phonebook_context *context;
- struct phonebook_driver *driver;
-
- driver = phonebook_get_driver(NULL);
- if (driver == NULL)
- return FALSE;
-
- context = phonebook_create(driver);
- if (context == NULL)
- return FALSE;
-
- session->pbctx = context;
-
- session_list = g_slist_append(session_list, session);
-
- return TRUE;
-}
-
-void pbap_phonebook_context_destroy(struct obex_session *session)
-{
- struct phonebook_context *context;
-
- context = session->pbctx;
- phonebook_unref(context);
-
- session_list = g_slist_remove(session_list, session);
-}
-
-struct obex_session *pbap_get_session(struct phonebook_context *context)
-{
- GSList *current;
-
- for (current = session_list; current != NULL; current = current->next) {
- struct obex_session *session = current->data;
- if (session->pbctx == context)
- return session;
- }
-
- return NULL;
-}
diff --git a/obexd/src/phonebook.c b/obexd/src/phonebook.c
deleted file mode 100644
index cdbc319..0000000
--- a/obexd/src/phonebook.c
+++ /dev/null
-/*
- *
- * OBEX Server
- *
- * Copyright (C) 2007-2009 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <glib.h>
-#include <stdlib.h>
-
-#include <openobex/obex.h>
-#include <openobex/obex_const.h>
-
-#include "logging.h"
-#include "obex.h"
-
-static GSList *driver_list = NULL;
-
-int phonebook_driver_register(struct phonebook_driver *driver)
-{
- DBG("driver %p name %s", driver, driver->name);
-
- driver_list = g_slist_append(driver_list, driver);
-
- return 0;
-}
-
-void phonebook_driver_unregister(struct phonebook_driver *driver)
-{
- DBG("driver %p name %s", driver, driver->name);
-
- driver_list = g_slist_remove(driver_list, driver);
-}
-
-struct phonebook_context *phonebook_create(struct phonebook_driver *driver)
-{
- struct phonebook_context *context;
-
- if (driver == NULL)
- return NULL;
-
- context = g_try_new0(struct phonebook_context, 1);
- if (context == NULL)
- return NULL;
-
- DBG("context %p", context);
-
- context->refcount = 1;
- context->driver = driver;
-
- if (driver->create) {
- if (driver->create(context) < 0) {
- g_free(context);
- return NULL;
- }
- }
-
- return context;
-}
-
-struct phonebook_context *phonebook_ref(struct phonebook_context *context)
-{
- DBG("context %p refcount %d", context,
- g_atomic_int_get(&context->refcount) + 1);
-
- g_atomic_int_inc(&context->refcount);
-
- return context;
-}
-
-void phonebook_unref(struct phonebook_context *context)
-{
- DBG("context %p refcount %d", context,
- g_atomic_int_get(&context->refcount) - 1);
-
- if (g_atomic_int_dec_and_test(&context->refcount) == TRUE) {
- if (context->driver->destroy)
- context->driver->destroy(context);
- g_free(context);
- }
-}
-
-int phonebook_pullphonebook(struct phonebook_context *context, gchar *objname,
- guint64 filter, guint8 format, guint16 maxlistcount,
- guint16 liststartoffset, guint16 *phonebooksize,
- guint8 *newmissedcalls)
-{
- if (!context->driver->pullphonebook)
- return -1;
-
- return context->driver->pullphonebook(context, objname, filter, format,
- maxlistcount, liststartoffset, phonebooksize,
- newmissedcalls);
-}
-
-int phonebook_pullvcardlisting(struct phonebook_context *context,
- gchar *objname, guint8 order, guint8 *searchval,
- guint8 searchattrib, guint16 maxlistcount,
- guint16 liststartoffset, guint16 *phonebooksize,
- guint8 *newmissedcalls)
-{
- if (!context->driver->pullvcardlisting)
- return -1;
-
- return context->driver->pullvcardlisting(context, objname, order,
- searchval, searchattrib, maxlistcount,
- liststartoffset, phonebooksize, newmissedcalls);
-}
-
-int phonebook_pullvcardentry(struct phonebook_context *context, gchar *objname,
- guint64 filter, guint8 format)
-{
- if (!context->driver->pullvcardentry)
- return -1;
-
- return context->driver->pullvcardentry(context, objname, filter,
- format);
-}
-
-/* if buf is NULL or size is 0, this indicate that no more result will
- * be returned by PBAP plugin
- * */
-void phonebook_return(struct phonebook_context *context,
- char *buf, int size)
-{
- struct obex_session *session;
-
- DBG("context %p", context);
-
- session = pbap_get_session(context);
-
- if (buf == NULL || size == 0) {
- session->finished = 1;
- OBEX_ResumeRequest(session->obex);
- return;
- }
-
- session->buf = g_realloc(session->buf, session->size + size);
- memcpy(session->buf + session->size, buf, size);
- session->size += size;
-
- OBEX_ResumeRequest(session->obex);
-}
-
-struct phonebook_driver *phonebook_get_driver(const char *name)
-{
- DBG("name %s", name);
-
- return g_slist_nth_data(driver_list, 0);
-}
diff --git a/obexd/src/phonebook.h b/obexd/src/phonebook.h
deleted file mode 100644
index ecf5e4f..0000000
--- a/obexd/src/phonebook.h
+++ /dev/null
-/*
- *
- * OBEX Server
- *
- * Copyright (C) 2007-2009 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#include <glib.h>
-
-struct phonebook_driver;
-
-struct phonebook_context {
- gint refcount;
-
- struct phonebook_driver *driver;
- void *driver_data;
-};
-
-extern struct phonebook_context *phonebook_create(
- struct phonebook_driver *driver);
-extern struct phonebook_context *phonebook_ref(
- struct phonebook_context *context);
-extern void phonebook_unref(struct phonebook_context *context);
-
-static inline void *phonebook_get_data(struct phonebook_context *context)
-{
- return context->driver_data;
-}
-
-static inline void phonebook_set_data(struct phonebook_context *context,
- void *data)
-{
- context->driver_data = data;
-}
-
-extern int phonebook_pullphonebook(struct phonebook_context *context,
- gchar *objname, guint64 filter, guint8 format,
- guint16 maxlistcount, guint16 liststartoffset,
- guint16 *phonebooksize, guint8 *newmissedcalls);
-extern int phonebook_pullvcardlisting(struct phonebook_context *context,
- gchar *objname, guint8 order, guint8 *searchval,
- guint8 searchattrib, guint16 maxlistcount,
- guint16 liststartoffset, guint16 *phonebooksize,
- guint8 *newmissedcalls);
-extern int phonebook_pullvcardentry(struct phonebook_context *context,
- gchar *objname, guint64 filter, guint8 format);
-extern void phonebook_return(struct phonebook_context *context,
- char *buf, int size);
-
-struct phonebook_driver {
- const char *name;
- int (*create) (struct phonebook_context *context);
- void (*destroy) (struct phonebook_context *context);
- int (*pullphonebook) (struct phonebook_context *context,
- gchar *objname, guint64 filter, guint8 format,
- guint16 maxlistcount, guint16 liststartoffset,
- guint16 *phonebooksize, guint8 *newmissedcalls);
- int (*pullvcardlisting) (struct phonebook_context *context,
- gchar *objname, guint8 order, guint8 *searchval,
- guint8 searchattrib, guint16 maxlistcount,
- guint16 liststartoffset, guint16 *phonebooksize,
- guint8 *newmissedcalls);
- int (*pullvcardentry) (struct phonebook_context *context,
- gchar *objname, guint64 filter, guint8 format);
-};
-
-extern int phonebook_driver_register(struct phonebook_driver *driver);
-extern void phonebook_driver_unregister(struct phonebook_driver *driver);
-
-struct phonebook_driver *phonebook_get_driver(const char *name);
diff --git a/obexd/src/service.c b/obexd/src/service.c
new file mode 100644
index 0000000..2a5494d
--- /dev/null
+++ b/obexd/src/service.c
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2009 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+
+#include <openobex/obex.h>
+#include <openobex/obex_const.h>
+
+#include "service.h"
+#include "logging.h"
+#include "obex.h"
+
+static GSList *drivers = NULL;
+
+struct obex_service_driver *obex_service_driver_find(GSList *list, const guint8 *target)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct obex_service_driver *driver = l->data;
+
+ if (driver->target == NULL && target == NULL)
+ return driver;
+
+ if (driver->target && target &&
+ memcmp(driver->target, target, TARGET_SIZE) == 0)
+ return driver;
+ }
+
+ return NULL;
+}
+
+GSList *obex_service_driver_list(guint16 services)
+{
+ GSList *l;
+ GSList *list = NULL;
+
+ for (l = drivers; l && services; l = l->next) {
+ struct obex_service_driver *driver = l->data;
+
+ if (driver->service & services) {
+ list = g_slist_append(list, driver);
+ services &= ~driver->service;
+ }
+ }
+
+ return list;
+}
+
+int obex_service_driver_register(struct obex_service_driver *driver)
+{
+ if (!driver) {
+ error("Invalid driver");
+ return -EINVAL;
+ }
+
+ if (obex_service_driver_list(driver->service)) {
+ error("Permission denied: service %s already registered",
+ driver->name);
+ return -EPERM;
+ }
+
+ debug("driver %p service %s registered", driver, driver->name);
+
+ drivers = g_slist_append(drivers, driver);
+
+ return 0;
+}
+
+void obex_service_driver_unregister(struct obex_service_driver *driver)
+{
+ if (!g_slist_find(drivers, driver)) {
+ error("Unable to unregister: No such driver %p", driver);
+ return;
+ }
+
+ debug("driver %p service %s unregistered", driver, driver->name);
+
+ drivers = g_slist_remove(drivers, driver);
+}
diff --git a/obexd/src/service.h b/obexd/src/service.h
new file mode 100644
index 0000000..2cf3443
--- /dev/null
+++ b/obexd/src/service.h
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2009 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct obex_service_driver {
+ const char *name;
+ guint16 service;
+ guint8 channel;
+ const guint8 *target;
+ const gchar *record;
+ void (*get) (obex_t *obex, obex_object_t *obj);
+ void (*put) (obex_t *obex, obex_object_t *obj);
+ gint (*chkput) (obex_t *obex, obex_object_t *obj);
+ void (*setpath) (obex_t *obex, obex_object_t *obj);
+};
+
+int obex_service_driver_register(struct obex_service_driver *driver);
+void obex_service_driver_unregister(struct obex_service_driver *driver);
+GSList *obex_service_driver_list(guint16 services);
+struct obex_service_driver *obex_service_driver_find(GSList *drivers,
+ const guint8 *uuid);