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
34 #include <gobex/gobex.h>
35 #include <gobex/gobex-apparam.h>
43 #include "filesystem.h"
49 #define READ_STATUS_REQ 0
50 #define DELETE_STATUS_REQ 1
52 /* Channel number according to bluez doc/assigned-numbers.txt */
53 #define MAS_CHANNEL 16
55 #define MAS_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
57 <attribute id=\"0x0001\"> \
59 <uuid value=\"0x1132\"/> \
63 <attribute id=\"0x0004\"> \
66 <uuid value=\"0x0100\"/> \
69 <uuid value=\"0x0003\"/> \
70 <uint8 value=\"%u\" name=\"channel\"/> \
73 <uuid value=\"0x0008\"/> \
78 <attribute id=\"0x0009\"> \
81 <uuid value=\"0x1134\"/> \
82 <uint16 value=\"0x0100\" name=\"version\"/> \
87 <attribute id=\"0x0100\"> \
88 <text value=\"%s\" name=\"name\"/> \
91 <attribute id=\"0x0315\"> \
92 <uint8 value=\"0x00\"/> \
95 <attribute id=\"0x0316\"> \
96 <uint8 value=\"0x0F\"/> \
100 #define XML_DECL "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
102 /* Building blocks for x-obex/folder-listing */
103 #define FL_DTD "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\">"
104 #define FL_BODY_BEGIN "<folder-listing version=\"1.0\">"
105 #define FL_BODY_EMPTY "<folder-listing version=\"1.0\"/>"
106 #define FL_PARENT_FOLDER_ELEMENT "<parent-folder/>"
107 #define FL_FOLDER_ELEMENT "<folder name=\"%s\"/>"
108 #define FL_BODY_END "</folder-listing>"
110 #define ML_BODY_BEGIN "<MAP-msg-listing version=\"1.0\">"
111 #define ML_BODY_END "</MAP-msg-listing>"
114 struct mas_request *request;
119 GObexApparam *inparams;
120 GObexApparam *outparams;
124 static const uint8_t MAS_TARGET[TARGET_SIZE] = {
125 0xbb, 0x58, 0x2b, 0x40, 0x42, 0x0c, 0x11, 0xdb,
126 0xb0, 0xde, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 };
128 static int get_params(struct obex_session *os, struct mas_session *mas)
130 const uint8_t *buffer;
133 size = obex_get_apparam(os, &buffer);
137 mas->inparams = g_obex_apparam_decode(buffer, size);
138 if (mas->inparams == NULL) {
139 DBG("Error when parsing parameters!");
146 static void reset_request(struct mas_session *mas)
149 g_string_free(mas->buffer, TRUE);
154 g_obex_apparam_free(mas->inparams);
155 mas->inparams = NULL;
158 if (mas->outparams) {
159 g_obex_apparam_free(mas->outparams);
160 mas->outparams = NULL;
163 mas->nth_call = FALSE;
164 mas->finished = FALSE;
165 mas->ap_sent = FALSE;
168 static void mas_clean(struct mas_session *mas)
174 static void *mas_connect(struct obex_session *os, int *err)
176 struct mas_session *mas;
180 mas = g_new0(struct mas_session, 1);
182 *err = messages_connect(&mas->backend_data);
186 manager_register_session(os);
196 static void mas_disconnect(struct obex_session *os, void *user_data)
198 struct mas_session *mas = user_data;
202 manager_unregister_session(os);
203 messages_disconnect(mas->backend_data);
208 static int mas_get(struct obex_session *os, void *user_data)
210 struct mas_session *mas = user_data;
211 const char *type = obex_get_type(os);
212 const char *name = obex_get_name(os);
215 DBG("GET: name %s type %s mas %p",
221 ret = get_params(os, mas);
225 ret = obex_get_stream_start(os, name);
237 static int mas_put(struct obex_session *os, void *user_data)
239 struct mas_session *mas = user_data;
240 const char *type = obex_get_type(os);
241 const char *name = obex_get_name(os);
244 DBG("PUT: name %s type %s mas %p", name, type, mas);
249 ret = get_params(os, mas);
253 ret = obex_put_stream_start(os, name);
265 /* FIXME: Preserve whitespaces */
266 static void g_string_append_escaped_printf(GString *string,
267 const gchar *format, ...)
272 va_start(ap, format);
273 escaped = g_markup_vprintf_escaped(format, ap);
274 g_string_append(string, escaped);
279 static const char *yesorno(gboolean a)
287 static void get_messages_listing_cb(void *session, int err, uint16_t size,
289 const struct messages_message *entry,
292 struct mas_session *mas = user_data;
295 if (err < 0 && err != -EAGAIN) {
296 obex_object_set_io_flags(mas, G_IO_ERR, err);
300 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
304 mas->finished = TRUE;
309 if (!mas->nth_call) {
310 g_string_append(mas->buffer, ML_BODY_BEGIN);
311 mas->nth_call = TRUE;
315 g_string_append(mas->buffer, ML_BODY_END);
316 mas->finished = TRUE;
321 g_string_append(mas->buffer, "<msg");
323 g_string_append_escaped_printf(mas->buffer, " handle=\"%s\"",
326 if (entry->mask & PMASK_SUBJECT)
327 g_string_append_escaped_printf(mas->buffer, " subject=\"%s\"",
330 if (entry->mask & PMASK_DATETIME)
331 g_string_append_escaped_printf(mas->buffer, " datetime=\"%s\"",
334 if (entry->mask & PMASK_SENDER_NAME)
335 g_string_append_escaped_printf(mas->buffer,
336 " sender_name=\"%s\"",
339 if (entry->mask & PMASK_SENDER_ADDRESSING)
340 g_string_append_escaped_printf(mas->buffer,
341 " sender_addressing=\"%s\"",
342 entry->sender_addressing);
344 if (entry->mask & PMASK_REPLYTO_ADDRESSING)
345 g_string_append_escaped_printf(mas->buffer,
346 " replyto_addressing=\"%s\"",
347 entry->replyto_addressing);
349 if (entry->mask & PMASK_RECIPIENT_NAME)
350 g_string_append_escaped_printf(mas->buffer,
351 " recipient_name=\"%s\"",
352 entry->recipient_name);
354 if (entry->mask & PMASK_RECIPIENT_ADDRESSING)
355 g_string_append_escaped_printf(mas->buffer,
356 " recipient_addressing=\"%s\"",
357 entry->recipient_addressing);
359 if (entry->mask & PMASK_TYPE)
360 g_string_append_escaped_printf(mas->buffer, " type=\"%s\"",
363 if (entry->mask & PMASK_RECEPTION_STATUS)
364 g_string_append_escaped_printf(mas->buffer,
365 " reception_status=\"%s\"",
366 entry->reception_status);
368 if (entry->mask & PMASK_SIZE)
369 g_string_append_escaped_printf(mas->buffer, " size=\"%s\"",
372 if (entry->mask & PMASK_ATTACHMENT_SIZE)
373 g_string_append_escaped_printf(mas->buffer,
374 " attachment_size=\"%s\"",
375 entry->attachment_size);
377 if (entry->mask & PMASK_TEXT)
378 g_string_append_escaped_printf(mas->buffer, " text=\"%s\"",
379 yesorno(entry->text));
381 if (entry->mask & PMASK_READ)
382 g_string_append_escaped_printf(mas->buffer, " read=\"%s\"",
383 yesorno(entry->read));
385 if (entry->mask & PMASK_SENT)
386 g_string_append_escaped_printf(mas->buffer, " sent=\"%s\"",
387 yesorno(entry->sent));
389 if (entry->mask & PMASK_PROTECTED)
390 g_string_append_escaped_printf(mas->buffer, " protected=\"%s\"",
391 yesorno(entry->protect));
393 if (entry->mask & PMASK_PRIORITY)
394 g_string_append_escaped_printf(mas->buffer, " priority=\"%s\"",
395 yesorno(entry->priority));
397 g_string_append(mas->buffer, "/>\n");
401 mas->outparams = g_obex_apparam_set_uint16(mas->outparams,
402 MAP_AP_MESSAGESLISTINGSIZE,
404 mas->outparams = g_obex_apparam_set_uint8(mas->outparams,
410 obex_object_set_io_flags(mas, G_IO_IN, 0);
413 static void get_message_cb(void *session, int err, gboolean fmore,
414 const char *chunk, void *user_data)
416 struct mas_session *mas = user_data;
420 if (err < 0 && err != -EAGAIN) {
421 obex_object_set_io_flags(mas, G_IO_ERR, err);
426 mas->finished = TRUE;
430 g_string_append(mas->buffer, chunk);
434 obex_object_set_io_flags(mas, G_IO_IN, 0);
437 static void get_folder_listing_cb(void *session, int err, uint16_t size,
438 const char *name, void *user_data)
440 struct mas_session *mas = user_data;
443 if (err < 0 && err != -EAGAIN) {
444 obex_object_set_io_flags(mas, G_IO_ERR, err);
448 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
452 mas->outparams = g_obex_apparam_set_uint16(
454 MAP_AP_FOLDERLISTINGSIZE,
458 mas->finished = TRUE;
463 if (!mas->nth_call) {
464 g_string_append(mas->buffer, XML_DECL);
465 g_string_append(mas->buffer, FL_DTD);
467 g_string_append(mas->buffer, FL_BODY_EMPTY);
468 mas->finished = TRUE;
471 g_string_append(mas->buffer, FL_BODY_BEGIN);
472 mas->nth_call = TRUE;
476 g_string_append(mas->buffer, FL_BODY_END);
477 mas->finished = TRUE;
481 if (g_strcmp0(name, "..") == 0)
482 g_string_append(mas->buffer, FL_PARENT_FOLDER_ELEMENT);
484 g_string_append_escaped_printf(mas->buffer, FL_FOLDER_ELEMENT,
489 obex_object_set_io_flags(mas, G_IO_IN, err);
492 static void set_status_cb(void *session, int err, void *user_data)
494 struct mas_session *mas = user_data;
498 mas->finished = TRUE;
501 obex_object_set_io_flags(mas, G_IO_ERR, err);
503 obex_object_set_io_flags(mas, G_IO_OUT, 0);
506 static int mas_setpath(struct obex_session *os, void *user_data)
509 const uint8_t *nonhdr;
510 struct mas_session *mas = user_data;
512 if (obex_get_non_header_data(os, &nonhdr) != 2) {
513 error("Set path failed: flag and constants not found!");
517 name = obex_get_name(os);
519 DBG("SETPATH: name %s nonhdr 0x%x%x", name, nonhdr[0], nonhdr[1]);
521 if ((nonhdr[0] & 0x02) != 0x02) {
522 DBG("Error: requested directory creation");
526 return messages_set_folder(mas->backend_data, name, nonhdr[0] & 0x01);
529 static void *folder_listing_open(const char *name, int oflag, mode_t mode,
530 void *driver_data, size_t *size, int *err)
532 struct mas_session *mas = driver_data;
533 /* 1024 is the default when there was no MaxListCount sent */
537 if (oflag != O_RDONLY) {
542 DBG("name = %s", name);
544 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
545 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset);
547 *err = messages_get_folder_listing(mas->backend_data, name, max,
548 offset, get_folder_listing_cb, mas);
550 mas->buffer = g_string_new("");
558 static void *msg_listing_open(const char *name, int oflag, mode_t mode,
559 void *driver_data, size_t *size, int *err)
561 struct mas_session *mas = driver_data;
562 struct messages_filter filter = { 0, };
563 /* 1024 is the default when there was no MaxListCount sent */
566 /* If MAP client does not specify the subject length,
567 then subject_len = 0 and subject should be sent unaltered. */
568 uint8_t subject_len = 0;
572 if (oflag != O_RDONLY) {
577 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
578 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset);
579 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_SUBJECTLENGTH,
582 g_obex_apparam_get_uint32(mas->inparams, MAP_AP_PARAMETERMASK,
583 &filter.parameter_mask);
584 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERMESSAGETYPE,
586 filter.period_begin = g_obex_apparam_get_string(mas->inparams,
587 MAP_AP_FILTERPERIODBEGIN);
588 filter.period_end = g_obex_apparam_get_string(mas->inparams,
589 MAP_AP_FILTERPERIODEND);
590 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERREADSTATUS,
591 &filter.read_status);
592 filter.recipient = g_obex_apparam_get_string(mas->inparams,
593 MAP_AP_FILTERRECIPIENT);
594 filter.originator = g_obex_apparam_get_string(mas->inparams,
595 MAP_AP_FILTERORIGINATOR);
596 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERPRIORITY,
599 *err = messages_get_messages_listing(mas->backend_data, name, max,
600 offset, subject_len, &filter,
601 get_messages_listing_cb, mas);
603 mas->buffer = g_string_new("");
611 static void *message_open(const char *name, int oflag, mode_t mode,
612 void *driver_data, size_t *size, int *err)
614 struct mas_session *mas = driver_data;
618 if (oflag != O_RDONLY) {
619 DBG("Message pushing unsupported");
625 *err = messages_get_message(mas->backend_data, name, 0,
626 get_message_cb, mas);
628 mas->buffer = g_string_new("");
636 static void *message_update_open(const char *name, int oflag, mode_t mode,
637 void *driver_data, size_t *size,
640 struct mas_session *mas = driver_data;
644 if (oflag == O_RDONLY) {
649 *err = messages_update_inbox(mas->backend_data, set_status_cb, mas);
656 static void *message_set_status_open(const char *name, int oflag, mode_t mode,
657 void *driver_data, size_t *size,
661 struct mas_session *mas = driver_data;
667 if (oflag == O_RDONLY) {
672 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSINDICATOR,
678 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSVALUE,
684 if (indicator == READ_STATUS_REQ)
685 *err = messages_set_read(mas->backend_data, name, value,
687 else if (indicator == DELETE_STATUS_REQ)
688 *err = messages_set_delete(mas->backend_data, name, value,
699 static ssize_t any_get_next_header(void *object, void *buf, size_t mtu,
702 struct mas_session *mas = object;
706 if (mas->buffer->len == 0 && !mas->finished)
709 *hi = G_OBEX_HDR_APPARAM;
715 return g_obex_apparam_encode(mas->outparams, buf, mtu);
718 static void *any_open(const char *name, int oflag, mode_t mode,
719 void *driver_data, size_t *size, int *err)
728 static ssize_t any_write(void *object, const void *buf, size_t count)
735 static ssize_t any_read(void *obj, void *buf, size_t count)
737 struct mas_session *mas = obj;
742 len = string_read(mas->buffer, buf, count);
744 if (len == 0 && !mas->finished)
750 static int any_close(void *obj)
752 struct mas_session *mas = obj;
757 messages_abort(mas->backend_data);
764 static struct obex_service_driver mas = {
765 .name = "Message Access server",
767 .channel = MAS_CHANNEL,
769 .record = MAS_RECORD,
770 .target = MAS_TARGET,
771 .target_size = TARGET_SIZE,
772 .connect = mas_connect,
775 .setpath = mas_setpath,
776 .disconnect = mas_disconnect,
779 static struct obex_mime_type_driver mime_map = {
780 .target = MAS_TARGET,
781 .target_size = TARGET_SIZE,
789 static struct obex_mime_type_driver mime_message = {
790 .target = MAS_TARGET,
791 .target_size = TARGET_SIZE,
792 .mimetype = "x-bt/message",
793 .open = message_open,
799 static struct obex_mime_type_driver mime_folder_listing = {
800 .target = MAS_TARGET,
801 .target_size = TARGET_SIZE,
802 .mimetype = "x-obex/folder-listing",
803 .get_next_header = any_get_next_header,
804 .open = folder_listing_open,
810 static struct obex_mime_type_driver mime_msg_listing = {
811 .target = MAS_TARGET,
812 .target_size = TARGET_SIZE,
813 .mimetype = "x-bt/MAP-msg-listing",
814 .get_next_header = any_get_next_header,
815 .open = msg_listing_open,
821 static struct obex_mime_type_driver mime_notification_registration = {
822 .target = MAS_TARGET,
823 .target_size = TARGET_SIZE,
824 .mimetype = "x-bt/MAP-NotificationRegistration",
831 static struct obex_mime_type_driver mime_message_status = {
832 .target = MAS_TARGET,
833 .target_size = TARGET_SIZE,
834 .mimetype = "x-bt/messageStatus",
835 .open = message_set_status_open,
841 static struct obex_mime_type_driver mime_message_update = {
842 .target = MAS_TARGET,
843 .target_size = TARGET_SIZE,
844 .mimetype = "x-bt/MAP-messageUpdate",
845 .open = message_update_open,
851 static struct obex_mime_type_driver *map_drivers[] = {
854 &mime_folder_listing,
856 &mime_notification_registration,
857 &mime_message_status,
858 &mime_message_update,
862 static int mas_init(void)
867 err = messages_init();
871 for (i = 0; map_drivers[i] != NULL; ++i) {
872 err = obex_mime_type_driver_register(map_drivers[i]);
877 err = obex_service_driver_register(&mas);
884 for (--i; i >= 0; --i)
885 obex_mime_type_driver_unregister(map_drivers[i]);
892 static void mas_exit(void)
896 obex_service_driver_unregister(&mas);
898 for (i = 0; map_drivers[i] != NULL; ++i)
899 obex_mime_type_driver_unregister(map_drivers[i]);
904 OBEX_PLUGIN_DEFINE(mas, mas_init, mas_exit)