// SPDX-License-Identifier: GPL-2.0-or-later /* * * OBEX Client * * Copyright (C) 2011 Bartosz Szatkowski for Comarch * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "lib/sdp.h" #include "gobex/gobex-apparam.h" #include "gdbus/gdbus.h" #include "obexd/src/log.h" #include "obexd/src/map_ap.h" #include "map-event.h" #include "map.h" #include "transfer.h" #include "session.h" #include "driver.h" #define OBEX_MAS_UUID \ "\xBB\x58\x2B\x40\x42\x0C\x11\xDB\xB0\xDE\x08\x00\x20\x0C\x9A\x66" #define OBEX_MAS_UUID_LEN 16 #define MAP_INTERFACE "org.bluez.obex.MessageAccess1" #define MAP_MSG_INTERFACE "org.bluez.obex.Message1" #define ERROR_INTERFACE "org.bluez.obex.Error" #define MAS_UUID "00001132-0000-1000-8000-00805f9b34fb" #define DEFAULT_COUNT 1024 #define DEFAULT_OFFSET 0 #define CHARSET_NATIVE 0 #define CHARSET_UTF8 1 static const char * const filter_list[] = { "subject", "timestamp", "sender", "sender-address", "recipient", "recipient-address", "type", "size", "status", "text", "attachment", "priority", "read", "sent", "protected", "replyto", NULL }; #define FILTER_BIT_MAX 15 #define FILTER_ALL 0x0000FFFF #define FILTER_READ_STATUS_NONE 0x00 #define FILTER_READ_STATUS_ONLY_UNREAD 0x01 #define FILTER_READ_STATUS_ONLY_READ 0x02 #define FILTER_PRIORITY_NONE 0x00 #define FILTER_PRIORITY_ONLY_HIGH 0x01 #define FILTER_PRIORITY_ONLY_NONHIGH 0x02 #define STATUS_READ 0 #define STATUS_DELETE 1 #define FILLER_BYTE 0x30 struct map_data { struct obc_session *session; GHashTable *messages; int16_t mas_instance_id; uint8_t supported_message_types; uint32_t supported_features; }; struct pending_request { struct map_data *map; DBusMessage *msg; char *folder; }; #define MAP_MSG_FLAG_PRIORITY 0x01 #define MAP_MSG_FLAG_READ 0x02 #define MAP_MSG_FLAG_SENT 0x04 #define MAP_MSG_FLAG_PROTECTED 0x08 #define MAP_MSG_FLAG_TEXT 0x10 struct map_msg { struct map_data *data; char *path; uint64_t handle; char *subject; char *timestamp; char *sender; char *sender_address; char *replyto; char *recipient; char *recipient_address; char *type; uint64_t size; char *status; uint64_t attachment_size; uint8_t flags; char *folder; GDBusPendingPropertySet pending; }; struct map_parser { struct pending_request *request; DBusMessageIter *iter; }; static DBusConnection *conn = NULL; static struct pending_request *pending_request_new(struct map_data *map, DBusMessage *message) { struct pending_request *p; p = g_new0(struct pending_request, 1); p->map = map; p->msg = dbus_message_ref(message); return p; } static void pending_request_free(struct pending_request *p) { dbus_message_unref(p->msg); g_free(p->folder); g_free(p); } static void simple_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; DBusMessage *reply; if (err != NULL) reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); else reply = dbus_message_new_method_return(request->msg); g_dbus_send_message(conn, reply); pending_request_free(request); } static DBusMessage *map_setpath(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_data *map = user_data; const char *folder; struct pending_request *request; GError *err = NULL; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &folder, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); request = pending_request_new(map, message); obc_session_setpath(map->session, folder, simple_cb, request, &err); if (err != NULL) { DBusMessage *reply; reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); pending_request_free(request); return reply; } return NULL; } static void folder_element(GMarkupParseContext *ctxt, const char *element, const char **names, const char **values, gpointer user_data, GError **gerr) { DBusMessageIter dict, *iter = user_data; const char *key; int i; if (strcasecmp("folder", element) != 0) return; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); for (i = 0, key = names[i]; key; key = names[++i]) { if (strcasecmp("name", key) == 0) g_dbus_dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &values[i]); } dbus_message_iter_close_container(iter, &dict); } static const GMarkupParser folder_parser = { folder_element, NULL, NULL, NULL, NULL }; static void folder_listing_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; GMarkupParseContext *ctxt; DBusMessage *reply; DBusMessageIter iter, array; char *contents; size_t size; int perr; if (err != NULL) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); goto done; } perr = obc_transfer_get_contents(transfer, &contents, &size); if (perr < 0) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "Error reading contents: %s", strerror(-perr)); goto done; } reply = dbus_message_new_method_return(request->msg); if (reply == NULL) { g_free(contents); goto clean; } dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); ctxt = g_markup_parse_context_new(&folder_parser, 0, &array, NULL); g_markup_parse_context_parse(ctxt, contents, size, NULL); g_markup_parse_context_free(ctxt); dbus_message_iter_close_container(&iter, &array); g_free(contents); done: g_dbus_send_message(conn, reply); clean: pending_request_free(request); } static DBusMessage *get_folder_listing(struct map_data *map, DBusMessage *message, GObexApparam *apparam) { struct pending_request *request; struct obc_transfer *transfer; GError *err = NULL; DBusMessage *reply; transfer = obc_transfer_get("x-obex/folder-listing", NULL, NULL, &err); if (transfer == NULL) { g_obex_apparam_free(apparam); goto fail; } obc_transfer_set_apparam(transfer, apparam); request = pending_request_new(map, message); if (!obc_session_queue(map->session, transfer, folder_listing_cb, request, &err)) { pending_request_free(request); goto fail; } return NULL; fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter) { guint16 num; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return NULL; dbus_message_iter_get_basic(iter, &num); return g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET, num); } static GObexApparam *parse_max_count(GObexApparam *apparam, DBusMessageIter *iter) { guint16 num; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return NULL; dbus_message_iter_get_basic(iter, &num); return g_obex_apparam_set_uint16(apparam, MAP_AP_MAXLISTCOUNT, num); } static GObexApparam *parse_folder_filters(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (strcasecmp(key, "Offset") == 0) { if (parse_offset(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "MaxCount") == 0) { if (parse_max_count(apparam, &value) == NULL) return NULL; } dbus_message_iter_next(&array); } return apparam; } static DBusMessage *map_list_folders(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_data *map = user_data; GObexApparam *apparam; DBusMessageIter args; dbus_message_iter_init(message, &args); apparam = g_obex_apparam_set_uint16(NULL, MAP_AP_MAXLISTCOUNT, DEFAULT_COUNT); apparam = g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET, DEFAULT_OFFSET); if (parse_folder_filters(apparam, &args) == NULL) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } return get_folder_listing(map, message, apparam); } static void map_msg_free(void *data) { struct map_msg *msg = data; g_free(msg->path); g_free(msg->subject); g_free(msg->folder); g_free(msg->timestamp); g_free(msg->sender); g_free(msg->sender_address); g_free(msg->replyto); g_free(msg->recipient); g_free(msg->recipient_address); g_free(msg->type); g_free(msg->status); g_free(msg); } static DBusMessage *map_msg_get(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_msg *msg = user_data; struct obc_transfer *transfer; const char *target_file; gboolean attachment; GError *err = NULL; DBusMessage *reply; GObexApparam *apparam; char handle[17]; if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &target_file, DBUS_TYPE_BOOLEAN, &attachment, DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); snprintf(handle, sizeof(handle), "%" PRIx64, msg->handle); transfer = obc_transfer_get("x-bt/message", handle, target_file, &err); if (transfer == NULL) goto fail; apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_ATTACHMENT, attachment); apparam = g_obex_apparam_set_uint8(apparam, MAP_AP_CHARSET, CHARSET_UTF8); obc_transfer_set_apparam(transfer, apparam); if (!obc_session_queue(msg->data->session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static void set_message_status_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct map_msg *msg = user_data; if (err != NULL) { g_dbus_pending_property_error(msg->pending, ERROR_INTERFACE ".Failed", "%s", err->message); goto done; } g_dbus_pending_property_success(msg->pending); done: msg->pending = 0; } static gboolean get_folder(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->folder); return TRUE; } static gboolean subject_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->subject != NULL; } static gboolean get_subject(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->subject); return TRUE; } static gboolean timestamp_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->timestamp != NULL; } static gboolean get_timestamp(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->timestamp); return TRUE; } static gboolean sender_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->sender != NULL; } static gboolean get_sender(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->sender); return TRUE; } static gboolean sender_address_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->sender_address != NULL; } static gboolean get_sender_address(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->sender_address); return TRUE; } static gboolean replyto_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->replyto != NULL; } static gboolean get_replyto(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->replyto); return TRUE; } static gboolean recipient_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->recipient != NULL; } static gboolean get_recipient(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->recipient); return TRUE; } static gboolean recipient_address_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->recipient_address != NULL; } static gboolean get_recipient_address(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->recipient_address); return TRUE; } static gboolean type_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->type != NULL; } static gboolean get_type(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->type); return TRUE; } static gboolean get_size(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &msg->size); return TRUE; } static gboolean reception_status_exists(const GDBusPropertyTable *property, void *data) { struct map_msg *msg = data; return msg->status != NULL; } static gboolean get_reception_status(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &msg->status); return TRUE; } static gboolean get_attachment_size(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct map_msg *msg = data; dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT64, &msg->attachment_size); return TRUE; } static gboolean get_flag(const GDBusPropertyTable *property, DBusMessageIter *iter, uint8_t flag, void *data) { struct map_msg *msg = data; dbus_bool_t value = (msg->flags & flag) != 0; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value); return TRUE; } static gboolean get_priority(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { return get_flag(property, iter, MAP_MSG_FLAG_PRIORITY, data); } static gboolean get_read(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { return get_flag(property, iter, MAP_MSG_FLAG_READ, data); } static gboolean get_sent(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { return get_flag(property, iter, MAP_MSG_FLAG_SENT, data); } static gboolean get_protected(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { return get_flag(property, iter, MAP_MSG_FLAG_PROTECTED, data); } static gboolean get_text(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { return get_flag(property, iter, MAP_MSG_FLAG_TEXT, data); } static void set_status(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, uint8_t status, void *data) { struct map_msg *msg = data; struct obc_transfer *transfer; gboolean value; GError *err = NULL; GObexApparam *apparam; char contents[1]; char handle[17]; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) { g_dbus_pending_property_error(id, ERROR_INTERFACE ".InvalidArguments", "Invalid arguments in method call"); return; } dbus_message_iter_get_basic(iter, &value); contents[0] = FILLER_BYTE; snprintf(handle, sizeof(handle), "%" PRIx64, msg->handle); transfer = obc_transfer_put("x-bt/messageStatus", handle, NULL, contents, sizeof(contents), &err); if (transfer == NULL) goto fail; apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_STATUSINDICATOR, status); apparam = g_obex_apparam_set_uint8(apparam, MAP_AP_STATUSVALUE, value); obc_transfer_set_apparam(transfer, apparam); if (!obc_session_queue(msg->data->session, transfer, set_message_status_cb, msg, &err)) goto fail; msg->pending = id; return; fail: g_dbus_pending_property_error(id, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); } static void set_read(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *data) { set_status(property, iter, id, STATUS_READ, data); } static void set_deleted(const GDBusPropertyTable *property, DBusMessageIter *iter, GDBusPendingPropertySet id, void *data) { set_status(property, iter, id, STATUS_DELETE, data); } static const GDBusMethodTable map_msg_methods[] = { { GDBUS_METHOD("Get", GDBUS_ARGS({ "targetfile", "s" }, { "attachment", "b" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), map_msg_get) }, { } }; static const GDBusPropertyTable map_msg_properties[] = { { "Folder", "s", get_folder }, { "Subject", "s", get_subject, NULL, subject_exists }, { "Timestamp", "s", get_timestamp, NULL, timestamp_exists }, { "Sender", "s", get_sender, NULL, sender_exists }, { "SenderAddress", "s", get_sender_address, NULL, sender_address_exists }, { "ReplyTo", "s", get_replyto, NULL, replyto_exists }, { "Recipient", "s", get_recipient, NULL, recipient_exists }, { "RecipientAddress", "s", get_recipient_address, NULL, recipient_address_exists }, { "Type", "s", get_type, NULL, type_exists }, { "Size", "t", get_size }, { "Text", "b", get_text }, { "Status", "s", get_reception_status, NULL, reception_status_exists }, { "AttachmentSize", "t", get_attachment_size }, { "Priority", "b", get_priority }, { "Read", "b", get_read, set_read }, { "Sent", "b", get_sent }, { "Protected", "b", get_protected }, { "Deleted", "b", NULL, set_deleted }, { } }; static void parse_type(struct map_msg *msg, const char *value) { const char *type = NULL; if (strcasecmp(value, "SMS_GSM") == 0) type = "sms-gsm"; else if (strcasecmp(value, "SMS_CDMA") == 0) type = "sms-cdma"; else if (strcasecmp(value, "EMAIL") == 0) type = "email"; else if (strcasecmp(value, "MMS") == 0) type = "mms"; if (g_strcmp0(msg->type, type) == 0) return; g_free(msg->type); msg->type = g_strdup(type); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Type"); } static struct map_msg *map_msg_create(struct map_data *data, uint64_t handle, const char *folder, const char *type) { struct map_msg *msg; msg = g_new0(struct map_msg, 1); msg->data = data; msg->handle = handle; msg->path = g_strdup_printf("%s/message%" PRIu64, obc_session_get_path(data->session), msg->handle); msg->folder = g_strdup(folder); if (!g_dbus_register_interface(conn, msg->path, MAP_MSG_INTERFACE, map_msg_methods, NULL, map_msg_properties, msg, map_msg_free)) { map_msg_free(msg); return NULL; } g_hash_table_insert(data->messages, &msg->handle, msg); if (type) parse_type(msg, type); return msg; } static void parse_subject(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->subject, value) == 0) return; g_free(msg->subject); msg->subject = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Subject"); } static void parse_datetime(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->timestamp, value) == 0) return; g_free(msg->timestamp); msg->timestamp = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Timestamp"); } static void parse_sender(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->sender, value) == 0) return; g_free(msg->sender); msg->sender = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Sender"); } static void parse_sender_address(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->sender_address, value) == 0) return; g_free(msg->sender_address); msg->sender_address = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "SenderAddress"); } static void parse_replyto(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->replyto, value) == 0) return; g_free(msg->replyto); msg->replyto = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "ReplyTo"); } static void parse_recipient(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->recipient, value) == 0) return; g_free(msg->recipient); msg->recipient = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Recipient"); } static void parse_recipient_address(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->recipient_address, value) == 0) return; g_free(msg->recipient_address); msg->recipient_address = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "RecipientAddress"); } static void parse_size(struct map_msg *msg, const char *value) { uint64_t size = g_ascii_strtoll(value, NULL, 10); if (msg->size == size) return; msg->size = size; g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Size"); } static void parse_text(struct map_msg *msg, const char *value) { gboolean flag = strcasecmp(value, "no") != 0; uint8_t oldflags = msg->flags; if (flag) msg->flags |= MAP_MSG_FLAG_TEXT; else msg->flags &= ~MAP_MSG_FLAG_TEXT; if (msg->flags != oldflags) g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Text"); } static void parse_status(struct map_msg *msg, const char *value) { if (g_strcmp0(msg->status, value) == 0) return; g_free(msg->status); msg->status = g_strdup(value); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Status"); } static void parse_attachment_size(struct map_msg *msg, const char *value) { uint64_t attachment_size = g_ascii_strtoll(value, NULL, 10); if (msg->attachment_size == attachment_size) return; msg->attachment_size = attachment_size; g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "AttachmentSize"); } static void parse_priority(struct map_msg *msg, const char *value) { gboolean flag = strcasecmp(value, "no") != 0; uint8_t oldflags = msg->flags; if (flag) msg->flags |= MAP_MSG_FLAG_PRIORITY; else msg->flags &= ~MAP_MSG_FLAG_PRIORITY; if (msg->flags != oldflags) g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Priority"); } static void parse_read(struct map_msg *msg, const char *value) { gboolean flag = strcasecmp(value, "no") != 0; uint8_t oldflags = msg->flags; if (flag) msg->flags |= MAP_MSG_FLAG_READ; else msg->flags &= ~MAP_MSG_FLAG_READ; if (msg->flags != oldflags) g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Read"); } static void parse_sent(struct map_msg *msg, const char *value) { gboolean flag = strcasecmp(value, "no") != 0; uint8_t oldflags = msg->flags; if (flag) msg->flags |= MAP_MSG_FLAG_SENT; else msg->flags &= ~MAP_MSG_FLAG_SENT; if (msg->flags != oldflags) g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Sent"); } static void parse_protected(struct map_msg *msg, const char *value) { gboolean flag = strcasecmp(value, "no") != 0; uint8_t oldflags = msg->flags; if (flag) msg->flags |= MAP_MSG_FLAG_PROTECTED; else msg->flags &= ~MAP_MSG_FLAG_PROTECTED; if (msg->flags != oldflags) g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Protected"); } static struct map_msg_parser { const char *name; void (*func) (struct map_msg *msg, const char *value); } msg_parsers[] = { { "subject", parse_subject }, { "datetime", parse_datetime }, { "sender_name", parse_sender }, { "sender_addressing", parse_sender_address }, { "replyto_addressing", parse_replyto }, { "recipient_name", parse_recipient }, { "recipient_addressing", parse_recipient_address }, { "type", parse_type }, { "size", parse_size }, { "text", parse_text }, { "reception_status", parse_status }, { "attachment_size", parse_attachment_size }, { "priority", parse_priority }, { "read", parse_read }, { "sent", parse_sent }, { "protected", parse_protected }, { } }; static void msg_element(GMarkupParseContext *ctxt, const char *element, const char **names, const char **values, gpointer user_data, GError **gerr) { struct map_parser *parser = user_data; struct map_data *data = parser->request->map; DBusMessageIter entry, *iter = parser->iter; struct map_msg *msg; const char *key; int i; uint64_t handle; if (strcasecmp("msg", element) != 0) return; for (i = 0, key = names[i]; key; key = names[++i]) { if (strcasecmp(key, "handle") == 0) break; } handle = strtoull(values[i], NULL, 16); msg = g_hash_table_lookup(data->messages, &handle); if (msg == NULL) { msg = map_msg_create(data, handle, parser->request->folder, NULL); if (msg == NULL) return; } dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry); dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &msg->path); for (i = 0, key = names[i]; key; key = names[++i]) { struct map_msg_parser *parser; for (parser = msg_parsers; parser && parser->name; parser++) { if (strcasecmp(key, parser->name) == 0) { if (values[i]) parser->func(msg, values[i]); break; } } } g_dbus_get_properties(conn, msg->path, MAP_MSG_INTERFACE, &entry); dbus_message_iter_close_container(iter, &entry); } static const GMarkupParser msg_parser = { msg_element, NULL, NULL, NULL, NULL }; static void message_listing_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; struct map_parser *parser; GMarkupParseContext *ctxt; DBusMessage *reply; DBusMessageIter iter, array; char *contents; size_t size; int perr; if (err != NULL) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); goto done; } perr = obc_transfer_get_contents(transfer, &contents, &size); if (perr < 0) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "Error reading contents: %s", strerror(-perr)); goto done; } reply = dbus_message_new_method_return(request->msg); if (reply == NULL) { g_free(contents); goto clean; } dbus_message_iter_init_append(reply, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array); parser = g_new(struct map_parser, 1); parser->request = request; parser->iter = &array; ctxt = g_markup_parse_context_new(&msg_parser, 0, parser, NULL); g_markup_parse_context_parse(ctxt, contents, size, NULL); g_markup_parse_context_free(ctxt); dbus_message_iter_close_container(&iter, &array); g_free(contents); g_free(parser); done: g_dbus_send_message(conn, reply); clean: pending_request_free(request); } static char *get_absolute_folder(struct map_data *map, const char *subfolder) { const char *root = obc_session_get_folder(map->session); if (!subfolder || strlen(subfolder) == 0) return g_strdup(root); else if (g_str_has_suffix(root, "/")) return g_strconcat(root, subfolder, NULL); else return g_strconcat(root, "/", subfolder, NULL); } static DBusMessage *get_message_listing(struct map_data *map, DBusMessage *message, const char *folder, GObexApparam *apparam) { struct pending_request *request; struct obc_transfer *transfer; GError *err = NULL; DBusMessage *reply; transfer = obc_transfer_get("x-bt/MAP-msg-listing", folder, NULL, &err); if (transfer == NULL) { g_obex_apparam_free(apparam); goto fail; } obc_transfer_set_apparam(transfer, apparam); request = pending_request_new(map, message); request->folder = get_absolute_folder(map, folder); if (!obc_session_queue(map->session, transfer, message_listing_cb, request, &err)) { pending_request_free(request); goto fail; } return NULL; fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static GObexApparam *parse_subject_length(GObexApparam *apparam, DBusMessageIter *iter) { guint8 num; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BYTE) return NULL; dbus_message_iter_get_basic(iter, &num); return g_obex_apparam_set_uint8(apparam, MAP_AP_SUBJECTLENGTH, num); } static uint64_t get_filter_mask(const char *filterstr) { int i; if (!filterstr) return 0; if (!g_ascii_strcasecmp(filterstr, "ALL")) return FILTER_ALL; for (i = 0; filter_list[i] != NULL; i++) if (!g_ascii_strcasecmp(filterstr, filter_list[i])) return 1ULL << i; return 0; } static int set_field(guint32 *filter, const char *filterstr) { guint64 mask; mask = get_filter_mask(filterstr); if (mask == 0) return -EINVAL; *filter |= mask; return 0; } static GObexApparam *parse_fields(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; guint32 filter = 0; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { const char *string; dbus_message_iter_get_basic(&array, &string); if (set_field(&filter, string) < 0) return NULL; dbus_message_iter_next(&array); } return g_obex_apparam_set_uint32(apparam, MAP_AP_PARAMETERMASK, filter); } static GObexApparam *parse_filter_type(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; guint8 types = 0; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) { const char *string; dbus_message_iter_get_basic(&array, &string); if (!g_ascii_strcasecmp(string, "sms")) types |= 0x03; /* sms-gsm and sms-cdma */ else if (!g_ascii_strcasecmp(string, "email")) types |= 0x04; /* email */ else if (!g_ascii_strcasecmp(string, "mms")) types |= 0x08; /* mms */ else return NULL; dbus_message_iter_next(&array); } return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERMESSAGETYPE, types); } static GObexApparam *parse_period_begin(GObexApparam *apparam, DBusMessageIter *iter) { const char *string; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); return g_obex_apparam_set_string(apparam, MAP_AP_FILTERPERIODBEGIN, string); } static GObexApparam *parse_period_end(GObexApparam *apparam, DBusMessageIter *iter) { const char *string; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); return g_obex_apparam_set_string(apparam, MAP_AP_FILTERPERIODEND, string); } static GObexApparam *parse_filter_read(GObexApparam *apparam, DBusMessageIter *iter) { guint8 status = FILTER_READ_STATUS_NONE; dbus_bool_t dbus_status = FALSE; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return NULL; dbus_message_iter_get_basic(iter, &dbus_status); if (dbus_status) status = FILTER_READ_STATUS_ONLY_READ; else status = FILTER_READ_STATUS_ONLY_UNREAD; return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERREADSTATUS, status); } static GObexApparam *parse_filter_recipient(GObexApparam *apparam, DBusMessageIter *iter) { const char *string; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); return g_obex_apparam_set_string(apparam, MAP_AP_FILTERRECIPIENT, string); } static GObexApparam *parse_filter_sender(GObexApparam *apparam, DBusMessageIter *iter) { const char *string; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); return g_obex_apparam_set_string(apparam, MAP_AP_FILTERORIGINATOR, string); } static GObexApparam *parse_filter_priority(GObexApparam *apparam, DBusMessageIter *iter) { guint8 priority = FILTER_PRIORITY_NONE; dbus_bool_t dbus_priority = FALSE; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return NULL; dbus_message_iter_get_basic(iter, &dbus_priority); if (dbus_priority) priority = FILTER_PRIORITY_ONLY_HIGH; else priority = FILTER_PRIORITY_ONLY_NONHIGH; return g_obex_apparam_set_uint8(apparam, MAP_AP_FILTERPRIORITY, priority); } static GObexApparam *parse_message_filters(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; DBG(""); if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (strcasecmp(key, "Offset") == 0) { if (parse_offset(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "MaxCount") == 0) { if (parse_max_count(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "SubjectLength") == 0) { if (parse_subject_length(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Fields") == 0) { if (parse_fields(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Types") == 0) { if (parse_filter_type(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "PeriodBegin") == 0) { if (parse_period_begin(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "PeriodEnd") == 0) { if (parse_period_end(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Read") == 0) { if (parse_filter_read(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Recipient") == 0) { if (parse_filter_recipient(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Sender") == 0) { if (parse_filter_sender(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Priority") == 0) { if (parse_filter_priority(apparam, &value) == NULL) return NULL; } dbus_message_iter_next(&array); } return apparam; } static DBusMessage *map_list_messages(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_data *map = user_data; const char *folder; GObexApparam *apparam; DBusMessageIter args; dbus_message_iter_init(message, &args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); dbus_message_iter_get_basic(&args, &folder); apparam = g_obex_apparam_set_uint16(NULL, MAP_AP_MAXLISTCOUNT, DEFAULT_COUNT); apparam = g_obex_apparam_set_uint16(apparam, MAP_AP_STARTOFFSET, DEFAULT_OFFSET); dbus_message_iter_next(&args); if (parse_message_filters(apparam, &args) == NULL) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } return get_message_listing(map, message, folder, apparam); } static char **get_filter_strs(uint64_t filter, int *size) { char **list, **item; int i; list = g_malloc0(sizeof(char **) * (FILTER_BIT_MAX + 2)); item = list; for (i = 0; filter_list[i] != NULL; i++) if (filter & (1ULL << i)) *(item++) = g_strdup(filter_list[i]); *item = NULL; *size = item - list; return list; } static DBusMessage *map_list_filter_fields(DBusConnection *connection, DBusMessage *message, void *user_data) { char **filters = NULL; int size; DBusMessage *reply; filters = get_filter_strs(FILTER_ALL, &size); reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &filters, size, DBUS_TYPE_INVALID); g_strfreev(filters); return reply; } static void update_inbox_cb(struct obc_session *session, struct obc_transfer *transfer, GError *err, void *user_data) { struct pending_request *request = user_data; DBusMessage *reply; if (err != NULL) { reply = g_dbus_create_error(request->msg, ERROR_INTERFACE ".Failed", "%s", err->message); goto done; } reply = dbus_message_new_method_return(request->msg); done: g_dbus_send_message(conn, reply); pending_request_free(request); } static DBusMessage *map_update_inbox(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_data *map = user_data; DBusMessage *reply; char contents[1]; struct obc_transfer *transfer; GError *err = NULL; struct pending_request *request; contents[0] = FILLER_BYTE; transfer = obc_transfer_put("x-bt/MAP-messageUpdate", NULL, NULL, contents, sizeof(contents), &err); if (transfer == NULL) goto fail; request = pending_request_new(map, message); if (!obc_session_queue(map->session, transfer, update_inbox_cb, request, &err)) { pending_request_free(request); goto fail; } return NULL; fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static DBusMessage *push_message(struct map_data *map, DBusMessage *message, const char *filename, const char *folder, GObexApparam *apparam) { struct obc_transfer *transfer; GError *err = NULL; DBusMessage *reply; transfer = obc_transfer_put("x-bt/message", folder, filename, NULL, 0, &err); if (transfer == NULL) { g_obex_apparam_free(apparam); goto fail; } obc_transfer_set_apparam(transfer, apparam); if (!obc_session_queue(map->session, transfer, NULL, NULL, &err)) goto fail; return obc_transfer_create_dbus_reply(transfer, message); fail: reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s", err->message); g_error_free(err); return reply; } static GObexApparam *parse_transparent(GObexApparam *apparam, DBusMessageIter *iter) { dbus_bool_t transparent; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return NULL; dbus_message_iter_get_basic(iter, &transparent); return g_obex_apparam_set_uint8(apparam, MAP_AP_TRANSPARENT, transparent ? TRUE : FALSE); } static GObexApparam *parse_retry(GObexApparam *apparam, DBusMessageIter *iter) { dbus_bool_t retry; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) return NULL; dbus_message_iter_get_basic(iter, &retry); return g_obex_apparam_set_uint8(apparam, MAP_AP_RETRY, retry ? TRUE : FALSE); } static GObexApparam *parse_charset(GObexApparam *apparam, DBusMessageIter *iter) { guint8 charset = 0; const char *string; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return NULL; dbus_message_iter_get_basic(iter, &string); if (strcasecmp(string, "native") == 0) charset = CHARSET_NATIVE; else if (strcasecmp(string, "utf8") == 0) charset = CHARSET_UTF8; else return NULL; return g_obex_apparam_set_uint8(apparam, MAP_AP_CHARSET, charset); } static GObexApparam *parse_push_options(GObexApparam *apparam, DBusMessageIter *iter) { DBusMessageIter array; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) return NULL; dbus_message_iter_recurse(iter, &array); while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) { const char *key; DBusMessageIter value, entry; dbus_message_iter_recurse(&array, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (strcasecmp(key, "Transparent") == 0) { if (parse_transparent(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Retry") == 0) { if (parse_retry(apparam, &value) == NULL) return NULL; } else if (strcasecmp(key, "Charset") == 0) { if (parse_charset(apparam, &value) == NULL) return NULL; } dbus_message_iter_next(&array); } return apparam; } static DBusMessage *map_push_message(DBusConnection *connection, DBusMessage *message, void *user_data) { struct map_data *map = user_data; char *filename; char *folder; GObexApparam *apparam; DBusMessageIter args; dbus_message_iter_init(message, &args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); dbus_message_iter_get_basic(&args, &filename); dbus_message_iter_next(&args); if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) { return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } dbus_message_iter_get_basic(&args, &folder); dbus_message_iter_next(&args); apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_CHARSET, CHARSET_UTF8); if (parse_push_options(apparam, &args) == NULL) { g_obex_apparam_free(apparam); return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); } return push_message(map, message, filename, folder, apparam); } static const GDBusMethodTable map_methods[] = { { GDBUS_ASYNC_METHOD("SetFolder", GDBUS_ARGS({ "name", "s" }), NULL, map_setpath) }, { GDBUS_ASYNC_METHOD("ListFolders", GDBUS_ARGS({ "filters", "a{sv}" }), GDBUS_ARGS({ "content", "aa{sv}" }), map_list_folders) }, { GDBUS_ASYNC_METHOD("ListMessages", GDBUS_ARGS({ "folder", "s" }, { "filter", "a{sv}" }), GDBUS_ARGS({ "messages", "a{oa{sv}}" }), map_list_messages) }, { GDBUS_METHOD("ListFilterFields", NULL, GDBUS_ARGS({ "fields", "as" }), map_list_filter_fields) }, { GDBUS_ASYNC_METHOD("UpdateInbox", NULL, NULL, map_update_inbox) }, { GDBUS_ASYNC_METHOD("PushMessage", GDBUS_ARGS({ "file", "s" }, { "folder", "s" }, { "args", "a{sv}" }), GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), map_push_message) }, { } }; static void map_msg_remove(void *data) { struct map_msg *msg = data; char *path; path = msg->path; msg->path = NULL; g_dbus_unregister_interface(conn, path, MAP_MSG_INTERFACE); g_free(path); } static void map_handle_new_message(struct map_data *map, struct map_event *event) { struct map_msg *msg; msg = g_hash_table_lookup(map->messages, &event->handle); /* New message event can be used if a new message replaces an old one */ if (msg) g_hash_table_remove(map->messages, &event->handle); map_msg_create(map, event->handle, event->folder, event->msg_type); } static void map_handle_status_changed(struct map_data *map, struct map_event *event, const char *status) { struct map_msg *msg; msg = g_hash_table_lookup(map->messages, &event->handle); if (msg == NULL) return; if (g_strcmp0(msg->status, status) == 0) return; g_free(msg->status); msg->status = g_strdup(status); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Status"); } static void map_handle_folder_changed(struct map_data *map, struct map_event *event, const char *folder) { struct map_msg *msg; if (!folder) return; msg = g_hash_table_lookup(map->messages, &event->handle); if (!msg) return; if (g_strcmp0(msg->folder, folder) == 0) return; g_free(msg->folder); msg->folder = g_strdup(folder); g_dbus_emit_property_changed(conn, msg->path, MAP_MSG_INTERFACE, "Folder"); } static void map_handle_notification(struct map_event *event, void *user_data) { struct map_data *map = user_data; DBG("Event report for %s:%d", obc_session_get_destination(map->session), map->mas_instance_id); DBG("type=%x handle=%" PRIx64 " folder=%s old_folder=%s msg_type=%s", event->type, event->handle, event->folder, event->old_folder, event->msg_type); switch (event->type) { case MAP_ET_NEW_MESSAGE: map_handle_new_message(map, event); map_handle_status_changed(map, event, "notification"); break; case MAP_ET_DELIVERY_SUCCESS: map_handle_status_changed(map, event, "delivery-success"); break; case MAP_ET_SENDING_SUCCESS: map_handle_status_changed(map, event, "sending-success"); break; case MAP_ET_DELIVERY_FAILURE: map_handle_status_changed(map, event, "delivery-failure"); break; case MAP_ET_SENDING_FAILURE: map_handle_status_changed(map, event, "sending-failure"); break; case MAP_ET_MESSAGE_DELETED: map_handle_folder_changed(map, event, "/telecom/msg/deleted"); break; case MAP_ET_MESSAGE_SHIFT: map_handle_folder_changed(map, event, event->folder); break; case MAP_ET_MEMORY_FULL: case MAP_ET_MEMORY_AVAILABLE: default: break; } } static bool set_notification_registration(struct map_data *map, bool status) { struct obc_transfer *transfer; GError *err = NULL; GObexApparam *apparam; char contents[1]; const char *address; address = obc_session_get_destination(map->session); if (!address || map->mas_instance_id < 0) return FALSE; if (status) { map_register_event_handler(map->session, map->mas_instance_id, &map_handle_notification, map); } else { map_unregister_event_handler(map->session, map->mas_instance_id); } contents[0] = FILLER_BYTE; transfer = obc_transfer_put("x-bt/MAP-NotificationRegistration", NULL, NULL, contents, sizeof(contents), &err); if (transfer == NULL) return false; apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_NOTIFICATIONSTATUS, status); obc_transfer_set_apparam(transfer, apparam); if (obc_session_queue(map->session, transfer, NULL, map, &err)) return true; return false; } static void map_free(void *data) { struct map_data *map = data; set_notification_registration(map, false); obc_session_unref(map->session); g_hash_table_unref(map->messages); g_free(map); } static void parse_service_record(struct map_data *map) { const void *data; /* MAS instance id */ map->mas_instance_id = -1; data = obc_session_get_attribute(map->session, SDP_ATTR_MAS_INSTANCE_ID); if (data != NULL) map->mas_instance_id = *(uint8_t *)data; else DBG("Failed to read MAS instance id"); /* Supported Message Types */ data = obc_session_get_attribute(map->session, SDP_ATTR_SUPPORTED_MESSAGE_TYPES); if (data != NULL) map->supported_message_types = *(uint8_t *)data; else DBG("Failed to read supported message types"); /* Supported Feature Bits */ data = obc_session_get_attribute(map->session, SDP_ATTR_MAP_SUPPORTED_FEATURES); if(data != NULL) map->supported_features = *(uint32_t *) data; else map->supported_features = 0x0000001f; } static int map_probe(struct obc_session *session) { struct map_data *map; const char *path; path = obc_session_get_path(session); map = g_try_new0(struct map_data, 1); if (!map) return -ENOMEM; map->session = obc_session_ref(session); map->messages = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, map_msg_remove); parse_service_record(map); DBG("%s, instance id %d", path, map->mas_instance_id); set_notification_registration(map, true); if (!g_dbus_register_interface(conn, path, MAP_INTERFACE, map_methods, NULL, NULL, map, map_free)) { map_free(map); return -ENOMEM; } return 0; } static void map_remove(struct obc_session *session) { const char *path = obc_session_get_path(session); DBG("%s", path); g_dbus_unregister_interface(conn, path, MAP_INTERFACE); } static struct obc_driver map = { .service = "MAP", .uuid = MAS_UUID, .target = OBEX_MAS_UUID, .target_len = OBEX_MAS_UUID_LEN, .probe = map_probe, .remove = map_remove }; int map_init(void) { int err; DBG(""); conn = dbus_bus_get(DBUS_BUS_SESSION, NULL); if (!conn) return -EIO; err = obc_driver_register(&map); if (err < 0) { dbus_connection_unref(conn); conn = NULL; return err; } return 0; } void map_exit(void) { DBG(""); dbus_connection_unref(conn); conn = NULL; obc_driver_unregister(&map); }