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
33 #include <sys/types.h>
35 #include <gobex/gobex.h>
36 #include <gobex/gobex-apparam.h>
44 #include "filesystem.h"
50 #define READ_STATUS_REQ 0
51 #define DELETE_STATUS_REQ 1
53 /* Channel number according to bluez doc/assigned-numbers.txt */
54 #define MAS_CHANNEL 16
56 #define MAS_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
58 <attribute id=\"0x0001\"> \
60 <uuid value=\"0x1132\"/> \
64 <attribute id=\"0x0004\"> \
67 <uuid value=\"0x0100\"/> \
70 <uuid value=\"0x0003\"/> \
71 <uint8 value=\"%u\" name=\"channel\"/> \
74 <uuid value=\"0x0008\"/> \
79 <attribute id=\"0x0009\"> \
82 <uuid value=\"0x1134\"/> \
83 <uint16 value=\"0x0100\" name=\"version\"/> \
88 <attribute id=\"0x0100\"> \
89 <text value=\"%s\" name=\"name\"/> \
92 <attribute id=\"0x0315\"> \
93 <uint8 value=\"0x00\"/> \
96 <attribute id=\"0x0316\"> \
97 <uint8 value=\"0x0F\"/> \
101 #define XML_DECL "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
103 /* Building blocks for x-obex/folder-listing */
104 #define FL_DTD "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\">"
105 #define FL_BODY_BEGIN "<folder-listing version=\"1.0\">"
106 #define FL_BODY_EMPTY "<folder-listing version=\"1.0\"/>"
107 #define FL_PARENT_FOLDER_ELEMENT "<parent-folder/>"
108 #define FL_FOLDER_ELEMENT "<folder name=\"%s\"/>"
109 #define FL_BODY_END "</folder-listing>"
111 #define ML_BODY_BEGIN "<MAP-msg-listing version=\"1.0\">"
112 #define ML_BODY_END "</MAP-msg-listing>"
115 struct mas_request *request;
120 GObexApparam *inparams;
121 GObexApparam *outparams;
125 static const uint8_t MAS_TARGET[TARGET_SIZE] = {
126 0xbb, 0x58, 0x2b, 0x40, 0x42, 0x0c, 0x11, 0xdb,
127 0xb0, 0xde, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 };
129 static int get_params(struct obex_session *os, struct mas_session *mas)
131 const uint8_t *buffer;
134 size = obex_get_apparam(os, &buffer);
138 mas->inparams = g_obex_apparam_decode(buffer, size);
139 if (mas->inparams == NULL) {
140 DBG("Error when parsing parameters!");
147 static void reset_request(struct mas_session *mas)
150 g_string_free(mas->buffer, TRUE);
155 g_obex_apparam_free(mas->inparams);
156 mas->inparams = NULL;
159 if (mas->outparams) {
160 g_obex_apparam_free(mas->outparams);
161 mas->outparams = NULL;
164 mas->nth_call = FALSE;
165 mas->finished = FALSE;
166 mas->ap_sent = FALSE;
169 static void mas_clean(struct mas_session *mas)
175 static void *mas_connect(struct obex_session *os, int *err)
177 struct mas_session *mas;
181 mas = g_new0(struct mas_session, 1);
183 *err = messages_connect(&mas->backend_data);
187 manager_register_session(os);
197 static void mas_disconnect(struct obex_session *os, void *user_data)
199 struct mas_session *mas = user_data;
203 manager_unregister_session(os);
204 messages_disconnect(mas->backend_data);
209 static int mas_get(struct obex_session *os, void *user_data)
211 struct mas_session *mas = user_data;
212 const char *type = obex_get_type(os);
213 const char *name = obex_get_name(os);
216 DBG("GET: name %s type %s mas %p",
222 ret = get_params(os, mas);
226 ret = obex_get_stream_start(os, name);
238 static int mas_put(struct obex_session *os, void *user_data)
240 struct mas_session *mas = user_data;
241 const char *type = obex_get_type(os);
242 const char *name = obex_get_name(os);
245 DBG("PUT: name %s type %s mas %p", name, type, mas);
250 ret = get_params(os, mas);
254 ret = obex_put_stream_start(os, name);
266 /* FIXME: Preserve whitespaces */
267 static void g_string_append_escaped_printf(GString *string,
268 const gchar *format, ...)
273 va_start(ap, format);
274 escaped = g_markup_vprintf_escaped(format, ap);
275 g_string_append(string, escaped);
280 static const char *yesorno(gboolean a)
288 static void get_messages_listing_cb(void *session, int err, uint16_t size,
290 const struct messages_message *entry,
293 struct mas_session *mas = user_data;
296 if (err < 0 && err != -EAGAIN) {
297 obex_object_set_io_flags(mas, G_IO_ERR, err);
301 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
305 mas->finished = TRUE;
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 mas->outparams = g_obex_apparam_set_uint16(mas->outparams,
403 MAP_AP_MESSAGESLISTINGSIZE,
405 mas->outparams = g_obex_apparam_set_uint8(mas->outparams,
411 obex_object_set_io_flags(mas, G_IO_IN, 0);
414 static void get_message_cb(void *session, int err, gboolean fmore,
415 const char *chunk, void *user_data)
417 struct mas_session *mas = user_data;
421 if (err < 0 && err != -EAGAIN) {
422 obex_object_set_io_flags(mas, G_IO_ERR, err);
427 mas->finished = TRUE;
431 g_string_append(mas->buffer, chunk);
435 obex_object_set_io_flags(mas, G_IO_IN, 0);
438 static void get_folder_listing_cb(void *session, int err, uint16_t size,
439 const char *name, void *user_data)
441 struct mas_session *mas = user_data;
444 if (err < 0 && err != -EAGAIN) {
445 obex_object_set_io_flags(mas, G_IO_ERR, err);
449 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
453 mas->outparams = g_obex_apparam_set_uint16(
455 MAP_AP_FOLDERLISTINGSIZE,
459 mas->finished = TRUE;
464 if (!mas->nth_call) {
465 g_string_append(mas->buffer, XML_DECL);
466 g_string_append(mas->buffer, FL_DTD);
468 g_string_append(mas->buffer, FL_BODY_EMPTY);
469 mas->finished = TRUE;
472 g_string_append(mas->buffer, FL_BODY_BEGIN);
473 mas->nth_call = TRUE;
477 g_string_append(mas->buffer, FL_BODY_END);
478 mas->finished = TRUE;
482 if (g_strcmp0(name, "..") == 0)
483 g_string_append(mas->buffer, FL_PARENT_FOLDER_ELEMENT);
485 g_string_append_escaped_printf(mas->buffer, FL_FOLDER_ELEMENT,
490 obex_object_set_io_flags(mas, G_IO_IN, err);
493 static void set_status_cb(void *session, int err, void *user_data)
495 struct mas_session *mas = user_data;
499 mas->finished = TRUE;
502 obex_object_set_io_flags(mas, G_IO_ERR, err);
504 obex_object_set_io_flags(mas, G_IO_OUT, 0);
507 static int mas_setpath(struct obex_session *os, void *user_data)
510 const uint8_t *nonhdr;
511 struct mas_session *mas = user_data;
513 if (obex_get_non_header_data(os, &nonhdr) != 2) {
514 error("Set path failed: flag and constants not found!");
518 name = obex_get_name(os);
520 DBG("SETPATH: name %s nonhdr 0x%x%x", name, nonhdr[0], nonhdr[1]);
522 if ((nonhdr[0] & 0x02) != 0x02) {
523 DBG("Error: requested directory creation");
527 return messages_set_folder(mas->backend_data, name, nonhdr[0] & 0x01);
530 static void *folder_listing_open(const char *name, int oflag, mode_t mode,
531 void *driver_data, size_t *size, int *err)
533 struct mas_session *mas = driver_data;
534 /* 1024 is the default when there was no MaxListCount sent */
538 if (oflag != O_RDONLY) {
543 DBG("name = %s", name);
545 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
546 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset);
548 *err = messages_get_folder_listing(mas->backend_data, name, max,
549 offset, get_folder_listing_cb, mas);
551 mas->buffer = g_string_new("");
559 static void *msg_listing_open(const char *name, int oflag, mode_t mode,
560 void *driver_data, size_t *size, int *err)
562 struct mas_session *mas = driver_data;
563 struct messages_filter filter = { 0, };
564 /* 1024 is the default when there was no MaxListCount sent */
567 /* If MAP client does not specify the subject length,
568 then subject_len = 0 and subject should be sent unaltered. */
569 uint8_t subject_len = 0;
573 if (oflag != O_RDONLY) {
578 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
579 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset);
580 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_SUBJECTLENGTH,
583 g_obex_apparam_get_uint32(mas->inparams, MAP_AP_PARAMETERMASK,
584 &filter.parameter_mask);
585 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERMESSAGETYPE,
587 filter.period_begin = g_obex_apparam_get_string(mas->inparams,
588 MAP_AP_FILTERPERIODBEGIN);
589 filter.period_end = g_obex_apparam_get_string(mas->inparams,
590 MAP_AP_FILTERPERIODEND);
591 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERREADSTATUS,
592 &filter.read_status);
593 filter.recipient = g_obex_apparam_get_string(mas->inparams,
594 MAP_AP_FILTERRECIPIENT);
595 filter.originator = g_obex_apparam_get_string(mas->inparams,
596 MAP_AP_FILTERORIGINATOR);
597 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERPRIORITY,
600 *err = messages_get_messages_listing(mas->backend_data, name, max,
601 offset, subject_len, &filter,
602 get_messages_listing_cb, mas);
604 mas->buffer = g_string_new("");
612 static void *message_open(const char *name, int oflag, mode_t mode,
613 void *driver_data, size_t *size, int *err)
615 struct mas_session *mas = driver_data;
619 if (oflag != O_RDONLY) {
620 DBG("Message pushing unsupported");
626 *err = messages_get_message(mas->backend_data, name, 0,
627 get_message_cb, mas);
629 mas->buffer = g_string_new("");
637 static void *message_update_open(const char *name, int oflag, mode_t mode,
638 void *driver_data, size_t *size,
641 struct mas_session *mas = driver_data;
645 if (oflag == O_RDONLY) {
650 *err = messages_update_inbox(mas->backend_data, set_status_cb, mas);
657 static void *message_set_status_open(const char *name, int oflag, mode_t mode,
658 void *driver_data, size_t *size,
662 struct mas_session *mas = driver_data;
668 if (oflag == O_RDONLY) {
673 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSINDICATOR,
679 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSVALUE,
685 if (indicator == READ_STATUS_REQ)
686 *err = messages_set_read(mas->backend_data, name, value,
688 else if (indicator == DELETE_STATUS_REQ)
689 *err = messages_set_delete(mas->backend_data, name, value,
700 static ssize_t any_get_next_header(void *object, void *buf, size_t mtu,
703 struct mas_session *mas = object;
707 if (mas->buffer->len == 0 && !mas->finished)
710 *hi = G_OBEX_HDR_APPARAM;
716 return g_obex_apparam_encode(mas->outparams, buf, mtu);
719 static void *any_open(const char *name, int oflag, mode_t mode,
720 void *driver_data, size_t *size, int *err)
729 static ssize_t any_write(void *object, const void *buf, size_t count)
736 static ssize_t any_read(void *obj, void *buf, size_t count)
738 struct mas_session *mas = obj;
743 len = string_read(mas->buffer, buf, count);
745 if (len == 0 && !mas->finished)
751 static int any_close(void *obj)
753 struct mas_session *mas = obj;
758 messages_abort(mas->backend_data);
765 static struct obex_service_driver mas = {
766 .name = "Message Access server",
768 .channel = MAS_CHANNEL,
770 .record = MAS_RECORD,
771 .target = MAS_TARGET,
772 .target_size = TARGET_SIZE,
773 .connect = mas_connect,
776 .setpath = mas_setpath,
777 .disconnect = mas_disconnect,
780 static struct obex_mime_type_driver mime_map = {
781 .target = MAS_TARGET,
782 .target_size = TARGET_SIZE,
790 static struct obex_mime_type_driver mime_message = {
791 .target = MAS_TARGET,
792 .target_size = TARGET_SIZE,
793 .mimetype = "x-bt/message",
794 .open = message_open,
800 static struct obex_mime_type_driver mime_folder_listing = {
801 .target = MAS_TARGET,
802 .target_size = TARGET_SIZE,
803 .mimetype = "x-obex/folder-listing",
804 .get_next_header = any_get_next_header,
805 .open = folder_listing_open,
811 static struct obex_mime_type_driver mime_msg_listing = {
812 .target = MAS_TARGET,
813 .target_size = TARGET_SIZE,
814 .mimetype = "x-bt/MAP-msg-listing",
815 .get_next_header = any_get_next_header,
816 .open = msg_listing_open,
822 static struct obex_mime_type_driver mime_notification_registration = {
823 .target = MAS_TARGET,
824 .target_size = TARGET_SIZE,
825 .mimetype = "x-bt/MAP-NotificationRegistration",
832 static struct obex_mime_type_driver mime_message_status = {
833 .target = MAS_TARGET,
834 .target_size = TARGET_SIZE,
835 .mimetype = "x-bt/messageStatus",
836 .open = message_set_status_open,
842 static struct obex_mime_type_driver mime_message_update = {
843 .target = MAS_TARGET,
844 .target_size = TARGET_SIZE,
845 .mimetype = "x-bt/MAP-messageUpdate",
846 .open = message_update_open,
852 static struct obex_mime_type_driver *map_drivers[] = {
855 &mime_folder_listing,
857 &mime_notification_registration,
858 &mime_message_status,
859 &mime_message_update,
863 static int mas_init(void)
868 err = messages_init();
872 for (i = 0; map_drivers[i] != NULL; ++i) {
873 err = obex_mime_type_driver_register(map_drivers[i]);
878 err = obex_service_driver_register(&mas);
885 for (--i; i >= 0; --i)
886 obex_mime_type_driver_unregister(map_drivers[i]);
893 static void mas_exit(void)
897 obex_service_driver_unregister(&mas);
899 for (i = 0; map_drivers[i] != NULL; ++i)
900 obex_mime_type_driver_unregister(map_drivers[i]);
905 OBEX_PLUGIN_DEFINE(mas, mas_init, mas_exit)