tizen 2.3.1 release
[framework/connectivity/bluez.git] / obexd / client / pbap.c
1 /*
2  *
3  *  OBEX Client
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation
6  *  Copyright (C) 2007-2010  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
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.
13  *
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.
18  *
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
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <errno.h>
30 #include <string.h>
31 #include <stdio.h>
32
33 #include <glib.h>
34
35 #include "lib/bluetooth.h"
36 #include "lib/sdp.h"
37
38 #include "gobex/gobex-apparam.h"
39 #include "gdbus/gdbus.h"
40
41 #include "obexd/src/log.h"
42
43 #include "transfer.h"
44 #include "session.h"
45 #include "driver.h"
46 #include "pbap.h"
47
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
51
52 #define FORMAT_VCARD21  0x0
53 #define FORMAT_VCARD30  0x1
54
55 #define ORDER_INDEXED           0x0
56 #define ORDER_ALPHANUMERIC      0x1
57 #define ORDER_PHONETIC          0x2
58
59 #define ATTRIB_NAME             0x0
60 #define ATTRIB_NUMBER           0x1
61 #define ATTRIB_SOUND            0x2
62
63 #define DEFAULT_COUNT   65535
64 #define DEFAULT_OFFSET  0
65
66 #define PULLPHONEBOOK           0x1
67 #define GETPHONEBOOKSIZE        0x2
68
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
82
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
93
94 static const char *filter_list[] = {
95         "VERSION",
96         "FN",
97         "N",
98         "PHOTO",
99         "BDAY",
100         "ADR",
101         "LABEL",
102         "TEL",
103         "EMAIL",
104         "MAILER",
105         "TZ",
106         "GEO",
107         "TITLE",
108         "ROLE",
109         "LOGO",
110         "AGENT",
111         "ORG",
112         "NOTE",
113         "REV",
114         "SOUND",
115         "URL",
116         "UID",
117         "KEY",
118         "NICKNAME",
119         "CATEGORIES",
120         "PROID",
121         "CLASS",
122         "SORT-STRING",
123         "X-IRMC-CALL-DATETIME",
124         "X-BT-SPEEDDIALKEY",
125         "X-BT-UCI",
126         "X-BT-UID",
127         NULL
128 };
129
130 #define FILTER_BIT_MAX  63
131 #define FILTER_ALL      0xFFFFFFFFFFFFFFFFULL
132
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"
136
137 struct pbap_data {
138         struct obc_session *session;
139         char *path;
140         uint16_t version;
141         uint32_t supported_features;
142         uint8_t databaseid[16];
143         uint8_t primary[16];
144         uint8_t secondary[16];
145 };
146
147 struct pending_request {
148         struct pbap_data *pbap;
149         DBusMessage *msg;
150 };
151
152 static DBusConnection *conn = NULL;
153
154 static struct pending_request *pending_request_new(struct pbap_data *pbap,
155                                                         DBusMessage *message)
156 {
157         struct pending_request *p;
158
159         p = g_new0(struct pending_request, 1);
160         p->pbap = pbap;
161         p->msg = dbus_message_ref(message);
162
163         return p;
164 }
165
166 static void pending_request_free(struct pending_request *p)
167 {
168         dbus_message_unref(p->msg);
169         g_free(p);
170 }
171
172 static void listing_element(GMarkupParseContext *ctxt,
173                                 const char *element,
174                                 const char **names,
175                                 const char **values,
176                                 gpointer user_data,
177                                 GError **gerr)
178 {
179         DBusMessageIter *item = user_data, entry;
180         char **key;
181         const char *handle = NULL, *vcardname = NULL;
182
183         if (g_str_equal(element, "card") != TRUE)
184                 return;
185
186         for (key = (char **) names; *key; key++, values++) {
187                 if (g_str_equal(*key, "handle") == TRUE)
188                         handle = *values;
189                 else if (g_str_equal(*key, "name") == TRUE)
190                         vcardname = *values;
191         }
192
193         if (!handle || !vcardname)
194                 return;
195
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);
200 }
201
202 static const GMarkupParser listing_parser = {
203         listing_element,
204         NULL,
205         NULL,
206         NULL,
207         NULL
208 };
209
210 static char *build_phonebook_path(const char *location, const char *item)
211 {
212         char *path = NULL, *tmp, *tmp1;
213         gboolean internal = FALSE;
214
215         if (!g_ascii_strcasecmp(location, "int") ||
216                         !g_ascii_strcasecmp(location, "internal")) {
217                 path = g_strdup("/telecom");
218                 internal = TRUE;
219         } else if (!g_ascii_strncasecmp(location, "sim", 3)) {
220                 if (strlen(location) == 3)
221                         tmp = g_strdup("sim1");
222                 else
223                         tmp = g_ascii_strup(location, 4);
224
225                 path = g_build_filename("/", tmp, "telecom", NULL);
226                 g_free(tmp);
227         } else
228                 return NULL;
229
230 #ifdef __TIZEN_PATCH__
231         if (!g_ascii_strcasecmp(item, "nil"))
232                 return path;
233 #endif
234
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"))) {
242                 tmp = path;
243                 tmp1 = g_ascii_strdown(item, -1);
244                 path = g_build_filename(tmp, tmp1, NULL);
245                 g_free(tmp);
246                 g_free(tmp1);
247         } else {
248                 g_free(path);
249                 return NULL;
250         }
251
252         return path;
253 }
254
255 /* should only be called inside pbap_set_path */
256 static void pbap_reset_path(struct pbap_data *pbap)
257 {
258         if (!pbap->path)
259                 return;
260
261         obc_session_setpath(pbap->session, pbap->path, NULL, NULL, NULL);
262 }
263
264 static void pbap_setpath_cb(struct obc_session *session,
265                                                 struct obc_transfer *transfer,
266                                                 GError *err, void *user_data)
267 {
268         struct pending_request *request = user_data;
269         struct pbap_data *pbap = request->pbap;
270
271         if (err != NULL)
272                 pbap_reset_path(pbap);
273         else
274                 g_dbus_emit_property_changed(conn,
275                                         obc_session_get_path(pbap->session),
276                                         PBAP_INTERFACE, "Folder");
277
278         if (err) {
279                 DBusMessage *reply = g_dbus_create_error(request->msg,
280                                                 ERROR_INTERFACE ".Failed",
281                                                 "%s", err->message);
282                 g_dbus_send_message(conn, reply);
283         } else
284                 g_dbus_send_reply(conn, request->msg, DBUS_TYPE_INVALID);
285
286         pending_request_free(request);
287 }
288
289 static void read_version(struct pbap_data *pbap, GObexApparam *apparam)
290 {
291         const guint8 *data;
292         uint8_t value[16];
293         gsize len;
294
295         if (!(pbap->supported_features & FOLDER_VERSION_FEATURE))
296                 return;
297
298         if (!g_obex_apparam_get_bytes(apparam, PRIMARY_COUNTER_TAG, &data,
299                                                                 &len)) {
300                 len = sizeof(value);
301                 memset(value, 0, len);
302                 data = value;
303         }
304
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");
310         }
311
312         if (!g_obex_apparam_get_bytes(apparam, SECONDARY_COUNTER_TAG, &data,
313                                                                 &len)) {
314                 len = sizeof(value);
315                 memset(value, 0, len);
316                 data = value;
317         }
318
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");
324         }
325 }
326
327 static void read_databaseid(struct pbap_data *pbap, GObexApparam *apparam)
328 {
329         const guint8 *data;
330         guint8 value[16];
331         gsize len;
332
333         if (!(pbap->supported_features & DATABASEID_FEATURE))
334                 return;
335
336         if (!g_obex_apparam_get_bytes(apparam, DATABASEID_TAG, &data, &len)) {
337                 len = sizeof(value);
338                 memset(value, 0, len);
339                 data = value;
340         }
341
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");
347         }
348 }
349
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)
354 {
355         GObexApparam *apparam;
356
357         *phone_book_size = 0;
358         *new_missed_calls = 0;
359
360         apparam = obc_transfer_get_apparam(transfer);
361         if (apparam == NULL)
362                 return;
363
364         g_obex_apparam_get_uint16(apparam, PHONEBOOKSIZE_TAG,
365                                                         phone_book_size);
366         g_obex_apparam_get_uint8(apparam, NEWMISSEDCALLS_TAG,
367                                                         new_missed_calls);
368
369         read_version(pbap, apparam);
370         read_databaseid(pbap, apparam);
371 }
372
373 static void phonebook_size_callback(struct obc_session *session,
374                                                 struct obc_transfer *transfer,
375                                                 GError *err, void *user_data)
376 {
377         struct pending_request *request = user_data;
378         DBusMessage *reply;
379         guint16 phone_book_size;
380         guint8 new_missed_calls;
381
382         if (err) {
383                 reply = g_dbus_create_error(request->msg,
384                                                 ERROR_INTERFACE ".Failed",
385                                                 "%s", err->message);
386                 goto send;
387         }
388
389         reply = dbus_message_new_method_return(request->msg);
390
391         read_return_apparam(transfer, request->pbap, &phone_book_size,
392                                                         &new_missed_calls);
393
394         if (dbus_message_is_method_call(request->msg, PBAP_INTERFACE,
395                                                                 "GetSize"))
396                 dbus_message_append_args(reply,
397                                         DBUS_TYPE_UINT16, &phone_book_size,
398                                         DBUS_TYPE_INVALID);
399
400 send:
401         g_dbus_send_message(conn, reply);
402         pending_request_free(request);
403 }
404
405 static void pull_vcard_listing_callback(struct obc_session *session,
406                                                 struct obc_transfer *transfer,
407                                                 GError *err, void *user_data)
408 {
409         struct pending_request *request = user_data;
410         GMarkupParseContext *ctxt;
411         DBusMessage *reply;
412         DBusMessageIter iter, array;
413         char *contents;
414         size_t size;
415         int perr;
416
417         if (err) {
418                 reply = g_dbus_create_error(request->msg,
419                                                 ERROR_INTERFACE ".Failed",
420                                                 "%s", err->message);
421                 goto send;
422         }
423
424         perr = obc_transfer_get_contents(transfer, &contents, &size);
425         if (perr < 0) {
426                 reply = g_dbus_create_error(request->msg,
427                                                 ERROR_INTERFACE ".Failed",
428                                                 "Error reading contents: %s",
429                                                 strerror(-perr));
430                 goto send;
431         }
432
433         reply = dbus_message_new_method_return(request->msg);
434
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);
444         g_free(contents);
445
446 send:
447         g_dbus_send_message(conn, reply);
448         pending_request_free(request);
449 }
450
451 static GObexApparam *parse_format(GObexApparam *apparam, DBusMessageIter *iter)
452 {
453         const char *string;
454         guint8 format;
455
456         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
457                 return NULL;
458
459         dbus_message_iter_get_basic(iter, &string);
460
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;
467         else
468                 return NULL;
469
470         return g_obex_apparam_set_uint8(apparam, FORMAT_TAG, format);
471 }
472
473 static GObexApparam *parse_order(GObexApparam *apparam, DBusMessageIter *iter)
474 {
475         const char *string;
476         guint8 order;
477
478         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
479                 return NULL;
480
481         dbus_message_iter_get_basic(iter, &string);
482
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;
491         else
492                 return NULL;
493
494         return g_obex_apparam_set_uint8(apparam, ORDER_TAG, order);
495 }
496
497 static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter)
498 {
499         guint16 num;
500
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)
504                 return NULL;
505 #else
506         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
507                 return NULL;
508 #endif
509
510         dbus_message_iter_get_basic(iter, &num);
511
512         return g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, num);
513 }
514
515 static GObexApparam *parse_max_count(GObexApparam *apparam,
516                                                         DBusMessageIter *iter)
517 {
518         guint16 num;
519
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)
523                 return NULL;
524 #else
525         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
526                 return NULL;
527 #endif
528
529         dbus_message_iter_get_basic(iter, &num);
530
531         return g_obex_apparam_set_uint16(apparam, MAXLISTCOUNT_TAG, num);
532 }
533
534 static uint64_t get_filter_mask(const char *filterstr)
535 {
536         int i, bit = -1;
537
538         if (!filterstr)
539                 return 0;
540
541         if (!g_ascii_strcasecmp(filterstr, "ALL"))
542                 return FILTER_ALL;
543
544         for (i = 0; filter_list[i] != NULL; i++)
545                 if (!g_ascii_strcasecmp(filterstr, filter_list[i]))
546                         return 1ULL << i;
547
548         if (strlen(filterstr) < 4 || strlen(filterstr) > 5
549                         || g_ascii_strncasecmp(filterstr, "bit", 3) != 0)
550                 return 0;
551
552         sscanf(&filterstr[3], "%d", &bit);
553         if (bit >= 0 && bit <= FILTER_BIT_MAX)
554                 return 1ULL << bit;
555         else
556                 return 0;
557 }
558
559 static int set_field(guint64 *filter, const char *filterstr)
560 {
561         guint64 mask;
562
563         mask = get_filter_mask(filterstr);
564
565         if (mask == 0)
566                 return -EINVAL;
567
568         *filter |= mask;
569         return 0;
570 }
571
572 static GObexApparam *parse_fields(GObexApparam *apparam, DBusMessageIter *iter)
573 {
574         DBusMessageIter array;
575         guint64 filter = 0;
576
577         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
578                 return NULL;
579
580         dbus_message_iter_recurse(iter, &array);
581
582         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
583                 const char *string;
584
585                 dbus_message_iter_get_basic(&array, &string);
586
587                 if (set_field(&filter, string) < 0)
588                         return NULL;
589
590                 dbus_message_iter_next(&array);
591         }
592
593         return g_obex_apparam_set_uint64(apparam, FILTER_TAG, filter);
594 }
595
596 static GObexApparam *parse_filters(GObexApparam *apparam,
597                                                         DBusMessageIter *iter)
598 {
599         DBusMessageIter array;
600
601         if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
602                 return NULL;
603
604         dbus_message_iter_recurse(iter, &array);
605
606         while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
607                 const char *key;
608                 DBusMessageIter value, entry;
609
610                 dbus_message_iter_recurse(&array, &entry);
611                 dbus_message_iter_get_basic(&entry, &key);
612
613                 dbus_message_iter_next(&entry);
614                 dbus_message_iter_recurse(&entry, &value);
615
616                 if (strcasecmp(key, "Format") == 0) {
617                         if (parse_format(apparam, &value) == NULL)
618                                 return NULL;
619                 } else if (strcasecmp(key, "Order") == 0) {
620                         if (parse_order(apparam, &value) == NULL)
621                                 return NULL;
622                 } else if (strcasecmp(key, "Offset") == 0) {
623                         if (parse_offset(apparam, &value) == NULL)
624                                 return NULL;
625                 } else if (strcasecmp(key, "MaxCount") == 0) {
626                         if (parse_max_count(apparam, &value) == NULL)
627                                 return NULL;
628                 } else if (strcasecmp(key, "Fields") == 0) {
629                         if (parse_fields(apparam, &value) == NULL)
630                                 return NULL;
631                 }
632
633                 dbus_message_iter_next(&array);
634         }
635
636         return apparam;
637 }
638
639 static DBusMessage *pull_phonebook(struct pbap_data *pbap,
640                                                 DBusMessage *message,
641                                                 guint8 type,
642                                                 const char *targetfile,
643                                                 GObexApparam *apparam)
644 {
645         struct pending_request *request;
646         struct obc_transfer *transfer;
647         char *name;
648         session_callback_t func;
649         DBusMessage *reply;
650         GError *err = NULL;
651
652         name = g_strconcat(g_path_skip_root(pbap->path), ".vcf", NULL);
653
654         transfer = obc_transfer_get("x-bt/phonebook", name, targetfile, &err);
655         if (transfer == NULL) {
656                 g_obex_apparam_free(apparam);
657                 goto fail;
658         }
659
660         switch (type) {
661         case PULLPHONEBOOK:
662                 func = NULL;
663                 request = NULL;
664                 break;
665         case GETPHONEBOOKSIZE:
666                 func = phonebook_size_callback;
667                 request = pending_request_new(pbap, message);
668                 break;
669         default:
670                 error("Unexpected type : 0x%2x", type);
671                 return NULL;
672         }
673
674         obc_transfer_set_apparam(transfer, apparam);
675
676         if (!obc_session_queue(pbap->session, transfer, func, request, &err)) {
677                 if (request != NULL)
678                         pending_request_free(request);
679
680                 goto fail;
681         }
682
683         g_free(name);
684
685         if (targetfile == NULL)
686                 return NULL;
687
688         return obc_transfer_create_dbus_reply(transfer, message);
689
690 fail:
691         g_free(name);
692         reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
693                                                                 err->message);
694         g_error_free(err);
695         return reply;
696 }
697
698 static DBusMessage *pull_vcard_listing(struct pbap_data *pbap,
699                                         DBusMessage *message, const char *name,
700                                         GObexApparam *apparam)
701 {
702         struct pending_request *request;
703         struct obc_transfer *transfer;
704         GError *err = NULL;
705         DBusMessage *reply;
706
707         transfer = obc_transfer_get("x-bt/vcard-listing", name, NULL, &err);
708         if (transfer == NULL) {
709                 g_obex_apparam_free(apparam);
710                 goto fail;
711         }
712
713         obc_transfer_set_apparam(transfer, apparam);
714
715         request = pending_request_new(pbap, message);
716         if (obc_session_queue(pbap->session, transfer,
717                                 pull_vcard_listing_callback, request, &err))
718                 return NULL;
719
720         pending_request_free(request);
721
722 fail:
723         reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
724                                                                 err->message);
725         g_error_free(err);
726         return reply;
727 }
728
729 static DBusMessage *pbap_select(DBusConnection *connection,
730                                         DBusMessage *message, void *user_data)
731 {
732         struct pbap_data *pbap = user_data;
733         const char *item, *location;
734         char *path;
735         struct pending_request *request;
736         GError *err = NULL;
737
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);
744
745         path = build_phonebook_path(location, item);
746         if (path == NULL)
747                 return g_dbus_create_error(message,
748                                         ERROR_INTERFACE ".InvalidArguments",
749                                         "Invalid path");
750
751         if (pbap->path != NULL && g_str_equal(pbap->path, path)) {
752                 g_free(path);
753                 return dbus_message_new_method_return(message);
754         }
755
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,
760                                                                                 &err);
761         else
762                 obc_session_setpath(pbap->session, path, pbap_setpath_cb, request,
763                                                                                 &err);
764 #else
765         obc_session_setpath(pbap->session, path, pbap_setpath_cb, request, &err);
766 #endif
767         if (err != NULL) {
768                 DBusMessage *reply;
769                 reply =  g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
770                                                         "%s", err->message);
771                 g_error_free(err);
772                 g_free(path);
773                 pending_request_free(request);
774                 return reply;
775         }
776
777         g_free(pbap->path);
778         pbap->path = path;
779
780         return NULL;
781 }
782
783 static DBusMessage *pbap_pull_all(DBusConnection *connection,
784                                         DBusMessage *message, void *user_data)
785 {
786         struct pbap_data *pbap = user_data;
787         const char *targetfile;
788         GObexApparam *apparam;
789         DBusMessageIter args;
790
791         if (!pbap->path)
792                 return g_dbus_create_error(message,
793                                         ERROR_INTERFACE ".Forbidden",
794                                         "Call Select first of all");
795
796         dbus_message_iter_init(message, &args);
797
798         if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
799                 return g_dbus_create_error(message,
800                                 ERROR_INTERFACE ".InvalidArguments", NULL);
801
802         dbus_message_iter_get_basic(&args, &targetfile);
803         dbus_message_iter_next(&args);
804
805         apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
806                                                         DEFAULT_COUNT);
807         apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
808                                                         DEFAULT_OFFSET);
809
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);
814         }
815
816         return pull_phonebook(pbap, message, PULLPHONEBOOK, targetfile,
817                                                                 apparam);
818 }
819
820 static DBusMessage *pull_vcard(struct pbap_data *pbap, DBusMessage *message,
821                                 const char *name, const char *targetfile,
822                                 GObexApparam *apparam)
823 {
824         struct obc_transfer *transfer;
825         DBusMessage *reply;
826         GError *err = NULL;
827
828         transfer = obc_transfer_get("x-bt/vcard", name, targetfile, &err);
829         if (transfer == NULL) {
830                 g_obex_apparam_free(apparam);
831                 goto fail;
832         }
833
834         obc_transfer_set_apparam(transfer, apparam);
835
836         if (!obc_session_queue(pbap->session, transfer, NULL, NULL, &err))
837                 goto fail;
838
839         return obc_transfer_create_dbus_reply(transfer, message);
840
841 fail:
842         reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", "%s",
843                                                                 err->message);
844         g_error_free(err);
845         return reply;
846 }
847
848 static DBusMessage *pbap_pull_vcard(DBusConnection *connection,
849                                         DBusMessage *message, void *user_data)
850 {
851         struct pbap_data *pbap = user_data;
852         GObexApparam *apparam;
853         const char *name, *targetfile;
854         DBusMessageIter args;
855
856         if (!pbap->path)
857                 return g_dbus_create_error(message,
858                                 ERROR_INTERFACE ".Forbidden",
859                                 "Call Select first of all");
860
861         dbus_message_iter_init(message, &args);
862
863         if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
864                 return g_dbus_create_error(message,
865                                 ERROR_INTERFACE ".InvalidArguments", NULL);
866
867         dbus_message_iter_get_basic(&args, &name);
868         dbus_message_iter_next(&args);
869
870         if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
871                 return g_dbus_create_error(message,
872                                 ERROR_INTERFACE ".InvalidArguments", NULL);
873
874         dbus_message_iter_get_basic(&args, &targetfile);
875         dbus_message_iter_next(&args);
876
877         apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
878                                                         DEFAULT_COUNT);
879         apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
880                                                         DEFAULT_OFFSET);
881
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);
886         }
887
888         return pull_vcard(pbap, message, name, targetfile, apparam);
889 }
890
891 static DBusMessage *pbap_list(DBusConnection *connection,
892                                         DBusMessage *message, void *user_data)
893 {
894         struct pbap_data *pbap = user_data;
895         GObexApparam *apparam;
896         DBusMessageIter args;
897 #ifdef __TIZEN_PATCH__
898         const char *pb_folder;
899 #endif
900
901         if (!pbap->path)
902                 return g_dbus_create_error(message,
903                                         ERROR_INTERFACE ".Forbidden",
904                                         "Call Select first of all");
905
906         dbus_message_iter_init(message, &args);
907
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);
912
913         dbus_message_iter_get_basic(&args, &pb_folder);
914         dbus_message_iter_next(&args);
915 #endif
916
917         apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
918                                                         DEFAULT_COUNT);
919         apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
920                                                         DEFAULT_OFFSET);
921
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);
926         }
927
928 #ifdef __TIZEN_PATCH__
929         return pull_vcard_listing(pbap, message, pb_folder, apparam);
930 #else
931         return pull_vcard_listing(pbap, message, "", apparam);
932 #endif
933 }
934
935 static GObexApparam *parse_attribute(GObexApparam *apparam, const char *field)
936 {
937         guint8 attrib;
938
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;
947         else
948                 return NULL;
949
950         return g_obex_apparam_set_uint8(apparam, SEARCHATTRIB_TAG, attrib);
951 }
952
953 static DBusMessage *pbap_search(DBusConnection *connection,
954                                         DBusMessage *message, void *user_data)
955 {
956         struct pbap_data *pbap = user_data;
957         char *field, *value;
958         GObexApparam *apparam;
959         DBusMessageIter args;
960
961         if (!pbap->path)
962                 return g_dbus_create_error(message,
963                                         ERROR_INTERFACE ".Forbidden",
964                                         "Call Select first of all");
965
966         dbus_message_iter_init(message, &args);
967
968         if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
969                 return g_dbus_create_error(message,
970                                 ERROR_INTERFACE ".InvalidArguments", NULL);
971
972         dbus_message_iter_get_basic(&args, &field);
973         dbus_message_iter_next(&args);
974
975         apparam = parse_attribute(NULL, field);
976         if (apparam == NULL)
977                 return g_dbus_create_error(message,
978                                 ERROR_INTERFACE ".InvalidArguments", NULL);
979
980         if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
981                 return g_dbus_create_error(message,
982                                 ERROR_INTERFACE ".InvalidArguments", NULL);
983
984         dbus_message_iter_get_basic(&args, &value);
985         dbus_message_iter_next(&args);
986
987         apparam = g_obex_apparam_set_uint16(apparam, MAXLISTCOUNT_TAG,
988                                                         DEFAULT_COUNT);
989         apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
990                                                         DEFAULT_OFFSET);
991         apparam = g_obex_apparam_set_string(apparam, SEARCHVALUE_TAG, value);
992
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);
997         }
998
999         return pull_vcard_listing(pbap, message, "", apparam);
1000 }
1001
1002 static DBusMessage *pbap_get_size(DBusConnection *connection,
1003                                         DBusMessage *message, void *user_data)
1004 {
1005         struct pbap_data *pbap = user_data;
1006         GObexApparam *apparam;
1007         DBusMessageIter args;
1008
1009         if (!pbap->path)
1010                 return g_dbus_create_error(message,
1011                                         ERROR_INTERFACE ".Forbidden",
1012                                         "Call Select first of all");
1013
1014         dbus_message_iter_init(message, &args);
1015
1016         apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, 0);
1017         apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
1018                                                         DEFAULT_OFFSET);
1019
1020         return pull_phonebook(pbap, message, GETPHONEBOOKSIZE, NULL, apparam);
1021 }
1022
1023 static char **get_filter_strs(uint64_t filter, int *size)
1024 {
1025         char **list, **item;
1026         int i;
1027
1028         list = g_malloc0(sizeof(char **) * (FILTER_BIT_MAX + 2));
1029
1030         item = list;
1031
1032         for (i = 0; filter_list[i] != NULL; i++)
1033                 if (filter & (1ULL << i))
1034                         *(item++) = g_strdup(filter_list[i]);
1035
1036         for (; i <= FILTER_BIT_MAX; i++)
1037                 if (filter & (1ULL << i))
1038                         *(item++) = g_strdup_printf("%s%d", "BIT", i);
1039
1040         *item = NULL;
1041         *size = item - list;
1042         return list;
1043 }
1044
1045 static DBusMessage *pbap_list_filter_fields(DBusConnection *connection,
1046                                         DBusMessage *message, void *user_data)
1047 {
1048         char **filters = NULL;
1049         int size;
1050         DBusMessage *reply;
1051
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,
1056                                 DBUS_TYPE_INVALID);
1057
1058         g_strfreev(filters);
1059         return reply;
1060 }
1061
1062 static DBusMessage *pbap_update_version(DBusConnection *connection,
1063                                         DBusMessage *message, void *user_data)
1064 {
1065         struct pbap_data *pbap = user_data;
1066
1067         if (!(pbap->supported_features & FOLDER_VERSION_FEATURE))
1068                 return g_dbus_create_error(message,
1069                                         ERROR_INTERFACE ".NotSupported",
1070                                         "Operation is not supported");
1071
1072         return pbap_get_size(connection, message, user_data);
1073 }
1074
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}" }),
1084                         pbap_pull_all) },
1085         { GDBUS_METHOD("Pull",
1086                         GDBUS_ARGS({ "vcard", "s" }, { "targetfile", "s" },
1087                                         { "filters", "a{sv}" }),
1088                         GDBUS_ARGS({ "transfer", "o" },
1089                                         { "properties", "a{sv}" }),
1090                         pbap_pull_vcard) },
1091         { GDBUS_ASYNC_METHOD("List",
1092 #ifdef __TIZEN_PATCH__
1093                         GDBUS_ARGS({ "folder", "s" }, {"filters", "a{sv}" }),
1094 #else
1095                         GDBUS_ARGS({"filters", "a{sv}" }),
1096 #endif
1097                         GDBUS_ARGS({ "vcard_listing", "a(ss)" }),
1098                         pbap_list) },
1099         { GDBUS_ASYNC_METHOD("Search",
1100                         GDBUS_ARGS({ "field", "s" }, { "value", "s" },
1101                                         { "filters", "a{sv}" }),
1102                         GDBUS_ARGS({ "vcard_listing", "a(ss)" }),
1103                         pbap_search) },
1104         { GDBUS_ASYNC_METHOD("GetSize",
1105                                 NULL, GDBUS_ARGS({ "size", "q" }),
1106                                 pbap_get_size) },
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) },
1112         { }
1113 };
1114
1115 static gboolean folder_exists(const GDBusPropertyTable *property, void *data)
1116 {
1117         struct pbap_data *pbap = data;
1118
1119         return pbap->path != NULL;
1120 }
1121
1122 static gboolean get_folder(const GDBusPropertyTable *property,
1123                                         DBusMessageIter *iter, void *data)
1124 {
1125         struct pbap_data *pbap = data;
1126
1127         if (!pbap->path)
1128                 return FALSE;
1129
1130         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pbap->path);
1131
1132         return TRUE;
1133 }
1134
1135 static gboolean databaseid_exists(const GDBusPropertyTable *property,
1136                                                                 void *data)
1137 {
1138         struct pbap_data *pbap = data;
1139
1140         return pbap->supported_features & DATABASEID_FEATURE;
1141 }
1142
1143 static int u128_to_string(uint8_t *data, char *str, size_t len)
1144 {
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]);
1151 }
1152
1153 static gboolean get_databaseid(const GDBusPropertyTable *property,
1154                                         DBusMessageIter *iter, void *data)
1155 {
1156         struct pbap_data *pbap = data;
1157         char value[33];
1158         const char *pvalue = value;
1159
1160         if (u128_to_string(pbap->databaseid, value, sizeof(value)) < 0)
1161                 return FALSE;
1162
1163         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue);
1164
1165         return TRUE;
1166 }
1167
1168 static gboolean version_exists(const GDBusPropertyTable *property,
1169                                                                 void *data)
1170 {
1171         struct pbap_data *pbap = data;
1172
1173         return pbap->supported_features & FOLDER_VERSION_FEATURE;
1174 }
1175
1176 static gboolean get_primary(const GDBusPropertyTable *property,
1177                                         DBusMessageIter *iter, void *data)
1178 {
1179         struct pbap_data *pbap = data;
1180         char value[33];
1181         const char *pvalue = value;
1182
1183         if (u128_to_string(pbap->primary, value, sizeof(value)) < 0)
1184                 return FALSE;
1185
1186         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue);
1187
1188         return TRUE;
1189 }
1190
1191 static gboolean get_secondary(const GDBusPropertyTable *property,
1192                                         DBusMessageIter *iter, void *data)
1193 {
1194         struct pbap_data *pbap = data;
1195         char value[33];
1196         const char *pvalue = value;
1197
1198         if (u128_to_string(pbap->secondary, value, sizeof(value)) < 0)
1199                 return FALSE;
1200
1201         dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pvalue);
1202
1203         return TRUE;
1204 }
1205
1206 static gboolean image_size_exists(const GDBusPropertyTable *property,
1207                                                                 void *data)
1208 {
1209         struct pbap_data *pbap = data;
1210
1211         return pbap->supported_features & DEFAULT_IMAGE_FEATURE;
1212 }
1213
1214 static gboolean get_image_size(const GDBusPropertyTable *property,
1215                                         DBusMessageIter *iter, void *data)
1216 {
1217         dbus_bool_t value = TRUE;
1218
1219         dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1220
1221         return TRUE;
1222 }
1223
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 },
1230         { }
1231 };
1232
1233 static void pbap_free(void *data)
1234 {
1235         struct pbap_data *pbap = data;
1236
1237         obc_session_unref(pbap->session);
1238         g_free(pbap->path);
1239         g_free(pbap);
1240 }
1241
1242 static void parse_service_record(struct pbap_data *pbap)
1243 {
1244         const void *data;
1245
1246         /* Version */
1247         data = obc_session_get_attribute(pbap->session,
1248                                                 SDP_ATTR_PFILE_DESC_LIST);
1249         if (!data)
1250                 return;
1251
1252         pbap->version = GPOINTER_TO_UINT(data);
1253
1254         /*
1255          * If the PbapSupportedFeatures attribute is not present
1256          * 0x00000003 shall be assumed for a remote PSE.
1257          */
1258         pbap->supported_features = 0x00000003;
1259
1260         if (pbap->version < 0x0102)
1261                 return;
1262
1263         /* Supported Feature Bits */
1264         data = obc_session_get_attribute(pbap->session,
1265                                         SDP_ATTR_PBAP_SUPPORTED_FEATURES);
1266         if (data)
1267                 pbap->supported_features = *(uint32_t *) data;
1268
1269 }
1270
1271 static void *pbap_supported_features(struct obc_session *session)
1272 {
1273         const void *data;
1274         uint16_t version;
1275
1276         /* Version */
1277         data = obc_session_get_attribute(session, SDP_ATTR_PFILE_DESC_LIST);
1278         if (!data)
1279                 return NULL;
1280
1281         version = GPOINTER_TO_UINT(data);
1282
1283         if (version < 0x0102)
1284                 return NULL;
1285
1286         /* Supported Feature Bits */
1287         data = obc_session_get_attribute(session,
1288                                         SDP_ATTR_PBAP_SUPPORTED_FEATURES);
1289         if (!data)
1290                 return NULL;
1291
1292         return g_obex_apparam_set_uint32(NULL, SUPPORTED_FEATURES_TAG,
1293                                                 DOWNLOAD_FEATURE |
1294                                                 BROWSE_FEATURE |
1295                                                 DATABASEID_FEATURE |
1296                                                 FOLDER_VERSION_FEATURE |
1297                                                 VCARD_SELECTING_FEATURE |
1298                                                 ENHANCED_CALLS_FEATURE |
1299                                                 UCI_FEATURE |
1300                                                 UID_FEATURE |
1301                                                 REFERENCING_FEATURE |
1302                                                 DEFAULT_IMAGE_FEATURE);
1303 }
1304
1305 static int pbap_probe(struct obc_session *session)
1306 {
1307         struct pbap_data *pbap;
1308         const char *path;
1309
1310         path = obc_session_get_path(session);
1311
1312         DBG("%s", path);
1313
1314         pbap = g_try_new0(struct pbap_data, 1);
1315         if (!pbap)
1316                 return -ENOMEM;
1317
1318         pbap->session = obc_session_ref(session);
1319
1320         parse_service_record(pbap);
1321
1322         DBG("%s, version 0x%04x supported features 0x%08x", path, pbap->version,
1323                                                 pbap->supported_features);
1324
1325         if (!g_dbus_register_interface(conn, path, PBAP_INTERFACE, pbap_methods,
1326                                                 NULL, pbap_properties, pbap,
1327                                                 pbap_free)) {
1328                 pbap_free(pbap);
1329                 return -ENOMEM;
1330         }
1331
1332         return 0;
1333 }
1334
1335 static void pbap_remove(struct obc_session *session)
1336 {
1337         const char *path = obc_session_get_path(session);
1338
1339         DBG("%s", path);
1340
1341         g_dbus_unregister_interface(conn, path, PBAP_INTERFACE);
1342 }
1343
1344 static struct obc_driver pbap = {
1345         .service = "PBAP",
1346         .uuid = PBAP_UUID,
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
1352 };
1353
1354 int pbap_init(void)
1355 {
1356         int err;
1357
1358         DBG("");
1359
1360         conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
1361         if (!conn)
1362                 return -EIO;
1363
1364         err = obc_driver_register(&pbap);
1365         if (err < 0) {
1366                 dbus_connection_unref(conn);
1367                 conn = NULL;
1368                 return err;
1369         }
1370
1371         return 0;
1372 }
1373
1374 void pbap_exit(void)
1375 {
1376         DBG("");
1377
1378         dbus_connection_unref(conn);
1379         conn = NULL;
1380
1381         obc_driver_unregister(&pbap);
1382 }