5 * Copyright (C) 2007-2010 Intel Corporation
6 * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
35 #include "lib/bluetooth.h"
38 #include "gobex/gobex-apparam.h"
39 #include "gdbus/gdbus.h"
41 #include "obexd/src/log.h"
48 #define OBEX_PBAP_UUID \
49 "\x79\x61\x35\xF0\xF0\xC5\x11\xD8\x09\x66\x08\x00\x20\x0C\x9A\x66"
50 #define OBEX_PBAP_UUID_LEN 16
52 #define FORMAT_VCARD21 0x0
53 #define FORMAT_VCARD30 0x1
55 #define ORDER_INDEXED 0x0
56 #define ORDER_ALPHANUMERIC 0x1
57 #define ORDER_PHONETIC 0x2
59 #define ATTRIB_NAME 0x0
60 #define ATTRIB_NUMBER 0x1
61 #define ATTRIB_SOUND 0x2
63 #define DEFAULT_COUNT 65535
64 #define DEFAULT_OFFSET 0
66 #define PULLPHONEBOOK 0x1
67 #define GETPHONEBOOKSIZE 0x2
69 #define ORDER_TAG 0x01
70 #define SEARCHVALUE_TAG 0x02
71 #define SEARCHATTRIB_TAG 0x03
72 #define MAXLISTCOUNT_TAG 0x04
73 #define LISTSTARTOFFSET_TAG 0x05
74 #define FILTER_TAG 0x06
75 #define FORMAT_TAG 0X07
76 #define PHONEBOOKSIZE_TAG 0X08
77 #define NEWMISSEDCALLS_TAG 0X09
78 #define PRIMARY_COUNTER_TAG 0X0A
79 #define SECONDARY_COUNTER_TAG 0X0B
80 #define DATABASEID_TAG 0X0D
81 #define SUPPORTED_FEATURES_TAG 0x10
83 #define DOWNLOAD_FEATURE 0x00000001
84 #define BROWSE_FEATURE 0x00000002
85 #define DATABASEID_FEATURE 0x00000004
86 #define FOLDER_VERSION_FEATURE 0x00000008
87 #define VCARD_SELECTING_FEATURE 0x00000010
88 #define ENHANCED_CALLS_FEATURE 0x00000020
89 #define UCI_FEATURE 0x00000040
90 #define UID_FEATURE 0x00000080
91 #define REFERENCING_FEATURE 0x00000100
92 #define DEFAULT_IMAGE_FEATURE 0x00000200
94 static const char *filter_list[] = {
123 "X-IRMC-CALL-DATETIME",
130 #define FILTER_BIT_MAX 63
131 #define FILTER_ALL 0xFFFFFFFFFFFFFFFFULL
133 #define PBAP_INTERFACE "org.bluez.obex.PhonebookAccess1"
134 #define ERROR_INTERFACE "org.bluez.obex.Error"
135 #define PBAP_UUID "0000112f-0000-1000-8000-00805f9b34fb"
138 struct obc_session *session;
141 uint32_t supported_features;
142 uint8_t databaseid[16];
144 uint8_t secondary[16];
147 struct pending_request {
148 struct pbap_data *pbap;
152 static DBusConnection *conn = NULL;
154 static struct pending_request *pending_request_new(struct pbap_data *pbap,
155 DBusMessage *message)
157 struct pending_request *p;
159 p = g_new0(struct pending_request, 1);
161 p->msg = dbus_message_ref(message);
166 static void pending_request_free(struct pending_request *p)
168 dbus_message_unref(p->msg);
172 static void listing_element(GMarkupParseContext *ctxt,
179 DBusMessageIter *item = user_data, entry;
181 const char *handle = NULL, *vcardname = NULL;
183 if (g_str_equal(element, "card") != TRUE)
186 for (key = (char **) names; *key; key++, values++) {
187 if (g_str_equal(*key, "handle") == TRUE)
189 else if (g_str_equal(*key, "name") == TRUE)
193 if (!handle || !vcardname)
196 dbus_message_iter_open_container(item, DBUS_TYPE_STRUCT, NULL, &entry);
197 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &handle);
198 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &vcardname);
199 dbus_message_iter_close_container(item, &entry);
202 static const GMarkupParser listing_parser = {
210 static char *build_phonebook_path(const char *location, const char *item)
212 char *path = NULL, *tmp, *tmp1;
213 gboolean internal = FALSE;
215 if (!g_ascii_strcasecmp(location, "int") ||
216 !g_ascii_strcasecmp(location, "internal")) {
217 path = g_strdup("/telecom");
219 } else if (!g_ascii_strncasecmp(location, "sim", 3)) {
220 if (strlen(location) == 3)
221 tmp = g_strdup("sim1");
223 tmp = g_ascii_strup(location, 4);
225 path = g_build_filename("/", tmp, "telecom", NULL);
230 #ifdef __TIZEN_PATCH__
231 if (!g_ascii_strcasecmp(item, "nil"))
235 if (!g_ascii_strcasecmp(item, "pb") ||
236 !g_ascii_strcasecmp(item, "ich") ||
237 !g_ascii_strcasecmp(item, "och") ||
238 !g_ascii_strcasecmp(item, "mch") ||
239 !g_ascii_strcasecmp(item, "cch") ||
240 (internal && !g_ascii_strcasecmp(item, "spd")) ||
241 (internal && !g_ascii_strcasecmp(item, "fav"))) {
243 tmp1 = g_ascii_strdown(item, -1);
244 path = g_build_filename(tmp, tmp1, NULL);
255 /* should only be called inside pbap_set_path */
256 static void pbap_reset_path(struct pbap_data *pbap)
261 obc_session_setpath(pbap->session, pbap->path, NULL, NULL, NULL);
264 static void pbap_setpath_cb(struct obc_session *session,
265 struct obc_transfer *transfer,
266 GError *err, void *user_data)
268 struct pending_request *request = user_data;
269 struct pbap_data *pbap = request->pbap;
272 pbap_reset_path(pbap);
274 g_dbus_emit_property_changed(conn,
275 obc_session_get_path(pbap->session),
276 PBAP_INTERFACE, "Folder");
279 DBusMessage *reply = g_dbus_create_error(request->msg,
280 ERROR_INTERFACE ".Failed",
282 g_dbus_send_message(conn, reply);
284 g_dbus_send_reply(conn, request->msg, DBUS_TYPE_INVALID);
286 pending_request_free(request);
289 static void read_version(struct pbap_data *pbap, GObexApparam *apparam)
295 if (!(pbap->supported_features & FOLDER_VERSION_FEATURE))
298 if (!g_obex_apparam_get_bytes(apparam, PRIMARY_COUNTER_TAG, &data,
301 memset(value, 0, len);
305 if (memcmp(pbap->primary, data, len)) {
306 memcpy(pbap->primary, data, len);
307 g_dbus_emit_property_changed(conn,
308 obc_session_get_path(pbap->session),
309 PBAP_INTERFACE, "PrimaryCounter");
312 if (!g_obex_apparam_get_bytes(apparam, SECONDARY_COUNTER_TAG, &data,
315 memset(value, 0, len);
319 if (memcmp(pbap->secondary, data, len)) {
320 memcpy(pbap->secondary, data, len);
321 g_dbus_emit_property_changed(conn,
322 obc_session_get_path(pbap->session),
323 PBAP_INTERFACE, "SecondaryCounter");
327 static void read_databaseid(struct pbap_data *pbap, GObexApparam *apparam)
333 if (!(pbap->supported_features & DATABASEID_FEATURE))
336 if (!g_obex_apparam_get_bytes(apparam, DATABASEID_TAG, &data, &len)) {
338 memset(value, 0, len);
342 if (memcmp(data, pbap->databaseid, len)) {
343 memcpy(pbap->databaseid, data, len);
344 g_dbus_emit_property_changed(conn,
345 obc_session_get_path(pbap->session),
346 PBAP_INTERFACE, "DatabaseIdentifier");
350 static void read_return_apparam(struct obc_transfer *transfer,
351 struct pbap_data *pbap,
352 guint16 *phone_book_size,
353 guint8 *new_missed_calls)
355 GObexApparam *apparam;
357 *phone_book_size = 0;
358 *new_missed_calls = 0;
360 apparam = obc_transfer_get_apparam(transfer);
364 g_obex_apparam_get_uint16(apparam, PHONEBOOKSIZE_TAG,
366 g_obex_apparam_get_uint8(apparam, NEWMISSEDCALLS_TAG,
369 read_version(pbap, apparam);
370 read_databaseid(pbap, apparam);
373 static void phonebook_size_callback(struct obc_session *session,
374 struct obc_transfer *transfer,
375 GError *err, void *user_data)
377 struct pending_request *request = user_data;
379 guint16 phone_book_size;
380 guint8 new_missed_calls;
383 reply = g_dbus_create_error(request->msg,
384 ERROR_INTERFACE ".Failed",
389 reply = dbus_message_new_method_return(request->msg);
391 read_return_apparam(transfer, request->pbap, &phone_book_size,
394 if (dbus_message_is_method_call(request->msg, PBAP_INTERFACE,
396 dbus_message_append_args(reply,
397 DBUS_TYPE_UINT16, &phone_book_size,
401 g_dbus_send_message(conn, reply);
402 pending_request_free(request);
405 static void pull_vcard_listing_callback(struct obc_session *session,
406 struct obc_transfer *transfer,
407 GError *err, void *user_data)
409 struct pending_request *request = user_data;
410 GMarkupParseContext *ctxt;
412 DBusMessageIter iter, array;
418 reply = g_dbus_create_error(request->msg,
419 ERROR_INTERFACE ".Failed",
424 perr = obc_transfer_get_contents(transfer, &contents, &size);
426 reply = g_dbus_create_error(request->msg,
427 ERROR_INTERFACE ".Failed",
428 "Error reading contents: %s",
433 reply = dbus_message_new_method_return(request->msg);
435 dbus_message_iter_init_append(reply, &iter);
436 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
437 DBUS_STRUCT_BEGIN_CHAR_AS_STRING
438 DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
439 DBUS_STRUCT_END_CHAR_AS_STRING, &array);
440 ctxt = g_markup_parse_context_new(&listing_parser, 0, &array, NULL);
441 g_markup_parse_context_parse(ctxt, contents, size, NULL);
442 g_markup_parse_context_free(ctxt);
443 dbus_message_iter_close_container(&iter, &array);
447 g_dbus_send_message(conn, reply);
448 pending_request_free(request);
451 static GObexApparam *parse_format(GObexApparam *apparam, DBusMessageIter *iter)
456 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
459 dbus_message_iter_get_basic(iter, &string);
461 if (!string || g_str_equal(string, ""))
462 format = FORMAT_VCARD21;
463 else if (!g_ascii_strcasecmp(string, "vcard21"))
464 format = FORMAT_VCARD21;
465 else if (!g_ascii_strcasecmp(string, "vcard30"))
466 format = FORMAT_VCARD30;
470 return g_obex_apparam_set_uint8(apparam, FORMAT_TAG, format);
473 static GObexApparam *parse_order(GObexApparam *apparam, DBusMessageIter *iter)
478 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
481 dbus_message_iter_get_basic(iter, &string);
483 if (!string || g_str_equal(string, ""))
484 order = ORDER_INDEXED;
485 else if (!g_ascii_strcasecmp(string, "indexed"))
486 order = ORDER_INDEXED;
487 else if (!g_ascii_strcasecmp(string, "alphanumeric"))
488 order = ORDER_ALPHANUMERIC;
489 else if (!g_ascii_strcasecmp(string, "phonetic"))
490 order = ORDER_PHONETIC;
494 return g_obex_apparam_set_uint8(apparam, ORDER_TAG, order);
497 static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter)
501 #ifdef __TIZEN_PATCH__
502 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16 &&
503 dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
506 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
510 dbus_message_iter_get_basic(iter, &num);
512 return g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, num);
515 static GObexApparam *parse_max_count(GObexApparam *apparam,
516 DBusMessageIter *iter)
520 #ifdef __TIZEN_PATCH__
521 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16 &&
522 dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
525 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
529 dbus_message_iter_get_basic(iter, &num);
531 return g_obex_apparam_set_uint16(apparam, MAXLISTCOUNT_TAG, num);
534 static uint64_t get_filter_mask(const char *filterstr)
541 if (!g_ascii_strcasecmp(filterstr, "ALL"))
544 for (i = 0; filter_list[i] != NULL; i++)
545 if (!g_ascii_strcasecmp(filterstr, filter_list[i]))
548 if (strlen(filterstr) < 4 || strlen(filterstr) > 5
549 || g_ascii_strncasecmp(filterstr, "bit", 3) != 0)
552 sscanf(&filterstr[3], "%d", &bit);
553 if (bit >= 0 && bit <= FILTER_BIT_MAX)
559 static int set_field(guint64 *filter, const char *filterstr)
563 mask = get_filter_mask(filterstr);
572 static GObexApparam *parse_fields(GObexApparam *apparam, DBusMessageIter *iter)
574 DBusMessageIter array;
577 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
580 dbus_message_iter_recurse(iter, &array);
582 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
585 dbus_message_iter_get_basic(&array, &string);
587 if (set_field(&filter, string) < 0)
590 dbus_message_iter_next(&array);
593 return g_obex_apparam_set_uint64(apparam, FILTER_TAG, filter);
596 static GObexApparam *parse_filters(GObexApparam *apparam,
597 DBusMessageIter *iter)
599 DBusMessageIter array;
601 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
604 dbus_message_iter_recurse(iter, &array);
606 while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
608 DBusMessageIter value, entry;
610 dbus_message_iter_recurse(&array, &entry);
611 dbus_message_iter_get_basic(&entry, &key);
613 dbus_message_iter_next(&entry);
614 dbus_message_iter_recurse(&entry, &value);
616 if (strcasecmp(key, "Format") == 0) {
617 if (parse_format(apparam, &value) == NULL)
619 } else if (strcasecmp(key, "Order") == 0) {
620 if (parse_order(apparam, &value) == NULL)
622 } else if (strcasecmp(key, "Offset") == 0) {
623 if (parse_offset(apparam, &value) == NULL)
625 } else if (strcasecmp(key, "MaxCount") == 0) {
626 if (parse_max_count(apparam, &value) == NULL)
628 } else if (strcasecmp(key, "Fields") == 0) {
629 if (parse_fields(apparam, &value) == NULL)
633 dbus_message_iter_next(&array);
639 static DBusMessage *pull_phonebook(struct pbap_data *pbap,
640 DBusMessage *message,
642 const char *targetfile,
643 GObexApparam *apparam)
645 struct pending_request *request;
646 struct obc_transfer *transfer;
648 session_callback_t func;
652 name = g_strconcat(g_path_skip_root(pbap->path), ".vcf", NULL);
654 transfer = obc_transfer_get("x-bt/phonebook", name, targetfile, &err);
655 if (transfer == NULL) {
656 g_obex_apparam_free(apparam);
665 case GETPHONEBOOKSIZE:
666 func = phonebook_size_callback;
667 request = pending_request_new(pbap, message);
670 error("Unexpected type : 0x%2x", type);
674 obc_transfer_set_apparam(transfer, apparam);
676 if (!obc_session_queue(pbap->session, transfer, func, request, &err)) {
678 pending_request_free(request);
685 if (targetfile == NULL)
688 return obc_transfer_create_dbus_reply(transfer, message);
692 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
698 static DBusMessage *pull_vcard_listing(struct pbap_data *pbap,
699 DBusMessage *message, const char *name,
700 GObexApparam *apparam)
702 struct pending_request *request;
703 struct obc_transfer *transfer;
707 transfer = obc_transfer_get("x-bt/vcard-listing", name, NULL, &err);
708 if (transfer == NULL) {
709 g_obex_apparam_free(apparam);
713 obc_transfer_set_apparam(transfer, apparam);
715 request = pending_request_new(pbap, message);
716 if (obc_session_queue(pbap->session, transfer,
717 pull_vcard_listing_callback, request, &err))
720 pending_request_free(request);
723 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
729 static DBusMessage *pbap_select(DBusConnection *connection,
730 DBusMessage *message, void *user_data)
732 struct pbap_data *pbap = user_data;
733 const char *item, *location;
735 struct pending_request *request;
738 if (dbus_message_get_args(message, NULL,
739 DBUS_TYPE_STRING, &location,
740 DBUS_TYPE_STRING, &item,
741 DBUS_TYPE_INVALID) == FALSE)
742 return g_dbus_create_error(message,
743 ERROR_INTERFACE ".InvalidArguments", NULL);
745 path = build_phonebook_path(location, item);
747 return g_dbus_create_error(message,
748 ERROR_INTERFACE ".InvalidArguments",
751 if (pbap->path != NULL && g_str_equal(pbap->path, path)) {
753 return dbus_message_new_method_return(message);
756 request = pending_request_new(pbap, message);
757 #ifdef __TIZEN_PATCH__
758 if (pbap->path == NULL || strlen(pbap->path) == 0)
759 obc_session_setpath(pbap->session, path + 1, pbap_setpath_cb, request,
762 obc_session_setpath(pbap->session, path, pbap_setpath_cb, request,
765 obc_session_setpath(pbap->session, path, pbap_setpath_cb, request, &err);
769 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
773 pending_request_free(request);
783 static DBusMessage *pbap_pull_all(DBusConnection *connection,
784 DBusMessage *message, void *user_data)
786 struct pbap_data *pbap = user_data;
787 const char *targetfile;
788 GObexApparam *apparam;
789 DBusMessageIter args;
792 return g_dbus_create_error(message,
793 ERROR_INTERFACE ".Forbidden",
794 "Call Select first of all");
796 dbus_message_iter_init(message, &args);
798 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
799 return g_dbus_create_error(message,
800 ERROR_INTERFACE ".InvalidArguments", NULL);
802 dbus_message_iter_get_basic(&args, &targetfile);
803 dbus_message_iter_next(&args);
805 apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
807 apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
810 if (parse_filters(apparam, &args) == NULL) {
811 g_obex_apparam_free(apparam);
812 return g_dbus_create_error(message,
813 ERROR_INTERFACE ".InvalidArguments", NULL);
816 return pull_phonebook(pbap, message, PULLPHONEBOOK, targetfile,
820 static DBusMessage *pull_vcard(struct pbap_data *pbap, DBusMessage *message,
821 const char *name, const char *targetfile,
822 GObexApparam *apparam)
824 struct obc_transfer *transfer;
828 transfer = obc_transfer_get("x-bt/vcard", name, targetfile, &err);
829 if (transfer == NULL) {
830 g_obex_apparam_free(apparam);
834 obc_transfer_set_apparam(transfer, apparam);
836 if (!obc_session_queue(pbap->session, transfer, NULL, NULL, &err))
839 return obc_transfer_create_dbus_reply(transfer, message);
842 reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
848 static DBusMessage *pbap_pull_vcard(DBusConnection *connection,
849 DBusMessage *message, void *user_data)
851 struct pbap_data *pbap = user_data;
852 GObexApparam *apparam;
853 const char *name, *targetfile;
854 DBusMessageIter args;
857 return g_dbus_create_error(message,
858 ERROR_INTERFACE ".Forbidden",
859 "Call Select first of all");
861 dbus_message_iter_init(message, &args);
863 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
864 return g_dbus_create_error(message,
865 ERROR_INTERFACE ".InvalidArguments", NULL);
867 dbus_message_iter_get_basic(&args, &name);
868 dbus_message_iter_next(&args);
870 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
871 return g_dbus_create_error(message,
872 ERROR_INTERFACE ".InvalidArguments", NULL);
874 dbus_message_iter_get_basic(&args, &targetfile);
875 dbus_message_iter_next(&args);
877 apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
879 apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
882 if (parse_filters(apparam, &args) == NULL) {
883 g_obex_apparam_free(apparam);
884 return g_dbus_create_error(message,
885 ERROR_INTERFACE ".InvalidArguments", NULL);
888 return pull_vcard(pbap, message, name, targetfile, apparam);
891 static DBusMessage *pbap_list(DBusConnection *connection,
892 DBusMessage *message, void *user_data)
894 struct pbap_data *pbap = user_data;
895 GObexApparam *apparam;
896 DBusMessageIter args;
897 #ifdef __TIZEN_PATCH__
898 const char *pb_folder;
902 return g_dbus_create_error(message,
903 ERROR_INTERFACE ".Forbidden",
904 "Call Select first of all");
906 dbus_message_iter_init(message, &args);
908 #ifdef __TIZEN_PATCH__
909 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
910 return g_dbus_create_error(message,
911 ERROR_INTERFACE ".InvalidArguments", NULL);
913 dbus_message_iter_get_basic(&args, &pb_folder);
914 dbus_message_iter_next(&args);
917 apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
919 apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
922 if (parse_filters(apparam, &args) == NULL) {
923 g_obex_apparam_free(apparam);
924 return g_dbus_create_error(message,
925 ERROR_INTERFACE ".InvalidArguments", NULL);
928 #ifdef __TIZEN_PATCH__
929 return pull_vcard_listing(pbap, message, pb_folder, apparam);
931 return pull_vcard_listing(pbap, message, "", apparam);
935 static GObexApparam *parse_attribute(GObexApparam *apparam, const char *field)
939 if (!field || g_str_equal(field, ""))
940 attrib = ATTRIB_NAME;
941 else if (!g_ascii_strcasecmp(field, "name"))
942 attrib = ATTRIB_NAME;
943 else if (!g_ascii_strcasecmp(field, "number"))
944 attrib = ATTRIB_NUMBER;
945 else if (!g_ascii_strcasecmp(field, "sound"))
946 attrib = ATTRIB_SOUND;
950 return g_obex_apparam_set_uint8(apparam, SEARCHATTRIB_TAG, attrib);
953 static DBusMessage *pbap_search(DBusConnection *connection,
954 DBusMessage *message, void *user_data)
956 struct pbap_data *pbap = user_data;
958 GObexApparam *apparam;
959 DBusMessageIter args;
962 return g_dbus_create_error(message,
963 ERROR_INTERFACE ".Forbidden",
964 "Call Select first of all");
966 dbus_message_iter_init(message, &args);
968 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
969 return g_dbus_create_error(message,
970 ERROR_INTERFACE ".InvalidArguments", NULL);
972 dbus_message_iter_get_basic(&args, &field);
973 dbus_message_iter_next(&args);
975 apparam = parse_attribute(NULL, field);
977 return g_dbus_create_error(message,
978 ERROR_INTERFACE ".InvalidArguments", NULL);
980 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
981 return g_dbus_create_error(message,
982 ERROR_INTERFACE ".InvalidArguments", NULL);
984 dbus_message_iter_get_basic(&args, &value);
985 dbus_message_iter_next(&args);
987 apparam = g_obex_apparam_set_uint16(apparam, MAXLISTCOUNT_TAG,
989 apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
991 apparam = g_obex_apparam_set_string(apparam, SEARCHVALUE_TAG, value);
993 if (parse_filters(apparam, &args) == NULL) {
994 g_obex_apparam_free(apparam);
995 return g_dbus_create_error(message,
996 ERROR_INTERFACE ".InvalidArguments", NULL);
999 return pull_vcard_listing(pbap, message, "", apparam);
1002 static DBusMessage *pbap_get_size(DBusConnection *connection,
1003 DBusMessage *message, void *user_data)
1005 struct pbap_data *pbap = user_data;
1006 GObexApparam *apparam;
1007 DBusMessageIter args;
1010 return g_dbus_create_error(message,
1011 ERROR_INTERFACE ".Forbidden",
1012 "Call Select first of all");
1014 dbus_message_iter_init(message, &args);
1016 apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, 0);
1017 apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
1020 return pull_phonebook(pbap, message, GETPHONEBOOKSIZE, NULL, apparam);
1023 static char **get_filter_strs(uint64_t filter, int *size)
1025 char **list, **item;
1028 list = g_malloc0(sizeof(char **) * (FILTER_BIT_MAX + 2));
1032 for (i = 0; filter_list[i] != NULL; i++)
1033 if (filter & (1ULL << i))
1034 *(item++) = g_strdup(filter_list[i]);
1036 for (; i <= FILTER_BIT_MAX; i++)
1037 if (filter & (1ULL << i))
1038 *(item++) = g_strdup_printf("%s%d", "BIT", i);
1041 *size = item - list;
1045 static DBusMessage *pbap_list_filter_fields(DBusConnection *connection,
1046 DBusMessage *message, void *user_data)
1048 char **filters = NULL;
1052 filters = get_filter_strs(FILTER_ALL, &size);
1053 reply = dbus_message_new_method_return(message);
1054 dbus_message_append_args(reply, DBUS_TYPE_ARRAY,
1055 DBUS_TYPE_STRING, &filters, size,
1058 g_strfreev(filters);
1062 static DBusMessage *pbap_update_version(DBusConnection *connection,
1063 DBusMessage *message, void *user_data)
1065 struct pbap_data *pbap = user_data;
1067 if (!(pbap->supported_features & FOLDER_VERSION_FEATURE))
1068 return g_dbus_create_error(message,
1069 ERROR_INTERFACE ".NotSupported",
1070 "Operation is not supported");
1072 return pbap_get_size(connection, message, user_data);
1075 static const GDBusMethodTable pbap_methods[] = {
1076 { GDBUS_ASYNC_METHOD("Select",
1077 GDBUS_ARGS({ "location", "s" }, { "phonebook", "s" }),
1078 NULL, pbap_select) },
1079 { GDBUS_METHOD("PullAll",
1080 GDBUS_ARGS({ "targetfile", "s" },
1081 { "filters", "a{sv}" }),
1082 GDBUS_ARGS({ "transfer", "o" },
1083 { "properties", "a{sv}" }),
1085 { GDBUS_METHOD("Pull",
1086 GDBUS_ARGS({ "vcard", "s" }, { "targetfile", "s" },
1087 { "filters", "a{sv}" }),
1088 GDBUS_ARGS({ "transfer", "o" },
1089 { "properties", "a{sv}" }),
1091 { GDBUS_ASYNC_METHOD("List",
1092 #ifdef __TIZEN_PATCH__
1093 GDBUS_ARGS({ "folder", "s" }, {"filters", "a{sv}" }),
1095 GDBUS_ARGS({"filters", "a{sv}" }),
1097 GDBUS_ARGS({ "vcard_listing", "a(ss)" }),
1099 { GDBUS_ASYNC_METHOD("Search",
1100 GDBUS_ARGS({ "field", "s" }, { "value", "s" },
1101 { "filters", "a{sv}" }),
1102 GDBUS_ARGS({ "vcard_listing", "a(ss)" }),
1104 { GDBUS_ASYNC_METHOD("GetSize",
1105 NULL, GDBUS_ARGS({ "size", "q" }),
1107 { GDBUS_METHOD("ListFilterFields",
1108 NULL, GDBUS_ARGS({ "fields", "as" }),
1109 pbap_list_filter_fields) },
1110 { GDBUS_ASYNC_METHOD("UpdateVersion", NULL, NULL,
1111 pbap_update_version) },
1115 static gboolean folder_exists(const GDBusPropertyTable *property, void *data)
1117 struct pbap_data *pbap = data;
1119 return pbap->path != NULL;
1122 static gboolean get_folder(const GDBusPropertyTable *property,
1123 DBusMessageIter *iter, void *data)
1125 struct pbap_data *pbap = data;
1130 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pbap->path);
1135 static gboolean databaseid_exists(const GDBusPropertyTable *property,
1138 struct pbap_data *pbap = data;
1140 return pbap->supported_features & DATABASEID_FEATURE;
1143 static int u128_to_string(uint8_t *data, char *str, size_t len)
1145 return snprintf(str, len, "%02X%02X%02X%02X%02X%02X%02X%02X"
1146 "%02X%02X%02X%02X%02X%02X%02X%02X",
1147 data[0], data[1], data[2], data[3],
1148 data[3], data[5], data[6], data[7],
1149 data[8], data[9], data[10], data[11],
1150 data[12], data[13], data[14], data[15]);
1153 static gboolean get_databaseid(const GDBusPropertyTable *property,
1154 DBusMessageIter *iter, void *data)
1156 struct pbap_data *pbap = data;
1158 const char *pvalue = value;
1160 if (u128_to_string(pbap->databaseid, value, sizeof(value)) < 0)
1163 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue);
1168 static gboolean version_exists(const GDBusPropertyTable *property,
1171 struct pbap_data *pbap = data;
1173 return pbap->supported_features & FOLDER_VERSION_FEATURE;
1176 static gboolean get_primary(const GDBusPropertyTable *property,
1177 DBusMessageIter *iter, void *data)
1179 struct pbap_data *pbap = data;
1181 const char *pvalue = value;
1183 if (u128_to_string(pbap->primary, value, sizeof(value)) < 0)
1186 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue);
1191 static gboolean get_secondary(const GDBusPropertyTable *property,
1192 DBusMessageIter *iter, void *data)
1194 struct pbap_data *pbap = data;
1196 const char *pvalue = value;
1198 if (u128_to_string(pbap->secondary, value, sizeof(value)) < 0)
1201 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue);
1206 static gboolean image_size_exists(const GDBusPropertyTable *property,
1209 struct pbap_data *pbap = data;
1211 return pbap->supported_features & DEFAULT_IMAGE_FEATURE;
1214 static gboolean get_image_size(const GDBusPropertyTable *property,
1215 DBusMessageIter *iter, void *data)
1217 dbus_bool_t value = TRUE;
1219 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1224 static const GDBusPropertyTable pbap_properties[] = {
1225 { "Folder", "s", get_folder, NULL, folder_exists },
1226 { "DatabaseIdentifier", "s", get_databaseid, NULL, databaseid_exists },
1227 { "PrimaryCounter", "s", get_primary, NULL, version_exists },
1228 { "SecondaryCounter", "s", get_secondary, NULL, version_exists },
1229 { "FixedImageSize", "b", get_image_size, NULL, image_size_exists },
1233 static void pbap_free(void *data)
1235 struct pbap_data *pbap = data;
1237 obc_session_unref(pbap->session);
1242 static void parse_service_record(struct pbap_data *pbap)
1247 data = obc_session_get_attribute(pbap->session,
1248 SDP_ATTR_PFILE_DESC_LIST);
1252 pbap->version = GPOINTER_TO_UINT(data);
1255 * If the PbapSupportedFeatures attribute is not present
1256 * 0x00000003 shall be assumed for a remote PSE.
1258 pbap->supported_features = 0x00000003;
1260 if (pbap->version < 0x0102)
1263 /* Supported Feature Bits */
1264 data = obc_session_get_attribute(pbap->session,
1265 SDP_ATTR_PBAP_SUPPORTED_FEATURES);
1267 pbap->supported_features = *(uint32_t *) data;
1271 static void *pbap_supported_features(struct obc_session *session)
1277 data = obc_session_get_attribute(session, SDP_ATTR_PFILE_DESC_LIST);
1281 version = GPOINTER_TO_UINT(data);
1283 if (version < 0x0102)
1286 /* Supported Feature Bits */
1287 data = obc_session_get_attribute(session,
1288 SDP_ATTR_PBAP_SUPPORTED_FEATURES);
1292 return g_obex_apparam_set_uint32(NULL, SUPPORTED_FEATURES_TAG,
1295 DATABASEID_FEATURE |
1296 FOLDER_VERSION_FEATURE |
1297 VCARD_SELECTING_FEATURE |
1298 ENHANCED_CALLS_FEATURE |
1301 REFERENCING_FEATURE |
1302 DEFAULT_IMAGE_FEATURE);
1305 static int pbap_probe(struct obc_session *session)
1307 struct pbap_data *pbap;
1310 path = obc_session_get_path(session);
1314 pbap = g_try_new0(struct pbap_data, 1);
1318 pbap->session = obc_session_ref(session);
1320 parse_service_record(pbap);
1322 DBG("%s, version 0x%04x supported features 0x%08x", path, pbap->version,
1323 pbap->supported_features);
1325 if (!g_dbus_register_interface(conn, path, PBAP_INTERFACE, pbap_methods,
1326 NULL, pbap_properties, pbap,
1335 static void pbap_remove(struct obc_session *session)
1337 const char *path = obc_session_get_path(session);
1341 g_dbus_unregister_interface(conn, path, PBAP_INTERFACE);
1344 static struct obc_driver pbap = {
1347 .target = OBEX_PBAP_UUID,
1348 .target_len = OBEX_PBAP_UUID_LEN,
1349 .supported_features = pbap_supported_features,
1350 .probe = pbap_probe,
1351 .remove = pbap_remove
1360 conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
1364 err = obc_driver_register(&pbap);
1366 dbus_connection_unref(conn);
1374 void pbap_exit(void)
1378 dbus_connection_unref(conn);
1381 obc_driver_unregister(&pbap);