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 #ifdef __TIZEN_PATCH__
36 #include <gobex/gobex.h>
37 #include <gobex/gobex-apparam.h>
45 #include "filesystem.h"
51 #define READ_STATUS_REQ 0
52 #define DELETE_STATUS_REQ 1
54 #define XML_DECL "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
56 /* Building blocks for x-obex/folder-listing */
57 #define FL_DTD "<!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\">"
58 #define FL_BODY_BEGIN "<folder-listing version=\"1.0\">"
59 #define FL_BODY_EMPTY "<folder-listing version=\"1.0\"/>"
60 #define FL_PARENT_FOLDER_ELEMENT "<parent-folder/>"
61 #define FL_FOLDER_ELEMENT "<folder name=\"%s\"/>"
62 #define FL_BODY_END "</folder-listing>"
64 #define ML_BODY_BEGIN "<MAP-msg-listing version=\"1.0\">"
65 #define ML_BODY_END "</MAP-msg-listing>"
68 struct mas_request *request;
73 GObexApparam *inparams;
74 GObexApparam *outparams;
76 #ifdef __TIZEN_PATCH__
77 gboolean headers_sent;
78 int notification_status;
80 char *response_handle;
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 const char *yesorno(gboolean a)
265 #ifdef __TIZEN_PATCH__
266 static gchar *get_local_timestamp(void)
271 gettimeofday(&tv, NULL);
273 if (!localtime_r(&tv.tv_sec, <ime))
276 return g_strdup_printf("%04d%02d%02dT%02d%02d%02d",
277 ltime.tm_year + 1900, ltime.tm_mon + 1,
278 ltime.tm_mday, ltime.tm_hour,
279 ltime.tm_min, ltime.tm_sec);
283 static void get_messages_listing_cb(void *session, int err, uint16_t size,
285 const struct messages_message *entry,
288 struct mas_session *mas = user_data;
290 #ifdef __TIZEN_PATCH__
294 if (err < 0 && err != -EAGAIN) {
295 obex_object_set_io_flags(mas, G_IO_ERR, err);
299 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
303 mas->finished = TRUE;
308 if (!mas->nth_call) {
309 g_string_append(mas->buffer, ML_BODY_BEGIN);
310 mas->nth_call = TRUE;
314 g_string_append(mas->buffer, ML_BODY_END);
315 mas->finished = TRUE;
320 g_string_append(mas->buffer, "<msg");
322 g_string_append_escaped_printf(mas->buffer, " handle=\"%s\"",
325 if (entry->mask & PMASK_SUBJECT)
326 g_string_append_escaped_printf(mas->buffer, " subject=\"%s\"",
329 if (entry->mask & PMASK_DATETIME)
330 g_string_append_escaped_printf(mas->buffer, " datetime=\"%s\"",
333 if (entry->mask & PMASK_SENDER_NAME)
334 g_string_append_escaped_printf(mas->buffer,
335 " sender_name=\"%s\"",
338 if (entry->mask & PMASK_SENDER_ADDRESSING)
339 g_string_append_escaped_printf(mas->buffer,
340 " sender_addressing=\"%s\"",
341 entry->sender_addressing);
343 if (entry->mask & PMASK_REPLYTO_ADDRESSING)
344 g_string_append_escaped_printf(mas->buffer,
345 " replyto_addressing=\"%s\"",
346 entry->replyto_addressing);
348 if (entry->mask & PMASK_RECIPIENT_NAME)
349 g_string_append_escaped_printf(mas->buffer,
350 " recipient_name=\"%s\"",
351 entry->recipient_name);
353 if (entry->mask & PMASK_RECIPIENT_ADDRESSING)
354 g_string_append_escaped_printf(mas->buffer,
355 " recipient_addressing=\"%s\"",
356 entry->recipient_addressing);
358 if (entry->mask & PMASK_TYPE)
359 g_string_append_escaped_printf(mas->buffer, " type=\"%s\"",
362 if (entry->mask & PMASK_RECEPTION_STATUS)
363 g_string_append_escaped_printf(mas->buffer,
364 " reception_status=\"%s\"",
365 entry->reception_status);
367 if (entry->mask & PMASK_SIZE)
368 g_string_append_escaped_printf(mas->buffer, " size=\"%s\"",
371 if (entry->mask & PMASK_ATTACHMENT_SIZE)
372 g_string_append_escaped_printf(mas->buffer,
373 " attachment_size=\"%s\"",
374 entry->attachment_size);
376 if (entry->mask & PMASK_TEXT)
377 g_string_append_escaped_printf(mas->buffer, " text=\"%s\"",
378 yesorno(entry->text));
380 if (entry->mask & PMASK_READ)
381 g_string_append_escaped_printf(mas->buffer, " read=\"%s\"",
382 yesorno(entry->read));
384 if (entry->mask & PMASK_SENT)
385 g_string_append_escaped_printf(mas->buffer, " sent=\"%s\"",
386 yesorno(entry->sent));
388 if (entry->mask & PMASK_PROTECTED)
389 g_string_append_escaped_printf(mas->buffer, " protected=\"%s\"",
390 yesorno(entry->protect));
392 if (entry->mask & PMASK_PRIORITY)
393 g_string_append_escaped_printf(mas->buffer, " priority=\"%s\"",
394 yesorno(entry->priority));
396 g_string_append(mas->buffer, "/>\n");
400 mas->outparams = g_obex_apparam_set_uint16(mas->outparams,
401 MAP_AP_MESSAGESLISTINGSIZE,
403 mas->outparams = g_obex_apparam_set_uint8(mas->outparams,
406 #ifdef __TIZEN_PATCH__
407 msetime = get_local_timestamp();
409 g_obex_apparam_set_string(mas->outparams,
410 MAP_AP_MSETIME, msetime);
417 obex_object_set_io_flags(mas, G_IO_IN, 0);
420 #ifdef __TIZEN_PATCH__
421 static void put_message_cb(void *session, int err, guint64 handle,
424 struct mas_session *mas = user_data;
429 obex_object_set_io_flags(mas, G_IO_ERR, err);
432 mas->finished = FALSE;
433 mas->response_handle = g_strdup_printf("%llx", handle);
435 obex_object_set_io_flags(mas, G_IO_OUT, 0);
439 static void get_message_cb(void *session, int err, gboolean fmore,
440 const char *chunk, void *user_data)
442 struct mas_session *mas = user_data;
446 if (err < 0 && err != -EAGAIN) {
447 obex_object_set_io_flags(mas, G_IO_ERR, err);
452 mas->finished = TRUE;
456 g_string_append(mas->buffer, chunk);
460 obex_object_set_io_flags(mas, G_IO_IN, 0);
463 static void get_folder_listing_cb(void *session, int err, uint16_t size,
464 const char *name, void *user_data)
466 struct mas_session *mas = user_data;
469 if (err < 0 && err != -EAGAIN) {
470 obex_object_set_io_flags(mas, G_IO_ERR, err);
474 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
478 mas->outparams = g_obex_apparam_set_uint16(
480 MAP_AP_FOLDERLISTINGSIZE,
484 mas->finished = TRUE;
487 #ifndef __TIZEN_PATCH__
494 if (!mas->nth_call) {
495 g_string_append(mas->buffer, XML_DECL);
496 g_string_append(mas->buffer, FL_DTD);
498 g_string_append(mas->buffer, FL_BODY_EMPTY);
499 mas->finished = TRUE;
502 g_string_append(mas->buffer, FL_BODY_BEGIN);
503 mas->nth_call = TRUE;
507 g_string_append(mas->buffer, FL_BODY_END);
508 mas->finished = TRUE;
512 if (g_strcmp0(name, "..") == 0)
513 g_string_append(mas->buffer, FL_PARENT_FOLDER_ELEMENT);
515 g_string_append_escaped_printf(mas->buffer, FL_FOLDER_ELEMENT,
520 obex_object_set_io_flags(mas, G_IO_IN, err);
523 static void set_status_cb(void *session, int err, void *user_data)
525 struct mas_session *mas = user_data;
529 mas->finished = TRUE;
532 obex_object_set_io_flags(mas, G_IO_ERR, err);
534 obex_object_set_io_flags(mas, G_IO_OUT, 0);
537 static int mas_setpath(struct obex_session *os, void *user_data)
540 const uint8_t *nonhdr;
541 struct mas_session *mas = user_data;
543 if (obex_get_non_header_data(os, &nonhdr) != 2) {
544 error("Set path failed: flag and constants not found!");
548 name = obex_get_name(os);
550 DBG("SETPATH: name %s nonhdr 0x%x%x", name, nonhdr[0], nonhdr[1]);
552 if ((nonhdr[0] & 0x02) != 0x02) {
553 DBG("Error: requested directory creation");
557 return messages_set_folder(mas->backend_data, name, nonhdr[0] & 0x01);
560 static void *folder_listing_open(const char *name, int oflag, mode_t mode,
561 void *driver_data, size_t *size, int *err)
563 struct mas_session *mas = driver_data;
564 /* 1024 is the default when there was no MaxListCount sent */
568 if (oflag != O_RDONLY) {
573 DBG("name = %s", name);
575 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
576 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset);
578 *err = messages_get_folder_listing(mas->backend_data, name, max,
579 offset, get_folder_listing_cb, mas);
581 mas->buffer = g_string_new("");
589 static void *msg_listing_open(const char *name, int oflag, mode_t mode,
590 void *driver_data, size_t *size, int *err)
592 struct mas_session *mas = driver_data;
593 struct messages_filter filter = { 0, };
594 /* 1024 is the default when there was no MaxListCount sent */
597 /* If MAP client does not specify the subject length,
598 then subject_len = 0 and subject should be sent unaltered. */
599 uint8_t subject_len = 0;
603 if (oflag != O_RDONLY) {
608 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_MAXLISTCOUNT, &max);
609 g_obex_apparam_get_uint16(mas->inparams, MAP_AP_STARTOFFSET, &offset);
610 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_SUBJECTLENGTH,
613 g_obex_apparam_get_uint32(mas->inparams, MAP_AP_PARAMETERMASK,
614 &filter.parameter_mask);
615 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERMESSAGETYPE,
617 filter.period_begin = g_obex_apparam_get_string(mas->inparams,
618 MAP_AP_FILTERPERIODBEGIN);
619 filter.period_end = g_obex_apparam_get_string(mas->inparams,
620 MAP_AP_FILTERPERIODEND);
621 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERREADSTATUS,
622 &filter.read_status);
623 filter.recipient = g_obex_apparam_get_string(mas->inparams,
624 MAP_AP_FILTERRECIPIENT);
625 filter.originator = g_obex_apparam_get_string(mas->inparams,
626 MAP_AP_FILTERORIGINATOR);
627 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FILTERPRIORITY,
630 *err = messages_get_messages_listing(mas->backend_data, name, max,
631 offset, subject_len, &filter,
632 get_messages_listing_cb, mas);
634 mas->buffer = g_string_new("");
642 static void *message_open(const char *name, int oflag, mode_t mode,
643 void *driver_data, size_t *size, int *err)
645 struct mas_session *mas = driver_data;
649 #ifdef __TIZEN_PATCH__
650 if (oflag != O_RDONLY) {
651 DBG("Message pushing invoked \n");
653 DBG("name = %s", name);
654 uint8_t transparent = 0;
658 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_TRANSPARENT,
660 DBG("transparent = %d \n", transparent);
661 g_obex_apparam_get_uint8(mas->inparams, MAP_AP_RETRY, &retry);
662 DBG("retry = %d \n", retry);
664 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_CHARSET,
669 mas->headers_sent = FALSE;
671 *err = messages_push_message(mas->backend_data, name,
672 transparent, retry, charset,
673 put_message_cb, mas);
675 uint8_t fraction_request = 0;
679 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_ATTACHMENT,
685 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_CHARSET,
691 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_FRACTIONREQUEST,
695 *err = messages_get_message(mas->backend_data, name, attachment,
696 charset, fraction_request,
697 get_message_cb, mas);
700 if (oflag != O_RDONLY) {
701 DBG("Message pushing unsupported");
707 *err = messages_get_message(mas->backend_data, name, 0,
708 get_message_cb, mas);
711 mas->buffer = g_string_new("");
719 #ifdef __TIZEN_PATCH__
720 static ssize_t message_write(void *object, const void *buf, size_t count)
723 struct mas_session *mas = object;
728 g_string_append_len(mas->buffer, buf, count);
730 DBG("count = %d \n", count);
732 if (g_strrstr(mas->buffer->str, "END:BMSG\r\n")) {
733 DBG("BMsg received. \n");
735 messages_push_message_data(mas->backend_data,
736 mas->buffer->str, NULL);
743 static void *message_update_open(const char *name, int oflag, mode_t mode,
744 void *driver_data, size_t *size,
747 struct mas_session *mas = driver_data;
751 if (oflag == O_RDONLY) {
756 *err = messages_update_inbox(mas->backend_data, set_status_cb, mas);
763 static void *message_set_status_open(const char *name, int oflag, mode_t mode,
764 void *driver_data, size_t *size,
768 struct mas_session *mas = driver_data;
774 if (oflag == O_RDONLY) {
779 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSINDICATOR,
785 if (!g_obex_apparam_get_uint8(mas->inparams, MAP_AP_STATUSVALUE,
791 if (indicator == READ_STATUS_REQ)
792 *err = messages_set_read(mas->backend_data, name, value,
794 else if (indicator == DELETE_STATUS_REQ)
795 *err = messages_set_delete(mas->backend_data, name, value,
806 #ifdef __TIZEN_PATCH__
807 static void *notification_registration_open(const char *name, int oflag,
808 mode_t mode, void *driver_data, size_t *size, int *err)
810 struct mas_session *mas = driver_data;
813 if (oflag == O_RDONLY) {
818 if (!g_obex_apparam_get_uint8(mas->inparams,
819 MAP_AP_NOTIFICATIONSTATUS, &status)) {
824 DBG("status: %d", status);
826 mas->notification_status = status;
828 mas->finished = TRUE;
832 static int notification_registration_close(void *obj)
834 struct mas_session *mas = obj;
838 messages_set_notification_registration(mas->backend_data,
839 mas->remote_addr, mas->notification_status,
847 static ssize_t put_next_header(void *object, void *buf, size_t mtu,
850 struct mas_session *mas = object;
854 if (mas->headers_sent)
857 if (mas->response_handle)
858 DBG("mas->response_handle %s\n", mas->response_handle);
862 *hi = G_OBEX_HDR_NAME;
864 len = strlen(mas->response_handle);
866 DBG("len %d\n", len);
867 DBG("mas->response_handle %s\n", mas->response_handle);
869 memcpy(buf, mas->response_handle, len);
871 mas->headers_sent = TRUE;
878 static ssize_t any_get_next_header(void *object, void *buf, size_t mtu,
881 struct mas_session *mas = object;
885 if (mas->buffer->len == 0 && !mas->finished)
888 *hi = G_OBEX_HDR_APPARAM;
894 return g_obex_apparam_encode(mas->outparams, buf, mtu);
897 static void *any_open(const char *name, int oflag, mode_t mode,
898 void *driver_data, size_t *size, int *err)
907 static ssize_t any_write(void *object, const void *buf, size_t count)
914 static ssize_t any_read(void *obj, void *buf, size_t count)
916 struct mas_session *mas = obj;
921 len = string_read(mas->buffer, buf, count);
923 if (len == 0 && !mas->finished)
929 static int any_close(void *obj)
931 struct mas_session *mas = obj;
936 messages_abort(mas->backend_data);
943 static struct obex_service_driver mas = {
944 .name = "Message Access server",
946 .target = MAS_TARGET,
947 .target_size = TARGET_SIZE,
948 .connect = mas_connect,
951 .setpath = mas_setpath,
952 .disconnect = mas_disconnect,
955 static struct obex_mime_type_driver mime_map = {
956 .target = MAS_TARGET,
957 .target_size = TARGET_SIZE,
965 static struct obex_mime_type_driver mime_message = {
966 .target = MAS_TARGET,
967 .target_size = TARGET_SIZE,
968 .mimetype = "x-bt/message",
969 .open = message_open,
972 #ifdef __TIZEN_PATCH__
973 .write = message_write,
974 .get_next_header = put_next_header
980 static struct obex_mime_type_driver mime_folder_listing = {
981 .target = MAS_TARGET,
982 .target_size = TARGET_SIZE,
983 .mimetype = "x-obex/folder-listing",
984 .get_next_header = any_get_next_header,
985 .open = folder_listing_open,
991 static struct obex_mime_type_driver mime_msg_listing = {
992 .target = MAS_TARGET,
993 .target_size = TARGET_SIZE,
994 .mimetype = "x-bt/MAP-msg-listing",
995 .get_next_header = any_get_next_header,
996 .open = msg_listing_open,
1002 static struct obex_mime_type_driver mime_notification_registration = {
1003 .target = MAS_TARGET,
1004 .target_size = TARGET_SIZE,
1005 .mimetype = "x-bt/MAP-NotificationRegistration",
1006 #ifdef __TIZEN_PATCH__
1007 .open = notification_registration_open,
1008 .close = notification_registration_close,
1017 static struct obex_mime_type_driver mime_message_status = {
1018 .target = MAS_TARGET,
1019 .target_size = TARGET_SIZE,
1020 .mimetype = "x-bt/messageStatus",
1021 .open = message_set_status_open,
1027 static struct obex_mime_type_driver mime_message_update = {
1028 .target = MAS_TARGET,
1029 .target_size = TARGET_SIZE,
1030 .mimetype = "x-bt/MAP-messageUpdate",
1031 .open = message_update_open,
1037 static struct obex_mime_type_driver *map_drivers[] = {
1040 &mime_folder_listing,
1042 &mime_notification_registration,
1043 &mime_message_status,
1044 &mime_message_update,
1048 static int mas_init(void)
1053 err = messages_init();
1057 for (i = 0; map_drivers[i] != NULL; ++i) {
1058 err = obex_mime_type_driver_register(map_drivers[i]);
1063 err = obex_service_driver_register(&mas);
1070 for (--i; i >= 0; --i)
1071 obex_mime_type_driver_unregister(map_drivers[i]);
1078 static void mas_exit(void)
1082 obex_service_driver_unregister(&mas);
1084 for (i = 0; map_drivers[i] != NULL; ++i)
1085 obex_mime_type_driver_unregister(map_drivers[i]);
1090 OBEX_PLUGIN_DEFINE(mas, mas_init, mas_exit)