5 * Copyright (C) 2010-2011 Nokia Corporation
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
39 #include "filesystem.h"
45 #include "bmessage-parser.h"
48 /* Channel number according to bluez doc/assigned-numbers.txt */
49 #define MAS_CHANNEL 16
51 #define MAS_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
53 <attribute id=\"0x0001\"> \
55 <uuid value=\"0x1132\"/> \
59 <attribute id=\"0x0004\"> \
62 <uuid value=\"0x0100\"/> \
65 <uuid value=\"0x0003\"/> \
66 <uint8 value=\"%u\" name=\"channel\"/> \
69 <uuid value=\"0x0008\"/> \
74 <attribute id=\"0x0009\"> \
77 <uuid value=\"0x1134\"/> \
78 <uint16 value=\"0x0100\" name=\"version\"/> \
83 <attribute id=\"0x0100\"> \
84 <text value=\"%s\" name=\"name\"/> \
87 <attribute id=\"0x0315\"> \
88 <uint8 value=\"0x00\"/> \
91 <attribute id=\"0x0316\"> \
92 <uint8 value=\"0x0F\"/> \
96 #define XML_DECL "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
98 /* Building blocks for x-obex/folder-listing */
99 #define FL_DTD "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\">"
100 #define FL_BODY_BEGIN "<folder-listing version=\"1.0\">"
101 #define FL_BODY_EMPTY "<folder-listing version=\"1.0\"/>"
102 #define FL_PARENT_FOLDER_ELEMENT "<parent-folder/>"
103 #define FL_FOLDER_ELEMENT "<folder name=\"%s\"/>"
104 #define FL_BODY_END "</folder-listing>"
106 #define ML_BODY_BEGIN "<MAP-msg-listing version=\"1.0\">"
107 #define ML_BODY_END "</MAP-msg-listing>"
110 struct mas_request *request;
120 static const uint8_t MAS_TARGET[TARGET_SIZE] = {
121 0xbb, 0x58, 0x2b, 0x40, 0x42, 0x0c, 0x11, 0xdb,
122 0xb0, 0xde, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 };
125 static void bmessage_parser(struct mas_session *mas,
126 struct bmessage_property *bmessage)
128 /* STRING PARSER : Under implementation */
129 DBG("%s", mas->buffer->str);
133 static void reset_request(struct mas_session *mas)
136 g_string_free(mas->buffer, TRUE);
140 map_ap_free(mas->ap);
143 mas->nth_call = FALSE;
144 mas->finished = FALSE;
147 static void mas_clean(struct mas_session *mas)
153 static void *mas_connect(struct obex_session *os, int *err)
155 struct mas_session *mas;
159 mas = g_new0(struct mas_session, 1);
161 *err = messages_connect(&mas->backend_data);
165 manager_register_session(os);
175 static void mas_disconnect(struct obex_session *os, void *user_data)
177 struct mas_session *mas = user_data;
181 manager_unregister_session(os);
182 messages_disconnect(mas->backend_data);
187 static int mas_get(struct obex_session *os, void *user_data)
189 struct mas_session *mas = user_data;
190 const char *type = obex_get_type(os);
191 const char *name = obex_get_name(os);
194 const uint8_t *buffer = NULL;
198 DBG("GET: name %s type %s mas %p",
205 rsize = obex_get_apparam(os, &buffer);
208 if (g_ascii_strcasecmp(type, "x-bt/message") == 0) {
213 mas->ap = map_ap_decode(buffer, rsize);
214 if (mas->ap == NULL) {
220 ret = obex_get_stream_start(os, name);
232 static int mas_put(struct obex_session *os, void *user_data)
234 struct mas_session *mas = user_data;
235 const char *type = obex_get_type(os);
236 const char *name = obex_get_name(os);
239 const uint8_t *buffer = NULL;
243 DBG("PUT: name %s type %s mas %p", name, type, mas);
249 rsize = obex_get_apparam(os, &buffer);
251 if (g_ascii_strcasecmp(type, "x-bt/messageStatus") == 0 ||
252 g_ascii_strcasecmp(type, "x-bt/message") == 0) {
257 mas->ap = map_ap_decode(buffer, rsize);
258 if (mas->ap == NULL) {
264 ret = obex_put_stream_start(os, name);
276 /* FIXME: Preserve whitespaces */
277 static void g_string_append_escaped_printf(GString *string,
278 const gchar *format, ...)
283 va_start(ap, format);
284 escaped = g_markup_vprintf_escaped(format, ap);
285 g_string_append(string, escaped);
290 static const char *yesorno(gboolean a)
298 static void get_messages_listing_cb(void *session, int err, uint16_t size,
300 const struct messages_message *entry,
303 struct mas_session *mas = user_data;
305 if (err < 0 && err != -EAGAIN) {
306 obex_object_set_io_flags(mas, G_IO_ERR, err);
310 if (!mas->nth_call) {
311 g_string_append(mas->buffer, ML_BODY_BEGIN);
312 mas->nth_call = TRUE;
316 g_string_append(mas->buffer, ML_BODY_END);
317 mas->finished = TRUE;
322 g_string_append(mas->buffer, "<msg");
324 g_string_append_escaped_printf(mas->buffer, " handle=\"%s\"",
327 if (entry->mask & PMASK_SUBJECT)
328 g_string_append_escaped_printf(mas->buffer, " subject=\"%s\"",
331 if (entry->mask & PMASK_DATETIME)
332 g_string_append_escaped_printf(mas->buffer, " datetime=\"%s\"",
335 if (entry->mask & PMASK_SENDER_NAME)
336 g_string_append_escaped_printf(mas->buffer,
337 " sender_name=\"%s\"",
340 if (entry->mask & PMASK_SENDER_ADDRESSING)
341 g_string_append_escaped_printf(mas->buffer,
342 " sender_addressing=\"%s\"",
343 entry->sender_addressing);
345 if (entry->mask & PMASK_REPLYTO_ADDRESSING)
346 g_string_append_escaped_printf(mas->buffer,
347 " replyto_addressing=\"%s\"",
348 entry->replyto_addressing);
350 if (entry->mask & PMASK_RECIPIENT_NAME)
351 g_string_append_escaped_printf(mas->buffer,
352 " recipient_name=\"%s\"",
353 entry->recipient_name);
355 if (entry->mask & PMASK_RECIPIENT_ADDRESSING)
356 g_string_append_escaped_printf(mas->buffer,
357 " recipient_addressing=\"%s\"",
358 entry->recipient_addressing);
360 if (entry->mask & PMASK_TYPE)
361 g_string_append_escaped_printf(mas->buffer, " type=\"%s\"",
364 if (entry->mask & PMASK_RECEPTION_STATUS)
365 g_string_append_escaped_printf(mas->buffer,
366 " reception_status=\"%s\"",
367 entry->reception_status);
369 if (entry->mask & PMASK_SIZE)
370 g_string_append_escaped_printf(mas->buffer, " size=\"%s\"",
373 if (entry->mask & PMASK_ATTACHMENT_SIZE)
374 g_string_append_escaped_printf(mas->buffer,
375 " attachment_size=\"%s\"",
376 entry->attachment_size);
378 if (entry->mask & PMASK_TEXT)
379 g_string_append_escaped_printf(mas->buffer, " text=\"%s\"",
380 yesorno(entry->text));
382 if (entry->mask & PMASK_READ)
383 g_string_append_escaped_printf(mas->buffer, " read=\"%s\"",
384 yesorno(entry->read));
386 if (entry->mask & PMASK_SENT)
387 g_string_append_escaped_printf(mas->buffer, " sent=\"%s\"",
388 yesorno(entry->sent));
390 if (entry->mask & PMASK_PROTECTED)
391 g_string_append_escaped_printf(mas->buffer, " protected=\"%s\"",
392 yesorno(entry->protect));
394 if (entry->mask & PMASK_PRIORITY)
395 g_string_append_escaped_printf(mas->buffer, " priority=\"%s\"",
396 yesorno(entry->priority));
398 g_string_append(mas->buffer, "/>\n");
402 obex_object_set_io_flags(mas, G_IO_IN, 0);
405 static void get_message_cb(void *session, int err, gboolean fmore,
406 const char *chunk, void *user_data)
408 struct mas_session *mas = user_data;
412 if (err < 0 && err != -EAGAIN) {
413 obex_object_set_io_flags(mas, G_IO_ERR, err);
418 mas->finished = TRUE;
422 g_string_append(mas->buffer, chunk);
426 obex_object_set_io_flags(mas, G_IO_IN, 0);
429 static void get_folder_listing_cb(void *session, int err, uint16_t size,
430 const char *name, void *user_data)
432 struct mas_session *mas = user_data;
434 if (err < 0 && err != -EAGAIN) {
435 obex_object_set_io_flags(mas, G_IO_ERR, err);
439 if (!mas->nth_call) {
440 g_string_append(mas->buffer, XML_DECL);
441 g_string_append(mas->buffer, FL_DTD);
443 g_string_append(mas->buffer, FL_BODY_EMPTY);
444 mas->finished = TRUE;
447 g_string_append(mas->buffer, FL_BODY_BEGIN);
448 mas->nth_call = TRUE;
452 g_string_append(mas->buffer, FL_BODY_END);
453 mas->finished = TRUE;
457 if (g_strcmp0(name, "..") == 0)
458 g_string_append(mas->buffer, FL_PARENT_FOLDER_ELEMENT);
460 g_string_append_escaped_printf(mas->buffer, FL_FOLDER_ELEMENT,
465 obex_object_set_io_flags(mas, G_IO_IN, err);
468 static void update_inbox_cb(void *session, int err, void *user_data)
470 struct mas_session *mas = user_data;
474 mas->finished = TRUE;
477 obex_object_set_io_flags(mas, G_IO_ERR, err);
479 obex_object_set_io_flags(mas, G_IO_OUT, 0);
483 static void message_status_cb(void *session, int err, void *user_data)
485 struct mas_session *mas = user_data;
489 mas->finished = TRUE;
492 obex_object_set_io_flags(mas, G_IO_ERR, err);
494 obex_object_set_io_flags(mas, G_IO_OUT, 0);
497 static void folder_size_result_cb(void *session, int err, uint16_t size,
498 const char *name, void *user_data)
500 struct mas_session *mas = user_data;
502 map_ap_set_u16(mas->ap, MAP_AP_FOLDERLISTINGSIZE,
503 GUINT16_FROM_BE(size));
506 obex_object_set_io_flags(mas, G_IO_ERR, err);
508 obex_object_set_io_flags(mas, G_IO_OUT, 0);
512 static int mas_setpath(struct obex_session *os, void *user_data)
515 const uint8_t *nonhdr;
516 struct mas_session *mas = user_data;
518 if (obex_get_non_header_data(os, &nonhdr) != 2) {
519 error("Set path failed: flag and constants not found!");
523 name = obex_get_name(os);
525 DBG("SETPATH: name %s nonhdr 0x%x%x", name, nonhdr[0], nonhdr[1]);
527 if ((nonhdr[0] & 0x02) != 0x02) {
528 DBG("Error: requested directory creation");
532 return messages_set_folder(mas->backend_data, name, nonhdr[0] & 0x01);
535 static void *folder_listing_open(const char *name, int oflag, mode_t mode,
536 void *driver_data, size_t *size, int *err)
538 struct mas_session *mas = driver_data;
540 messages_folder_listing_cb cb;
541 uint16_t maxlistcount = 1024;
545 if (oflag != O_RDONLY) {
550 DBG("name = %s", name);
553 if (mas->ap != NULL) {
554 map_ap_get_u16(mas->ap, MAP_AP_MAXLISTCOUNT, &maxlistcount);
555 map_ap_get_u16(mas->ap, MAP_AP_STARTOFFSET, &offset);
558 DBG("Maxlistcount = %d \n offset = %d\n", maxlistcount, offset);
560 if (maxlistcount == 0)
561 cb = folder_size_result_cb;
563 cb = get_folder_listing_cb;
565 *err = messages_get_folder_listing(mas->backend_data, name,
566 maxlistcount, offset, cb, mas);
568 /* 1024 is the default when there was no MaxListCount sent */
569 *err = messages_get_folder_listing(mas->backend_data, name, 1024, 0,
570 get_folder_listing_cb, mas);
573 mas->buffer = g_string_new("");
581 static void *msg_listing_open(const char *name, int oflag, mode_t mode,
582 void *driver_data, size_t *size, int *err)
584 struct mas_session *mas = driver_data;
585 struct messages_filter filter = { 0, };
589 if (oflag != O_RDONLY) {
594 *err = messages_get_messages_listing(mas->backend_data, name, 0xffff, 0,
596 get_messages_listing_cb, mas);
598 mas->buffer = g_string_new("");
607 static void message_get(struct mas_session *mas, const char *name, int *err)
611 /* TODO: check for Mandatory application parameter
612 * Attachment and Charset and optional parameter fraction request */
613 *err = messages_get_message(mas->backend_data, name, 0,
614 get_message_cb, mas);
615 mas->buffer = g_string_new("");
618 static void message_put(struct mas_session *mas, const char *name, int *err)
622 /* TODO: check for Mandatory application parameter
623 * Charset and optional parameter transparent and retry */
624 mas->buffer = g_string_new("");
628 static void *message_open(const char *name, int oflag, mode_t mode,
629 void *driver_data, size_t *size, int *err)
631 struct mas_session *mas = driver_data;
636 if (oflag == O_RDONLY)
637 message_get(mas, name, err);
639 message_put(mas, name, err);
641 if (oflag != O_RDONLY) {
642 DBG("Message pushing unsupported");
648 *err = messages_get_message(mas->backend_data, name, 0,
649 get_message_cb, mas);
651 mas->buffer = g_string_new("");
659 static void *message_update_open(const char *name, int oflag, mode_t mode,
660 void *driver_data, size_t *size,
663 struct mas_session *mas = driver_data;
667 if (!(oflag & O_WRONLY)) {
672 *err = messages_update_inbox(mas->backend_data, update_inbox_cb, mas);
680 static void *message_status_open(const char *name, int oflag, mode_t mode,
681 void *driver_data, size_t *size,
685 struct mas_session *mas = driver_data;
689 if (!(oflag & O_WRONLY)) {
694 map_ap_get_u8(mas->ap, MAP_AP_STATUSINDICATOR, &indicator);
695 map_ap_get_u8(mas->ap, MAP_AP_STATUSVALUE, &value);
697 DBG("Indicator = %d \n value = %d\n", indicator, value);
699 *err = messages_set_message_status(mas->backend_data, name, indicator,
700 value, message_status_cb, mas);
708 static ssize_t message_write(void *object, const void *buf, size_t count)
710 struct mas_session *mas = object;
715 string = g_string_append_len(mas->buffer, buf, count);
720 static int message_close(void *obj)
722 struct mas_session *mas = obj;
723 struct bmessage_property bmessage = { 0, };
727 /* Bmessage body parsing */
728 bmessage_parser(mas, &bmessage);
731 messages_abort(mas->backend_data);
739 static void *any_open(const char *name, int oflag, mode_t mode,
740 void *driver_data, size_t *size, int *err)
749 static ssize_t any_write(void *object, const void *buf, size_t count)
756 static ssize_t any_read(void *obj, void *buf, size_t count)
758 struct mas_session *mas = obj;
763 len = string_read(mas->buffer, buf, count);
765 if (len == 0 && !mas->finished)
771 static int any_close(void *obj)
773 struct mas_session *mas = obj;
778 messages_abort(mas->backend_data);
785 static struct obex_service_driver mas = {
786 .name = "Message Access server",
788 .channel = MAS_CHANNEL,
790 .record = MAS_RECORD,
791 .target = MAS_TARGET,
792 .target_size = TARGET_SIZE,
793 .connect = mas_connect,
796 .setpath = mas_setpath,
797 .disconnect = mas_disconnect,
800 static struct obex_mime_type_driver mime_map = {
801 .target = MAS_TARGET,
802 .target_size = TARGET_SIZE,
810 static struct obex_mime_type_driver mime_message = {
811 .target = MAS_TARGET,
812 .target_size = TARGET_SIZE,
813 .mimetype = "x-bt/message",
814 .open = message_open,
816 .close = message_close,
822 .write = message_write,
828 static struct obex_mime_type_driver mime_folder_listing = {
829 .target = MAS_TARGET,
830 .target_size = TARGET_SIZE,
831 .mimetype = "x-obex/folder-listing",
832 .open = folder_listing_open,
838 static struct obex_mime_type_driver mime_msg_listing = {
839 .target = MAS_TARGET,
840 .target_size = TARGET_SIZE,
841 .mimetype = "x-bt/MAP-msg-listing",
842 .open = msg_listing_open,
848 static struct obex_mime_type_driver mime_notification_registration = {
849 .target = MAS_TARGET,
850 .target_size = TARGET_SIZE,
851 .mimetype = "x-bt/MAP-NotificationRegistration",
858 static struct obex_mime_type_driver mime_message_status = {
859 .target = MAS_TARGET,
860 .target_size = TARGET_SIZE,
861 .mimetype = "x-bt/messageStatus",
863 .open = message_status_open,
872 static struct obex_mime_type_driver mime_message_update = {
873 .target = MAS_TARGET,
874 .target_size = TARGET_SIZE,
875 .mimetype = "x-bt/MAP-messageUpdate",
876 .open = message_update_open,
882 static struct obex_mime_type_driver *map_drivers[] = {
885 &mime_folder_listing,
887 &mime_notification_registration,
888 &mime_message_status,
889 &mime_message_update,
893 static int mas_init(void)
898 err = messages_init();
902 for (i = 0; map_drivers[i] != NULL; ++i) {
903 err = obex_mime_type_driver_register(map_drivers[i]);
908 err = obex_service_driver_register(&mas);
915 for (--i; i >= 0; --i)
916 obex_mime_type_driver_unregister(map_drivers[i]);
923 static void mas_exit(void)
927 obex_service_driver_unregister(&mas);
929 for (i = 0; map_drivers[i] != NULL; ++i)
930 obex_mime_type_driver_unregister(map_drivers[i]);
935 OBEX_PLUGIN_DEFINE(mas, mas_init, mas_exit)