5 * Copyright (C) 2011 Bartosz Szatkowski <bulislaw@linux.com> for Comarch
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
40 #define OBEX_MAS_UUID \
41 "\xBB\x58\x2B\x40\x42\x0C\x11\xDB\xB0\xDE\x08\x00\x20\x0C\x9A\x66"
42 #define OBEX_MAS_UUID_LEN 16
44 #define MAP_INTERFACE "org.bluez.obex.MessageAccess"
45 #define MAP_MSG_INTERFACE "org.bluez.obex.Message"
46 #define ERROR_INTERFACE "org.bluez.obex.Error"
47 #define MAS_UUID "00001132-0000-1000-8000-00805f9b34fb"
50 struct obc_session *session;
55 #define MAP_MSG_FLAG_PRIORITY 0x01
56 #define MAP_MSG_FLAG_READ 0x02
57 #define MAP_MSG_FLAG_SENT 0x04
58 #define MAP_MSG_FLAG_PROTECTED 0x08
61 struct map_data *data;
70 char *recipient_address;
78 struct map_data *data;
79 DBusMessageIter *iter;
82 static DBusConnection *conn = NULL;
84 static void simple_cb(struct obc_session *session,
85 struct obc_transfer *transfer,
86 GError *err, void *user_data)
89 struct map_data *map = user_data;
92 reply = g_dbus_create_error(map->msg,
93 ERROR_INTERFACE ".Failed",
96 reply = dbus_message_new_method_return(map->msg);
98 g_dbus_send_message(conn, reply);
99 dbus_message_unref(map->msg);
102 static DBusMessage *map_setpath(DBusConnection *connection,
103 DBusMessage *message, void *user_data)
105 struct map_data *map = user_data;
109 if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &folder,
110 DBUS_TYPE_INVALID) == FALSE)
111 return g_dbus_create_error(message,
112 ERROR_INTERFACE ".InvalidArguments",
115 obc_session_setpath(map->session, folder, simple_cb, map, &err);
118 reply = g_dbus_create_error(message,
119 ERROR_INTERFACE ".Failed",
125 map->msg = dbus_message_ref(message);
130 static void folder_element(GMarkupParseContext *ctxt, const gchar *element,
131 const gchar **names, const gchar **values,
132 gpointer user_data, GError **gerr)
134 DBusMessageIter dict, *iter = user_data;
138 if (strcasecmp("folder", element) != 0)
141 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
142 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
143 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
144 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
146 for (i = 0, key = names[i]; key; key = names[++i]) {
147 if (strcasecmp("name", key) == 0)
148 obex_dbus_dict_append(&dict, "Name", DBUS_TYPE_STRING,
152 dbus_message_iter_close_container(iter, &dict);
155 static const GMarkupParser folder_parser = {
163 static void folder_listing_cb(struct obc_session *session,
164 struct obc_transfer *transfer,
165 GError *err, void *user_data)
167 struct map_data *map = user_data;
168 GMarkupParseContext *ctxt;
170 DBusMessageIter iter, array;
176 reply = g_dbus_create_error(map->msg,
177 ERROR_INTERFACE ".Failed",
182 perr = obc_transfer_get_contents(transfer, &contents, &size);
184 reply = g_dbus_create_error(map->msg,
185 ERROR_INTERFACE ".Failed",
186 "Error reading contents: %s",
191 reply = dbus_message_new_method_return(map->msg);
195 dbus_message_iter_init_append(reply, &iter);
196 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
197 DBUS_TYPE_ARRAY_AS_STRING
198 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
199 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
200 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &array);
201 ctxt = g_markup_parse_context_new(&folder_parser, 0, &array, NULL);
202 g_markup_parse_context_parse(ctxt, contents, size, NULL);
203 g_markup_parse_context_free(ctxt);
204 dbus_message_iter_close_container(&iter, &array);
208 g_dbus_send_message(conn, reply);
209 dbus_message_unref(map->msg);
212 static DBusMessage *map_get_folder_listing(DBusConnection *connection,
213 DBusMessage *message, void *user_data)
215 struct map_data *map = user_data;
216 struct obc_transfer *transfer;
220 transfer = obc_transfer_get("x-obex/folder-listing", NULL, NULL, &err);
221 if (transfer == NULL)
224 if (obc_session_queue(map->session, transfer, folder_listing_cb, map,
226 map->msg = dbus_message_ref(message);
231 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
237 static void map_msg_free(void *data)
239 struct map_msg *msg = data;
242 g_free(msg->subject);
244 g_free(msg->timestamp);
246 g_free(msg->sender_address);
247 g_free(msg->replyto);
248 g_free(msg->recipient);
249 g_free(msg->recipient_address);
255 static DBusMessage *map_msg_get(DBusConnection *connection,
256 DBusMessage *message, void *user_data)
258 struct map_msg *msg = user_data;
259 struct obc_transfer *transfer;
260 const char *target_file;
264 if (dbus_message_get_args(message, NULL,
265 DBUS_TYPE_STRING, &target_file,
266 DBUS_TYPE_INVALID) == FALSE)
267 return g_dbus_create_error(message,
268 ERROR_INTERFACE ".InvalidArguments", NULL);
270 transfer = obc_transfer_get("x-bt/message", msg->handle, target_file,
272 if (transfer == NULL)
275 if (!obc_session_queue(msg->data->session, transfer, NULL, NULL, &err))
278 return obc_transfer_create_dbus_reply(transfer, message);
281 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
287 static const GDBusMethodTable map_msg_methods[] = {
288 { GDBUS_METHOD("Get",
289 GDBUS_ARGS({ "targetfile", "s" }),
290 GDBUS_ARGS({ "transfer", "o" },
291 { "properties", "a{sv}" }),
296 static struct map_msg *map_msg_create(struct map_data *data, const char *handle)
300 msg = g_new0(struct map_msg, 1);
302 msg->path = g_strdup_printf("%s/message%s",
303 obc_session_get_path(data->session),
306 if (!g_dbus_register_interface(conn, msg->path, MAP_MSG_INTERFACE,
307 map_msg_methods, NULL, NULL,
308 msg, map_msg_free)) {
313 msg->handle = g_strdup(handle);
314 g_hash_table_insert(data->messages, msg->handle, msg);
319 static void parse_subject(struct map_msg *msg, const char *value,
320 DBusMessageIter *iter)
322 g_free(msg->subject);
323 msg->subject = g_strdup(value);
324 obex_dbus_dict_append(iter, "Subject", DBUS_TYPE_STRING, &value);
327 static void parse_datetime(struct map_msg *msg, const char *value,
328 DBusMessageIter *iter)
330 g_free(msg->timestamp);
331 msg->timestamp = g_strdup(value);
332 obex_dbus_dict_append(iter, "Timestamp", DBUS_TYPE_STRING, &value);
335 static void parse_sender(struct map_msg *msg, const char *value,
336 DBusMessageIter *iter)
339 msg->sender = g_strdup(value);
340 obex_dbus_dict_append(iter, "Sender", DBUS_TYPE_STRING, &value);
343 static void parse_sender_address(struct map_msg *msg, const char *value,
344 DBusMessageIter *iter)
346 g_free(msg->sender_address);
347 msg->sender_address = g_strdup(value);
348 obex_dbus_dict_append(iter, "SenderAddress", DBUS_TYPE_STRING,
352 static void parse_replyto(struct map_msg *msg, const char *value,
353 DBusMessageIter *iter)
355 g_free(msg->replyto);
356 msg->replyto = g_strdup(value);
357 obex_dbus_dict_append(iter, "ReplyTo", DBUS_TYPE_STRING, &value);
360 static void parse_recipient(struct map_msg *msg, const char *value,
361 DBusMessageIter *iter)
363 g_free(msg->recipient);
364 msg->recipient = g_strdup(value);
365 obex_dbus_dict_append(iter, "Recipient", DBUS_TYPE_STRING, &value);
368 static void parse_recipient_address(struct map_msg *msg, const char *value,
369 DBusMessageIter *iter)
371 g_free(msg->recipient_address);
372 msg->recipient_address = g_strdup(value);
373 obex_dbus_dict_append(iter, "RecipientAddress", DBUS_TYPE_STRING,
377 static void parse_type(struct map_msg *msg, const char *value,
378 DBusMessageIter *iter)
381 msg->type = g_strdup(value);
382 obex_dbus_dict_append(iter, "Type", DBUS_TYPE_STRING, &value);
385 static void parse_status(struct map_msg *msg, const char *value,
386 DBusMessageIter *iter)
389 msg->status = g_strdup(value);
390 obex_dbus_dict_append(iter, "Status", DBUS_TYPE_STRING, &value);
393 static void parse_size(struct map_msg *msg, const char *value,
394 DBusMessageIter *iter)
396 msg->size = g_ascii_strtoll(value, NULL, 10);
397 obex_dbus_dict_append(iter, "Size", DBUS_TYPE_UINT64, &msg->size);
400 static void parse_priority(struct map_msg *msg, const char *value,
401 DBusMessageIter *iter)
403 gboolean flag = strcasecmp(value, "no");
406 msg->flags |= MAP_MSG_FLAG_PRIORITY;
408 msg->flags &= ~MAP_MSG_FLAG_PRIORITY;
410 obex_dbus_dict_append(iter, "Priority", DBUS_TYPE_BOOLEAN, &flag);
413 static void parse_read(struct map_msg *msg, const char *value,
414 DBusMessageIter *iter)
416 gboolean flag = strcasecmp(value, "no");
419 msg->flags |= MAP_MSG_FLAG_READ;
421 msg->flags &= ~MAP_MSG_FLAG_READ;
423 obex_dbus_dict_append(iter, "Read", DBUS_TYPE_BOOLEAN, &flag);
426 static void parse_sent(struct map_msg *msg, const char *value,
427 DBusMessageIter *iter)
429 gboolean flag = strcasecmp(value, "no");
432 msg->flags |= MAP_MSG_FLAG_SENT;
434 msg->flags &= ~MAP_MSG_FLAG_SENT;
436 obex_dbus_dict_append(iter, "Sent", DBUS_TYPE_BOOLEAN, &flag);
439 static void parse_protected(struct map_msg *msg, const char *value,
440 DBusMessageIter *iter)
442 gboolean flag = strcasecmp(value, "no");
445 msg->flags |= MAP_MSG_FLAG_PROTECTED;
447 msg->flags &= ~MAP_MSG_FLAG_PROTECTED;
449 obex_dbus_dict_append(iter, "Protected", DBUS_TYPE_BOOLEAN, &flag);
452 static struct map_msg_parser {
454 void (*func) (struct map_msg *msg, const char *value,
455 DBusMessageIter *iter);
457 { "subject", parse_subject },
458 { "datetime", parse_datetime },
459 { "sender_name", parse_sender },
460 { "sender_addressing", parse_sender_address },
461 { "replyto_addressing", parse_replyto },
462 { "recipient_name", parse_recipient },
463 { "recipient_addressing", parse_recipient_address },
464 { "type", parse_type },
465 { "reception_status", parse_status },
466 { "size", parse_size },
467 { "priority", parse_priority },
468 { "read", parse_read },
469 { "sent", parse_sent },
470 { "protected", parse_protected },
474 static void msg_element(GMarkupParseContext *ctxt, const gchar *element,
475 const gchar **names, const gchar **values,
476 gpointer user_data, GError **gerr)
478 struct map_parser *parser = user_data;
479 struct map_data *data = parser->data;
480 DBusMessageIter entry, dict, *iter = parser->iter;
485 if (strcasecmp("msg", element) != 0)
488 for (i = 0, key = names[i]; key; key = names[++i]) {
489 if (strcasecmp(key, "handle") == 0)
493 msg = g_hash_table_lookup(data->messages, key);
495 msg = map_msg_create(data, values[i]);
500 dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL,
503 dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
506 dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
507 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
508 DBUS_TYPE_STRING_AS_STRING
509 DBUS_TYPE_VARIANT_AS_STRING
510 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
513 for (i = 0, key = names[i]; key; key = names[++i]) {
514 struct map_msg_parser *parser;
516 for (parser = msg_parsers; parser && parser->name; parser++) {
517 if (strcasecmp(key, parser->name) == 0) {
518 parser->func(msg, values[i], &dict);
524 dbus_message_iter_close_container(&entry, &dict);
525 dbus_message_iter_close_container(iter, &entry);
528 static const GMarkupParser msg_parser = {
536 static void message_listing_cb(struct obc_session *session,
537 struct obc_transfer *transfer,
538 GError *err, void *user_data)
540 struct map_data *map = user_data;
541 struct map_parser *parser;
542 GMarkupParseContext *ctxt;
544 DBusMessageIter iter, array;
550 reply = g_dbus_create_error(map->msg,
551 ERROR_INTERFACE ".Failed",
556 perr = obc_transfer_get_contents(transfer, &contents, &size);
558 reply = g_dbus_create_error(map->msg,
559 ERROR_INTERFACE ".Failed",
560 "Error reading contents: %s",
565 reply = dbus_message_new_method_return(map->msg);
569 dbus_message_iter_init_append(reply, &iter);
570 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
571 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
572 DBUS_TYPE_OBJECT_PATH_AS_STRING
573 DBUS_TYPE_ARRAY_AS_STRING
574 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
575 DBUS_TYPE_STRING_AS_STRING
576 DBUS_TYPE_VARIANT_AS_STRING
577 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
578 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
581 parser = g_new(struct map_parser, 1);
583 parser->iter = &array;
585 ctxt = g_markup_parse_context_new(&msg_parser, 0, parser, NULL);
586 g_markup_parse_context_parse(ctxt, contents, size, NULL);
587 g_markup_parse_context_free(ctxt);
588 dbus_message_iter_close_container(&iter, &array);
593 g_dbus_send_message(conn, reply);
594 dbus_message_unref(map->msg);
597 static DBusMessage *map_get_message_listing(DBusConnection *connection,
598 DBusMessage *message, void *user_data)
600 struct map_data *map = user_data;
601 struct obc_transfer *transfer;
603 DBusMessageIter msg_iter;
607 dbus_message_iter_init(message, &msg_iter);
609 if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_STRING)
610 return g_dbus_create_error(message,
611 ERROR_INTERFACE ".InvalidArguments", NULL);
613 dbus_message_iter_get_basic(&msg_iter, &folder);
615 transfer = obc_transfer_get("x-bt/MAP-msg-listing", folder, NULL, &err);
616 if (transfer == NULL)
619 if (obc_session_queue(map->session, transfer, message_listing_cb, map,
621 map->msg = dbus_message_ref(message);
626 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
632 static const GDBusMethodTable map_methods[] = {
633 { GDBUS_ASYNC_METHOD("SetFolder",
634 GDBUS_ARGS({ "name", "s" }), NULL,
636 { GDBUS_ASYNC_METHOD("GetFolderListing",
637 GDBUS_ARGS({ "filter", "a{ss}" }),
638 GDBUS_ARGS({ "content", "aa{sv}" }),
639 map_get_folder_listing) },
640 { GDBUS_ASYNC_METHOD("GetMessageListing",
641 GDBUS_ARGS({ "folder", "s" }, { "filter", "a{ss}" }),
642 GDBUS_ARGS({ "messages", "a{oa{sv}}" }),
643 map_get_message_listing) },
647 static void map_msg_remove(void *data)
649 struct map_msg *msg = data;
654 g_dbus_unregister_interface(conn, path, MAP_MSG_INTERFACE);
658 static void map_free(void *data)
660 struct map_data *map = data;
662 obc_session_unref(map->session);
663 g_hash_table_unref(map->messages);
667 static int map_probe(struct obc_session *session)
669 struct map_data *map;
672 path = obc_session_get_path(session);
676 map = g_try_new0(struct map_data, 1);
680 map->session = obc_session_ref(session);
681 map->messages = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
684 if (!g_dbus_register_interface(conn, path, MAP_INTERFACE, map_methods,
685 NULL, NULL, map, map_free)) {
694 static void map_remove(struct obc_session *session)
696 const char *path = obc_session_get_path(session);
700 g_dbus_unregister_interface(conn, path, MAP_INTERFACE);
703 static struct obc_driver map = {
706 .target = OBEX_MAS_UUID,
707 .target_len = OBEX_MAS_UUID_LEN,
718 conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
722 err = obc_driver_register(&map);
724 dbus_connection_unref(conn);
736 dbus_connection_unref(conn);
739 obc_driver_unregister(&map);