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
35 #include "gobex/gobex.h"
36 #include "gobex/gobex-apparam.h"
38 #include "obexd/src/obexd.h"
39 #include "obexd/src/plugin.h"
40 #include "obexd/src/log.h"
41 #include "obexd/src/obex.h"
42 #include "obexd/src/service.h"
43 #include "obexd/src/mimetype.h"
44 #include "obexd/src/manager.h"
45 #include "obexd/src/map_ap.h"
46 #include "filesystem.h"
49 #define READ_STATUS_REQ 0
50 #define DELETE_STATUS_REQ 1
52 #define XML_DECL "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
54 /* Building blocks for x-obex/folder-listing */
55 #define FL_DTD "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\">"
56 #define FL_BODY_BEGIN "<folder-listing version=\"1.0\">"
57 #define FL_BODY_EMPTY "<folder-listing version=\"1.0\"/>"
58 #define FL_PARENT_FOLDER_ELEMENT "<parent-folder/>"
59 #define FL_FOLDER_ELEMENT "<folder name=\"%s\"/>"
60 #define FL_BODY_END "</folder-listing>"
62 #define ML_BODY_BEGIN "<MAP-msg-listing version=\"1.0\">"
63 #define ML_BODY_END "</MAP-msg-listing>"
66 struct mas_request *request;
71 GObexApparam *inparams;
72 GObexApparam *outparams;
74 #ifdef __TIZEN_PATCH__
75 gboolean headers_sent;
76 int notification_status;
78 char *response_handle;
80 uint8_t notification_status;
84 static const uint8_t MAS_TARGET[TARGET_SIZE] = {
85 0xbb, 0x58, 0x2b, 0x40, 0x42, 0x0c, 0x11, 0xdb,
86 0xb0, 0xde, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 };
88 static int get_params(struct obex_session *os, struct mas_session *mas)
90 const uint8_t *buffer;
93 size = obex_get_apparam(os, &buffer);
97 mas->inparams = g_obex_apparam_decode(buffer, size);
98 if (mas->inparams == NULL) {
99 DBG("Error when parsing parameters!");
106 static void reset_request(struct mas_session *mas)
109 g_string_free(mas->buffer, TRUE);
114 g_obex_apparam_free(mas->inparams);
115 mas->inparams = NULL;
118 if (mas->outparams) {
119 g_obex_apparam_free(mas->outparams);
120 mas->outparams = NULL;
123 mas->nth_call = FALSE;
124 mas->finished = FALSE;
125 mas->ap_sent = FALSE;
126 #ifdef __TIZEN_PATCH__
127 mas->headers_sent = FALSE;
128 g_free(mas->response_handle);
129 mas->response_handle = NULL;
133 static void mas_clean(struct mas_session *mas)
136 #ifdef __TIZEN_PATCH__
137 g_free(mas->remote_addr);
142 static void *mas_connect(struct obex_session *os, int *err)
144 struct mas_session *mas;
145 #ifdef __TIZEN_PATCH__
151 mas = g_new0(struct mas_session, 1);
153 *err = messages_connect(&mas->backend_data);
157 manager_register_session(os);
159 #ifdef __TIZEN_PATCH__
160 if (obex_getpeername(os, &address) == 0) {
161 mas->remote_addr = address;
162 DBG("mas->remote_addr = %s\n", mas->remote_addr);
174 static void mas_disconnect(struct obex_session *os, void *user_data)
176 struct mas_session *mas = user_data;
180 manager_unregister_session(os);
181 messages_disconnect(mas->backend_data);
186 static int mas_get(struct obex_session *os, void *user_data)
188 struct mas_session *mas = user_data;
189 const char *type = obex_get_type(os);
190 const char *name = obex_get_name(os);
193 DBG("GET: name %s type %s mas %p",
199 ret = get_params(os, mas);
203 ret = obex_get_stream_start(os, name);
215 static int mas_put(struct obex_session *os, void *user_data)
217 struct mas_session *mas = user_data;
218 const char *type = obex_get_type(os);
219 const char *name = obex_get_name(os);
222 DBG("PUT: name %s type %s mas %p", name, type, mas);
227 ret = get_params(os, mas);
231 ret = obex_put_stream_start(os, name);
243 /* FIXME: Preserve whitespaces */
244 static void g_string_append_escaped_printf(GString *string,
245 const char *format, ...)
250 va_start(ap, format);
251 escaped = g_markup_vprintf_escaped(format, ap);
252 g_string_append(string, escaped);
257 static gchar *get_mse_timestamp(void)
259 struct timeval time_val;
264 if (gettimeofday(&time_val, NULL) < 0)
267 if (!localtime_r(&time_val.tv_sec, <ime))
270 if (difftime(mktime(localtime(&time_val.tv_sec)),
271 mktime(gmtime(&time_val.tv_sec))) < 0)
276 local_ts = g_strdup_printf("%04d%02d%02dT%02d%02d%02d%c%2ld%2ld",
277 ltime.tm_year + 1900, ltime.tm_mon + 1,
278 ltime.tm_mday, ltime.tm_hour,
279 ltime.tm_min, ltime.tm_sec, sign,
280 ltime.tm_gmtoff/3600,
281 (ltime.tm_gmtoff%3600)/60);
286 static const char *yesorno(gboolean a)
294 static void get_messages_listing_cb(void *session, int err, uint16_t size,
296 const struct messages_message *entry,
299 struct mas_session *mas = user_data;
303 if (err < 0 && err != -EAGAIN) {
304 obex_object_set_io_flags(mas, G_IO_ERR, err);
309 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT,
314 mas->finished = TRUE;
319 if (!mas->nth_call) {
320 g_string_append(mas->buffer, ML_BODY_BEGIN);
321 mas->nth_call = TRUE;
325 g_string_append(mas->buffer, ML_BODY_END);
326 mas->finished = TRUE;
331 g_string_append(mas->buffer, "<msg");
333 g_string_append_escaped_printf(mas->buffer, " handle=\"%s\"",
336 if (entry->mask & PMASK_SUBJECT)
337 g_string_append_escaped_printf(mas->buffer, " subject=\"%s\"",
340 if (entry->mask & PMASK_DATETIME)
341 g_string_append_escaped_printf(mas->buffer, " datetime=\"%s\"",
344 if (entry->mask & PMASK_SENDER_NAME)
345 g_string_append_escaped_printf(mas->buffer,
346 " sender_name=\"%s\"",
349 if (entry->mask & PMASK_SENDER_ADDRESSING)
350 g_string_append_escaped_printf(mas->buffer,
351 " sender_addressing=\"%s\"",
352 entry->sender_addressing);
354 if (entry->mask & PMASK_REPLYTO_ADDRESSING)
355 g_string_append_escaped_printf(mas->buffer,
356 " replyto_addressing=\"%s\"",
357 entry->replyto_addressing);
359 if (entry->mask & PMASK_RECIPIENT_NAME)
360 g_string_append_escaped_printf(mas->buffer,
361 " recipient_name=\"%s\"",
362 entry->recipient_name);
364 if (entry->mask & PMASK_RECIPIENT_ADDRESSING)
365 g_string_append_escaped_printf(mas->buffer,
366 " recipient_addressing=\"%s\"",
367 entry->recipient_addressing);
369 if (entry->mask & PMASK_TYPE)
370 g_string_append_escaped_printf(mas->buffer, " type=\"%s\"",
373 if (entry->mask & PMASK_RECEPTION_STATUS)
374 g_string_append_escaped_printf(mas->buffer,
375 " reception_status=\"%s\"",
376 entry->reception_status);
378 if (entry->mask & PMASK_SIZE)
379 g_string_append_escaped_printf(mas->buffer, " size=\"%s\"",
382 if (entry->mask & PMASK_ATTACHMENT_SIZE)
383 g_string_append_escaped_printf(mas->buffer,
384 " attachment_size=\"%s\"",
385 entry->attachment_size);
387 if (entry->mask & PMASK_TEXT)
388 g_string_append_escaped_printf(mas->buffer, " text=\"%s\"",
389 yesorno(entry->text));
391 if (entry->mask & PMASK_READ)
392 g_string_append_escaped_printf(mas->buffer, " read=\"%s\"",
393 yesorno(entry->read));
395 if (entry->mask & PMASK_SENT)
396 g_string_append_escaped_printf(mas->buffer, " sent=\"%s\"",
397 yesorno(entry->sent));
399 if (entry->mask & PMASK_PROTECTED)
400 g_string_append_escaped_printf(mas->buffer, " protected=\"%s\"",
401 yesorno(entry->protect));
403 if (entry->mask & PMASK_PRIORITY)
404 g_string_append_escaped_printf(mas->buffer, " priority=\"%s\"",
405 yesorno(entry->priority));
407 g_string_append(mas->buffer, "/>\n");
411 mas->outparams = g_obex_apparam_set_uint16(mas->outparams,
412 MAP_AP_MESSAGESLISTINGSIZE,
414 mas->outparams = g_obex_apparam_set_uint8(mas->outparams,
417 /* Response to report the local time of MSE */
418 mse_time = get_mse_timestamp();
420 g_obex_apparam_set_string(mas->outparams,
421 MAP_AP_MSETIME, mse_time);
427 obex_object_set_io_flags(mas, G_IO_IN, 0);
430 #ifdef __TIZEN_PATCH__
431 static void put_message_cb(void *session, int err, guint64 handle,
434 struct mas_session *mas = user_data;
439 obex_object_set_io_flags(mas, G_IO_ERR, err);
442 mas->finished = FALSE;
443 mas->response_handle = g_strdup_printf("%llx", handle);
445 obex_object_set_io_flags(mas, G_IO_OUT, 0);
449 static void get_message_cb(void *session, int err, gboolean fmore,
450 const char *chunk, void *user_data)
452 struct mas_session *mas = user_data;
456 if (err < 0 && err != -EAGAIN) {
457 obex_object_set_io_flags(mas, G_IO_ERR, err);
462 mas->finished = TRUE;
466 g_string_append(mas->buffer, chunk);
470 obex_object_set_io_flags(mas, G_IO_IN, 0);
473 static void get_folder_listing_cb(void *session, int err, uint16_t size,
474 const char *name, void *user_data)
476 struct mas_session *mas = user_data;
479 if (err < 0 && err != -EAGAIN) {
480 obex_object_set_io_flags(mas, G_IO_ERR, err);
485 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT,
490 mas->outparams = g_obex_apparam_set_uint16(
492 MAP_AP_FOLDERLISTINGSIZE,
496 mas->finished = TRUE;
499 #ifdef __TIZEN_PATCH__
505 if (!mas->nth_call) {
506 g_string_append(mas->buffer, XML_DECL);
507 g_string_append(mas->buffer, FL_DTD);
509 g_string_append(mas->buffer, FL_BODY_EMPTY);
510 mas->finished = TRUE;
513 g_string_append(mas->buffer, FL_BODY_BEGIN);
514 mas->nth_call = TRUE;
518 g_string_append(mas->buffer, FL_BODY_END);
519 mas->finished = TRUE;
523 if (g_strcmp0(name, "..") == 0)
524 g_string_append(mas->buffer, FL_PARENT_FOLDER_ELEMENT);
526 g_string_append_escaped_printf(mas->buffer, FL_FOLDER_ELEMENT,
531 obex_object_set_io_flags(mas, G_IO_IN, err);
534 static void set_status_cb(void *session, int err, void *user_data)
536 struct mas_session *mas = user_data;
540 mas->finished = TRUE;
543 obex_object_set_io_flags(mas, G_IO_ERR, err);
545 obex_object_set_io_flags(mas, G_IO_OUT, 0);
548 static int mas_setpath(struct obex_session *os, void *user_data)
551 const uint8_t *nonhdr;
552 struct mas_session *mas = user_data;
554 if (obex_get_non_header_data(os, &nonhdr) != 2) {
555 error("Set path failed: flag and constants not found!");
559 name = obex_get_name(os);
561 DBG("SETPATH: name %s nonhdr 0x%x%x", name, nonhdr[0], nonhdr[1]);
563 if ((nonhdr[0] & 0x02) != 0x02) {
564 DBG("Error: requested directory creation");
568 return messages_set_folder(mas->backend_data, name, nonhdr[0] & 0x01);
571 static void *folder_listing_open(const char *name, int oflag, mode_t mode,
572 void *driver_data, size_t *size, int *err)
574 struct mas_session *mas = driver_data;
575 /* 1024 is the default when there was no MaxListCount sent */
579 if (oflag != O_RDONLY) {
584 DBG("name = %s", name);
587 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT,
589 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET,
593 *err = messages_get_folder_listing(mas->backend_data, name, max,
594 offset, get_folder_listing_cb, mas);
596 mas->buffer = g_string_new("");
604 static void *msg_listing_open(const char *name, int oflag, mode_t mode,
605 void *driver_data, size_t *size, int *err)
607 struct mas_session *mas = driver_data;
608 struct messages_filter filter = { 0, };
609 /* 1024 is the default when there was no MaxListCount sent */
612 /* If MAP client does not specify the subject length,
613 then subject_len = 0 and subject should be sent unaltered. */
614 uint8_t subject_len = 0;
618 if (oflag != O_RDONLY) {
626 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
627 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset);
628 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_SUBJECTLENGTH,
631 g_obex_apparam_get_uint32(mas->inparams, MAP_AP_PARAMETERMASK,
632 &filter.parameter_mask);
633 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERMESSAGETYPE,
635 filter.period_begin = g_obex_apparam_get_string(mas->inparams,
636 MAP_AP_FILTERPERIODBEGIN);
637 filter.period_end = g_obex_apparam_get_string(mas->inparams,
638 MAP_AP_FILTERPERIODEND);
639 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERREADSTATUS,
640 &filter.read_status);
641 filter.recipient = g_obex_apparam_get_string(mas->inparams,
642 MAP_AP_FILTERRECIPIENT);
643 filter.originator = g_obex_apparam_get_string(mas->inparams,
644 MAP_AP_FILTERORIGINATOR);
645 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERPRIORITY,
649 *err = messages_get_messages_listing(mas->backend_data, name, max,
650 offset, subject_len, &filter,
651 get_messages_listing_cb, mas);
653 mas->buffer = g_string_new("");
661 static void *message_open(const char *name, int oflag, mode_t mode,
662 void *driver_data, size_t *size, int *err)
664 struct mas_session *mas = driver_data;
668 #ifdef __TIZEN_PATCH__
669 if (oflag != O_RDONLY) {
670 DBG("Message pushing invoked \n");
672 DBG("name = %s", name);
673 uint8_t transparent = 0;
677 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_TRANSPARENT,
679 DBG("transparent = %d \n", transparent);
680 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_RETRY, &retry);
681 DBG("retry = %d \n", retry);
683 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_CHARSET,
688 mas->headers_sent = FALSE;
690 *err = messages_push_message(mas->backend_data, name,
691 transparent, retry, charset,
692 put_message_cb, mas);
694 uint8_t fraction_request = 0;
698 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_ATTACHMENT,
704 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_CHARSET,
710 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FRACTIONREQUEST,
714 *err = messages_get_message(mas->backend_data, name, attachment,
715 charset, fraction_request,
716 get_message_cb, mas);
719 if (oflag != O_RDONLY) {
720 DBG("Message pushing unsupported");
726 *err = messages_get_message(mas->backend_data, name, 0,
727 get_message_cb, mas);
730 mas->buffer = g_string_new("");
738 #ifdef __TIZEN_PATCH__
739 static ssize_t message_write(void *object, const void *buf, size_t count)
742 struct mas_session *mas = object;
747 g_string_append_len(mas->buffer, buf, count);
749 DBG("count = %d \n", count);
751 if (g_strrstr(mas->buffer->str, "END:BMSG\r\n")) {
752 DBG("BMsg received. \n");
754 messages_push_message_data(mas->backend_data,
755 mas->buffer->str, NULL);
762 static void *message_update_open(const char *name, int oflag, mode_t mode,
763 void *driver_data, size_t *size,
766 struct mas_session *mas = driver_data;
770 if (oflag == O_RDONLY) {
775 *err = messages_update_inbox(mas->backend_data, set_status_cb, mas);
782 static void *message_set_status_open(const char *name, int oflag, mode_t mode,
783 void *driver_data, size_t *size,
787 struct mas_session *mas = driver_data;
793 if (oflag == O_RDONLY) {
798 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSINDICATOR,
804 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSVALUE,
810 if (indicator == READ_STATUS_REQ)
811 *err = messages_set_read(mas->backend_data, name, value,
813 else if (indicator == DELETE_STATUS_REQ)
814 *err = messages_set_delete(mas->backend_data, name, value,
825 #ifdef __TIZEN_PATCH__
826 static int notification_registration_close(void *obj)
828 struct mas_session *mas = obj;
832 messages_set_notification_registration(mas->backend_data,
833 mas->remote_addr, mas->notification_status,
841 static ssize_t put_next_header(void *object, void *buf, size_t mtu,
844 struct mas_session *mas = object;
848 if (mas->headers_sent)
851 if (mas->response_handle)
852 DBG("mas->response_handle %s\n", mas->response_handle);
856 *hi = G_OBEX_HDR_NAME;
858 len = strlen(mas->response_handle);
860 DBG("len %d\n", len);
861 DBG("mas->response_handle %s\n", mas->response_handle);
863 memcpy(buf, mas->response_handle, len);
865 mas->headers_sent = TRUE;
872 static ssize_t any_get_next_header(void *object, void *buf, size_t mtu,
875 struct mas_session *mas = object;
879 if (mas->buffer->len == 0 && !mas->finished)
882 *hi = G_OBEX_HDR_APPARAM;
891 return g_obex_apparam_encode(mas->outparams, buf, mtu);
894 static void *any_open(const char *name, int oflag, mode_t mode,
895 void *driver_data, size_t *size, int *err)
904 static ssize_t any_write(void *object, const void *buf, size_t count)
911 static ssize_t any_read(void *obj, void *buf, size_t count)
913 struct mas_session *mas = obj;
918 len = string_read(mas->buffer, buf, count);
920 if (len == 0 && !mas->finished)
926 static int any_close(void *obj)
928 struct mas_session *mas = obj;
933 messages_abort(mas->backend_data);
940 static void *notification_registration_open(const char *name, int oflag,
941 mode_t mode, void *driver_data,
942 size_t *size, int *err)
944 struct mas_session *mas = driver_data;
954 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_NOTIFICATIONSTATUS,
960 mas->notification_status = status;
961 mas->finished = TRUE;
967 static struct obex_service_driver mas = {
968 .name = "Message Access server",
970 .target = MAS_TARGET,
971 .target_size = TARGET_SIZE,
972 .connect = mas_connect,
975 .setpath = mas_setpath,
976 .disconnect = mas_disconnect,
979 static struct obex_mime_type_driver mime_map = {
980 .target = MAS_TARGET,
981 .target_size = TARGET_SIZE,
989 static struct obex_mime_type_driver mime_message = {
990 .target = MAS_TARGET,
991 .target_size = TARGET_SIZE,
992 .mimetype = "x-bt/message",
993 .open = message_open,
996 #ifdef __TIZEN_PATCH__
997 .write = message_write,
998 .get_next_header = put_next_header
1004 static struct obex_mime_type_driver mime_folder_listing = {
1005 .target = MAS_TARGET,
1006 .target_size = TARGET_SIZE,
1007 .mimetype = "x-obex/folder-listing",
1008 .get_next_header = any_get_next_header,
1009 .open = folder_listing_open,
1015 static struct obex_mime_type_driver mime_msg_listing = {
1016 .target = MAS_TARGET,
1017 .target_size = TARGET_SIZE,
1018 .mimetype = "x-bt/MAP-msg-listing",
1019 .get_next_header = any_get_next_header,
1020 .open = msg_listing_open,
1026 static struct obex_mime_type_driver mime_notification_registration = {
1027 .target = MAS_TARGET,
1028 .target_size = TARGET_SIZE,
1029 .mimetype = "x-bt/MAP-NotificationRegistration",
1030 .open = notification_registration_open,
1031 #ifdef __TIZEN_PATCH__
1032 .close = notification_registration_close,
1040 static struct obex_mime_type_driver mime_message_status = {
1041 .target = MAS_TARGET,
1042 .target_size = TARGET_SIZE,
1043 .mimetype = "x-bt/messageStatus",
1044 .open = message_set_status_open,
1050 static struct obex_mime_type_driver mime_message_update = {
1051 .target = MAS_TARGET,
1052 .target_size = TARGET_SIZE,
1053 .mimetype = "x-bt/MAP-messageUpdate",
1054 .open = message_update_open,
1060 static struct obex_mime_type_driver *map_drivers[] = {
1063 &mime_folder_listing,
1065 &mime_notification_registration,
1066 &mime_message_status,
1067 &mime_message_update,
1071 static int mas_init(void)
1076 err = messages_init();
1080 for (i = 0; map_drivers[i] != NULL; ++i) {
1081 err = obex_mime_type_driver_register(map_drivers[i]);
1086 err = obex_service_driver_register(&mas);
1093 for (--i; i >= 0; --i)
1094 obex_mime_type_driver_unregister(map_drivers[i]);
1101 static void mas_exit(void)
1105 obex_service_driver_unregister(&mas);
1107 for (i = 0; map_drivers[i] != NULL; ++i)
1108 obex_mime_type_driver_unregister(map_drivers[i]);
1113 OBEX_PLUGIN_DEFINE(mas, mas_init, mas_exit)