obexd-test package is needed for testing
[profile/ivi/obexd.git] / 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 #include <glib.h>
33 #include <gdbus.h>
34
35 #include <bluetooth/bluetooth.h>
36
37 #include "log.h"
38
39 #include "transfer.h"
40 #include "session.h"
41 #include "driver.h"
42 #include "pbap.h"
43
44 #define OBEX_PBAP_UUID \
45         "\x79\x61\x35\xF0\xF0\xC5\x11\xD8\x09\x66\x08\x00\x20\x0C\x9A\x66"
46 #define OBEX_PBAP_UUID_LEN 16
47
48 #define ERROR_INF PBAP_INTERFACE ".Error"
49
50 #define FORMAT_VCARD21  0x0
51 #define FORMAT_VCARD30  0x1
52
53 #define ORDER_INDEXED           0x0
54 #define ORDER_ALPHANUMERIC      0x1
55 #define ORDER_PHONETIC          0x2
56
57 #define ATTRIB_NAME             0x0
58 #define ATTRIB_NUMBER           0x1
59 #define ATTRIB_SOUND            0x2
60
61 #define DEFAULT_COUNT   65535
62 #define DEFAULT_OFFSET  0
63
64 #define PULLPHONEBOOK           0x1
65 #define GETPHONEBOOKSIZE        0x2
66
67 #define ORDER_TAG               0x01
68 #define SEARCHVALUE_TAG         0x02
69 #define SEARCHATTRIB_TAG        0x03
70 #define MAXLISTCOUNT_TAG        0x04
71 #define LISTSTARTOFFSET_TAG     0x05
72 #define FILTER_TAG              0x06
73 #define FORMAT_TAG              0X07
74 #define PHONEBOOKSIZE_TAG       0X08
75 #define NEWMISSEDCALLS_TAG      0X09
76
77 /* The following length is in the unit of byte */
78 #define ORDER_LEN               1
79 #define SEARCHATTRIB_LEN        1
80 #define MAXLISTCOUNT_LEN        2
81 #define LISTSTARTOFFSET_LEN     2
82 #define FILTER_LEN              8
83 #define FORMAT_LEN              1
84 #define PHONEBOOKSIZE_LEN       2
85 #define NEWMISSEDCALLS_LEN      1
86
87 #define get_be16(val)   GUINT16_FROM_BE(bt_get_unaligned((guint16 *) val))
88
89 static const char *filter_list[] = {
90         "VERSION",
91         "FN",
92         "N",
93         "PHOTO",
94         "BDAY",
95         "ADR",
96         "LABEL",
97         "TEL",
98         "EMAIL",
99         "MAILER",
100         "TZ",
101         "GEO",
102         "TITLE",
103         "ROLE",
104         "LOGO",
105         "AGENT",
106         "ORG",
107         "NOTE",
108         "REV",
109         "SOUND",
110         "URL",
111         "UID",
112         "KEY",
113         "NICKNAME",
114         "CATEGORIES",
115         "PROID",
116         "CLASS",
117         "SORT-STRING",
118         "X-IRMC-CALL-DATETIME",
119         NULL
120 };
121
122 #define FILTER_BIT_MAX  63
123 #define FILTER_ALL      0xFFFFFFFFFFFFFFFFULL
124
125 #define PBAP_INTERFACE  "org.openobex.PhonebookAccess"
126 #define PBAP_UUID "0000112f-0000-1000-8000-00805f9b34fb"
127
128 struct pbap_data {
129         struct obc_session *session;
130         char *path;
131         guint8 format;
132         guint8 order;
133         uint64_t filter;
134 };
135
136 struct pending_request {
137         struct pbap_data *pbap;
138         DBusMessage *msg;
139 };
140
141 struct pullphonebook_apparam {
142         uint8_t     filter_tag;
143         uint8_t     filter_len;
144         uint64_t    filter;
145         uint8_t     format_tag;
146         uint8_t     format_len;
147         uint8_t     format;
148         uint8_t     maxlistcount_tag;
149         uint8_t     maxlistcount_len;
150         uint16_t    maxlistcount;
151         uint8_t     liststartoffset_tag;
152         uint8_t     liststartoffset_len;
153         uint16_t    liststartoffset;
154 } __attribute__ ((packed));
155
156 struct pullvcardentry_apparam {
157         uint8_t     filter_tag;
158         uint8_t     filter_len;
159         uint64_t    filter;
160         uint8_t     format_tag;
161         uint8_t     format_len;
162         uint8_t     format;
163 } __attribute__ ((packed));
164
165 struct apparam_hdr {
166         uint8_t         tag;
167         uint8_t         len;
168         uint8_t         val[0];
169 } __attribute__ ((packed));
170
171 #define APPARAM_HDR_SIZE 2
172
173 static DBusConnection *conn = NULL;
174
175 static struct pending_request *pending_request_new(struct pbap_data *pbap,
176                                                         DBusMessage *message)
177 {
178         struct pending_request *p;
179
180         p = g_new0(struct pending_request, 1);
181         p->pbap = pbap;
182         p->msg = dbus_message_ref(message);
183
184         return p;
185 }
186
187 static void pending_request_free(struct pending_request *p)
188 {
189         dbus_message_unref(p->msg);
190         g_free(p);
191 }
192
193 static void listing_element(GMarkupParseContext *ctxt,
194                                 const gchar *element,
195                                 const gchar **names,
196                                 const gchar **values,
197                                 gpointer user_data,
198                                 GError **gerr)
199 {
200         DBusMessageIter *item = user_data, entry;
201         gchar **key;
202         const gchar *handle = NULL, *vcardname = NULL;
203
204         if (g_str_equal(element, "card") != TRUE)
205                 return;
206
207         for (key = (gchar **) names; *key; key++, values++) {
208                 if (g_str_equal(*key, "handle") == TRUE)
209                         handle = *values;
210                 else if (g_str_equal(*key, "name") == TRUE)
211                         vcardname = *values;
212         }
213
214         if (!handle || !vcardname)
215                 return;
216
217         dbus_message_iter_open_container(item, DBUS_TYPE_STRUCT, NULL, &entry);
218         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &handle);
219         dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &vcardname);
220         dbus_message_iter_close_container(item, &entry);
221 }
222
223 static const GMarkupParser listing_parser = {
224         listing_element,
225         NULL,
226         NULL,
227         NULL,
228         NULL
229 };
230 static gchar *build_phonebook_path(const char *location, const char *item)
231 {
232         gchar *path = NULL, *tmp, *tmp1;
233
234         if (!g_ascii_strcasecmp(location, "INT") ||
235                         !g_ascii_strcasecmp(location, "INTERNAL"))
236                 path = g_strdup("telecom");
237         else if (!g_ascii_strncasecmp(location, "SIM", 3)) {
238                 if (strlen(location) == 3)
239                         tmp = g_strdup("SIM1");
240                 else
241                         tmp = g_ascii_strup(location, 4);
242
243                 path = g_build_filename(tmp, "telecom", NULL);
244                 g_free(tmp);
245         } else
246                 return NULL;
247
248         if (!g_ascii_strcasecmp(item, "PB") ||
249                 !g_ascii_strcasecmp(item, "ICH") ||
250                 !g_ascii_strcasecmp(item, "OCH") ||
251                 !g_ascii_strcasecmp(item, "MCH") ||
252                 !g_ascii_strcasecmp(item, "CCH")) {
253                 tmp = path;
254                 tmp1 = g_ascii_strdown(item, -1);
255                 path = g_build_filename(tmp, tmp1, NULL);
256                 g_free(tmp);
257                 g_free(tmp1);
258         } else {
259                 g_free(path);
260                 return NULL;
261         }
262
263         return path;
264 }
265
266 /* should only be called inside pbap_set_path */
267 static void pbap_reset_path(struct pbap_data *pbap)
268 {
269         if (!pbap->path)
270                 return;
271
272         obc_session_setpath(pbap->session, pbap->path, NULL, NULL, NULL);
273 }
274
275 static void pbap_setpath_cb(struct obc_session *session,
276                                                 struct obc_transfer *transfer,
277                                                 GError *err, void *user_data)
278 {
279         struct pending_request *request = user_data;
280         struct pbap_data *pbap = request->pbap;
281
282         if (err != NULL)
283                 pbap_reset_path(pbap);
284
285         if (err) {
286                 DBusMessage *reply = g_dbus_create_error(request->msg,
287                                                         ERROR_INF ".Failed",
288                                                         "%s", err->message);
289                 g_dbus_send_message(conn, reply);
290         } else
291                 g_dbus_send_reply(conn, request->msg, DBUS_TYPE_INVALID);
292
293         pending_request_free(request);
294 }
295
296 static void read_return_apparam(struct obc_transfer *transfer,
297                                 guint16 *phone_book_size, guint8 *new_missed_calls)
298 {
299         const struct apparam_hdr *hdr;
300         size_t size;
301
302         *phone_book_size = 0;
303         *new_missed_calls = 0;
304
305         hdr = obc_transfer_get_params(transfer, &size);
306         if (hdr == NULL)
307                 return;
308
309         if (size < APPARAM_HDR_SIZE)
310                 return;
311
312         while (size > APPARAM_HDR_SIZE) {
313                 if (hdr->len > size - APPARAM_HDR_SIZE) {
314                         error("Unexpected PBAP pullphonebook app"
315                                         " length, tag %d, len %d",
316                                         hdr->tag, hdr->len);
317                         return;
318                 }
319
320                 switch (hdr->tag) {
321                 case PHONEBOOKSIZE_TAG:
322                         if (hdr->len == PHONEBOOKSIZE_LEN) {
323                                 guint16 val;
324                                 memcpy(&val, hdr->val, sizeof(val));
325                                 *phone_book_size = get_be16(&val);
326                         }
327                         break;
328                 case NEWMISSEDCALLS_TAG:
329                         if (hdr->len == NEWMISSEDCALLS_LEN)
330                                 *new_missed_calls = hdr->val[0];
331                         break;
332                 default:
333                         error("Unexpected PBAP pullphonebook app"
334                                         " parameter, tag %d, len %d",
335                                         hdr->tag, hdr->len);
336                 }
337
338                 size -= APPARAM_HDR_SIZE + hdr->len;
339                 hdr += APPARAM_HDR_SIZE + hdr->len;
340         }
341 }
342
343 static void pull_phonebook_callback(struct obc_session *session,
344                                                 struct obc_transfer *transfer,
345                                                 GError *err, void *user_data)
346 {
347         struct pending_request *request = user_data;
348         DBusMessage *reply;
349         char *contents;
350         size_t size;
351         int perr;
352
353         if (err) {
354                 reply = g_dbus_create_error(request->msg,
355                                                 "org.openobex.Error.Failed",
356                                                 "%s", err->message);
357                 goto send;
358         }
359
360         perr = obc_transfer_get_contents(transfer, &contents, &size);
361         if (perr < 0) {
362                 reply = g_dbus_create_error(request->msg,
363                                                 "org.openobex.Error.Failed",
364                                                 "Error reading contents: %s",
365                                                 strerror(-perr));
366                 goto send;
367         }
368
369         reply = dbus_message_new_method_return(request->msg);
370
371         dbus_message_append_args(reply,
372                         DBUS_TYPE_STRING, &contents,
373                         DBUS_TYPE_INVALID);
374
375         g_free(contents);
376
377 send:
378         g_dbus_send_message(conn, reply);
379         pending_request_free(request);
380 }
381
382 static void phonebook_size_callback(struct obc_session *session,
383                                                 struct obc_transfer *transfer,
384                                                 GError *err, void *user_data)
385 {
386         struct pending_request *request = user_data;
387         DBusMessage *reply;
388         guint16 phone_book_size;
389         guint8 new_missed_calls;
390
391         if (err) {
392                 reply = g_dbus_create_error(request->msg,
393                                                 "org.openobex.Error.Failed",
394                                                 "%s", err->message);
395                 goto send;
396         }
397
398         reply = dbus_message_new_method_return(request->msg);
399
400         read_return_apparam(transfer, &phone_book_size, &new_missed_calls);
401
402         dbus_message_append_args(reply,
403                         DBUS_TYPE_UINT16, &phone_book_size,
404                         DBUS_TYPE_INVALID);
405
406 send:
407         g_dbus_send_message(conn, reply);
408         pending_request_free(request);
409 }
410
411 static void pull_vcard_listing_callback(struct obc_session *session,
412                                                 struct obc_transfer *transfer,
413                                                 GError *err, void *user_data)
414 {
415         struct pending_request *request = user_data;
416         GMarkupParseContext *ctxt;
417         DBusMessage *reply;
418         DBusMessageIter iter, array;
419         char *contents;
420         size_t size;
421         int perr;
422
423         if (err) {
424                 reply = g_dbus_create_error(request->msg,
425                                                 "org.openobex.Error.Failed",
426                                                 "%s", err->message);
427                 goto send;
428         }
429
430         perr = obc_transfer_get_contents(transfer, &contents, &size);
431         if (perr < 0) {
432                 reply = g_dbus_create_error(request->msg,
433                                                 "org.openobex.Error.Failed",
434                                                 "Error reading contents: %s",
435                                                 strerror(-perr));
436                 goto send;
437         }
438
439         reply = dbus_message_new_method_return(request->msg);
440
441         dbus_message_iter_init_append(reply, &iter);
442         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
443                         DBUS_STRUCT_BEGIN_CHAR_AS_STRING
444                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
445                         DBUS_STRUCT_END_CHAR_AS_STRING, &array);
446         ctxt = g_markup_parse_context_new(&listing_parser, 0, &array, NULL);
447         g_markup_parse_context_parse(ctxt, contents, size, NULL);
448         g_markup_parse_context_free(ctxt);
449         dbus_message_iter_close_container(&iter, &array);
450         g_free(contents);
451
452 send:
453         g_dbus_send_message(conn, reply);
454         pending_request_free(request);
455 }
456
457 static DBusMessage *pull_phonebook(struct pbap_data *pbap,
458                                         DBusMessage *message, guint8 type,
459                                         const char *name, uint64_t filter,
460                                         guint8 format, guint16 maxlistcount,
461                                         guint16 liststartoffset)
462 {
463         struct pending_request *request;
464         struct pullphonebook_apparam apparam;
465         struct obc_transfer *transfer;
466         session_callback_t func;
467         GError *err = NULL;
468         DBusMessage *reply;
469
470         transfer = obc_transfer_get("x-bt/phonebook", name, NULL, &err);
471         if (transfer == NULL)
472                 goto fail;
473
474         apparam.filter_tag = FILTER_TAG;
475         apparam.filter_len = FILTER_LEN;
476         apparam.filter = GUINT64_TO_BE(filter);
477         apparam.format_tag = FORMAT_TAG;
478         apparam.format_len = FORMAT_LEN;
479         apparam.format = format;
480         apparam.maxlistcount_tag = MAXLISTCOUNT_TAG;
481         apparam.maxlistcount_len = MAXLISTCOUNT_LEN;
482         apparam.maxlistcount = GUINT16_TO_BE(maxlistcount);
483         apparam.liststartoffset_tag = LISTSTARTOFFSET_TAG;
484         apparam.liststartoffset_len = LISTSTARTOFFSET_LEN;
485         apparam.liststartoffset = GUINT16_TO_BE(liststartoffset);
486
487         switch (type) {
488         case PULLPHONEBOOK:
489                 func = pull_phonebook_callback;
490                 break;
491         case GETPHONEBOOKSIZE:
492                 func = phonebook_size_callback;
493                 break;
494         default:
495                 error("Unexpected type : 0x%2x", type);
496                 return NULL;
497         }
498
499         request = pending_request_new(pbap, message);
500
501         obc_transfer_set_params(transfer, &apparam, sizeof(apparam));
502
503         if (obc_session_queue(pbap->session, transfer, func, request, &err))
504                 return NULL;
505
506         pending_request_free(request);
507
508 fail:
509         reply = g_dbus_create_error(message, "org.openobex.Error.Failed", "%s",
510                                                                 err->message);
511         g_error_free(err);
512         return reply;
513 }
514
515 static guint8 *fill_apparam(guint8 *dest, void *buf, guint8 tag, guint8 len)
516 {
517         if (dest && buf) {
518                 *dest++ = tag;
519                 *dest++ = len;
520                 memcpy(dest, buf, len);
521                 dest += len;
522         }
523
524         return dest;
525 }
526
527 static DBusMessage *pull_vcard_listing(struct pbap_data *pbap,
528                                         DBusMessage *message, const char *name,
529                                         guint8 order, char *searchval, guint8 attrib,
530                                         guint16 count, guint16 offset)
531 {
532         struct pending_request *request;
533         struct obc_transfer *transfer;
534         guint8 *p, apparam[272];
535         gint apparam_size;
536         GError *err = NULL;
537         DBusMessage *reply;
538
539         transfer = obc_transfer_get("x-bt/vcard-listing", name, NULL, &err);
540         if (transfer == NULL)
541                 goto fail;
542
543         /* trunc the searchval string if it's length exceed the max value of guint8 */
544         if (strlen(searchval) > 254)
545                 searchval[255] = '\0';
546
547         apparam_size = APPARAM_HDR_SIZE + ORDER_LEN +
548                         (APPARAM_HDR_SIZE + strlen(searchval) + 1) +
549                         (APPARAM_HDR_SIZE + SEARCHATTRIB_LEN) +
550                         (APPARAM_HDR_SIZE + MAXLISTCOUNT_LEN) +
551                         (APPARAM_HDR_SIZE + LISTSTARTOFFSET_LEN);
552
553         p = apparam;
554
555         p = fill_apparam(p, &order, ORDER_TAG, ORDER_LEN);
556         p = fill_apparam(p, searchval, SEARCHVALUE_TAG, strlen(searchval) + 1);
557         p = fill_apparam(p, &attrib, SEARCHATTRIB_TAG, SEARCHATTRIB_LEN);
558
559         count = GUINT16_TO_BE(count);
560         p = fill_apparam(p, &count, MAXLISTCOUNT_TAG, MAXLISTCOUNT_LEN);
561
562         offset = GUINT16_TO_BE(offset);
563         p = fill_apparam(p, &offset, LISTSTARTOFFSET_TAG, LISTSTARTOFFSET_LEN);
564
565         request = pending_request_new(pbap, message);
566
567         obc_transfer_set_params(transfer, apparam, apparam_size);
568
569         if (obc_session_queue(pbap->session, transfer,
570                                 pull_vcard_listing_callback, request, &err))
571                 return NULL;
572
573         pending_request_free(request);
574
575 fail:
576         reply = g_dbus_create_error(message, "org.openobex.Error.Failed", "%s",
577                                                                 err->message);
578         g_error_free(err);
579         return reply;
580 }
581
582 static int set_format(struct pbap_data *pbap, const char *formatstr)
583 {
584         if (!formatstr || g_str_equal(formatstr, "")) {
585                 pbap->format = FORMAT_VCARD21;
586                 return 0;
587         }
588
589         if (!g_ascii_strcasecmp(formatstr, "vcard21"))
590                 pbap->format = FORMAT_VCARD21;
591         else if (!g_ascii_strcasecmp(formatstr, "vcard30"))
592                 pbap->format = FORMAT_VCARD30;
593         else
594                 return -EINVAL;
595
596         return 0;
597 }
598
599 static int set_order(struct pbap_data *pbap, const char *orderstr)
600 {
601         if (!orderstr || g_str_equal(orderstr, "")) {
602                 pbap->order = ORDER_INDEXED;
603                 return 0;
604         }
605
606         if (!g_ascii_strcasecmp(orderstr, "indexed"))
607                 pbap->order = ORDER_INDEXED;
608         else if (!g_ascii_strcasecmp(orderstr, "alphanumeric"))
609                 pbap->order = ORDER_ALPHANUMERIC;
610         else if (!g_ascii_strcasecmp(orderstr, "phonetic"))
611                 pbap->order = ORDER_PHONETIC;
612         else
613                 return -EINVAL;
614
615         return 0;
616 }
617
618 static uint64_t get_filter_mask(const char *filterstr)
619 {
620         int i, bit = -1;
621
622         if (!filterstr)
623                 return 0;
624
625         if (!g_ascii_strcasecmp(filterstr, "ALL"))
626                 return FILTER_ALL;
627
628         for (i = 0; filter_list[i] != NULL; i++)
629                 if (!g_ascii_strcasecmp(filterstr, filter_list[i]))
630                         return 1ULL << i;
631
632         if (strlen(filterstr) < 4 || strlen(filterstr) > 5
633                         || g_ascii_strncasecmp(filterstr, "bit", 3) != 0)
634                 return 0;
635
636         sscanf(&filterstr[3], "%d", &bit);
637         if (bit >= 0 && bit <= FILTER_BIT_MAX)
638                 return 1ULL << bit;
639         else
640                 return 0;
641 }
642
643 static int add_filter(struct pbap_data *pbap, const char *filterstr)
644 {
645         uint64_t mask;
646
647         mask = get_filter_mask(filterstr);
648
649         if (mask == 0)
650                 return -EINVAL;
651
652         pbap->filter |= mask;
653         return 0;
654 }
655
656 static int remove_filter(struct pbap_data *pbap, const char *filterstr)
657 {
658         uint64_t mask;
659
660         mask = get_filter_mask(filterstr);
661
662         if (mask == 0)
663                 return -EINVAL;
664
665         pbap->filter &= ~mask;
666         return 0;
667 }
668
669 static gchar **get_filter_strs(uint64_t filter, gint *size)
670 {
671         gchar **list, **item;
672         gint i;
673         gint filter_list_size = sizeof(filter_list) / sizeof(filter_list[0]) - 1;
674
675         list = g_try_malloc0(sizeof(gchar **) * (FILTER_BIT_MAX + 2));
676
677         if (!list)
678                 return NULL;
679
680         item = list;
681
682         for (i = 0; i < filter_list_size; i++)
683                 if (filter & (1ULL << i))
684                         *(item++) = g_strdup(filter_list[i]);
685
686         for (i = filter_list_size; i <= FILTER_BIT_MAX; i++)
687                 if (filter & (1ULL << i))
688                         *(item++) = g_strdup_printf("%s%d", "BIT", i);
689
690         *item = NULL;
691         *size = item - list;
692         return list;
693 }
694
695 static DBusMessage *pbap_select(DBusConnection *connection,
696                                         DBusMessage *message, void *user_data)
697 {
698         struct pbap_data *pbap = user_data;
699         const char *item, *location;
700         char *path;
701         struct pending_request *request;
702         GError *err = NULL;
703
704         if (dbus_message_get_args(message, NULL,
705                         DBUS_TYPE_STRING, &location,
706                         DBUS_TYPE_STRING, &item,
707                         DBUS_TYPE_INVALID) == FALSE)
708                 return g_dbus_create_error(message,
709                                 ERROR_INF ".InvalidArguments", NULL);
710
711         path = build_phonebook_path(location, item);
712         if (path == NULL)
713                 return g_dbus_create_error(message,
714                                 ERROR_INF ".InvalidArguments", "Invalid path");
715
716         if (pbap->path != NULL && g_str_equal(pbap->path, path)) {
717                 g_free(path);
718                 return dbus_message_new_method_return(message);
719         }
720
721         request = pending_request_new(pbap, message);
722
723         obc_session_setpath(pbap->session, path, pbap_setpath_cb, request,
724                                                                         &err);
725         if (err != NULL) {
726                 DBusMessage *reply;
727                 reply =  g_dbus_create_error(message, ERROR_INF ".Failed",
728                                                         "%s", err->message);
729                 g_error_free(err);
730                 g_free(path);
731                 pending_request_free(request);
732                 return reply;
733         }
734
735         g_free(pbap->path);
736         pbap->path = path;
737
738         return NULL;
739 }
740
741 static DBusMessage *pbap_pull_all(DBusConnection *connection,
742                                         DBusMessage *message, void *user_data)
743 {
744         struct pbap_data *pbap = user_data;
745         DBusMessage * err;
746         char *name;
747
748         if (!pbap->path)
749                 return g_dbus_create_error(message,
750                                 ERROR_INF ".Forbidden", "Call Select first of all");
751
752         name = g_strconcat(pbap->path, ".vcf", NULL);
753
754         err = pull_phonebook(pbap, message, PULLPHONEBOOK, name,
755                                 pbap->filter, pbap->format,
756                                 DEFAULT_COUNT, DEFAULT_OFFSET);
757         g_free(name);
758         return err;
759 }
760
761 static DBusMessage *pbap_pull_vcard(DBusConnection *connection,
762                                         DBusMessage *message, void *user_data)
763 {
764         struct pbap_data *pbap = user_data;
765         struct pullvcardentry_apparam apparam;
766         const char *name;
767         struct pending_request *request;
768         struct obc_transfer *transfer;
769         GError *err = NULL;
770         DBusMessage *reply;
771
772         if (!pbap->path)
773                 return g_dbus_create_error(message,
774                                 ERROR_INF ".Forbidden",
775                                 "Call Select first of all");
776
777         if (dbus_message_get_args(message, NULL,
778                         DBUS_TYPE_STRING, &name,
779                         DBUS_TYPE_INVALID) == FALSE)
780                 return g_dbus_create_error(message,
781                                 ERROR_INF ".InvalidArguments", NULL);
782
783         transfer = obc_transfer_get("x-bt/vcard", name, NULL, &err);
784         if (transfer == NULL)
785                 goto fail;
786
787         apparam.filter_tag = FILTER_TAG;
788         apparam.filter_len = FILTER_LEN;
789         apparam.filter = GUINT64_TO_BE(pbap->filter);
790         apparam.format_tag = FORMAT_TAG;
791         apparam.format_len = FORMAT_LEN;
792         apparam.format = pbap->format;
793
794         request = pending_request_new(pbap, message);
795
796         obc_transfer_set_params(transfer, &apparam, sizeof(apparam));
797
798         if (obc_session_queue(pbap->session, transfer, pull_phonebook_callback,
799                                                                 request, &err))
800                 return NULL;
801
802         pending_request_free(request);
803
804 fail:
805         reply = g_dbus_create_error(message, "org.openobex.Error.Failed", "%s",
806                                                                 err->message);
807         g_error_free(err);
808         return reply;
809 }
810
811 static DBusMessage *pbap_list(DBusConnection *connection,
812                                         DBusMessage *message, void *user_data)
813 {
814         struct pbap_data *pbap = user_data;
815
816         if (!pbap->path)
817                 return g_dbus_create_error(message,
818                                 ERROR_INF ".Forbidden", "Call Select first of all");
819
820         return pull_vcard_listing(pbap, message, "", pbap->order, "",
821                                 ATTRIB_NAME, DEFAULT_COUNT, DEFAULT_OFFSET);
822 }
823
824 static DBusMessage *pbap_search(DBusConnection *connection,
825                                         DBusMessage *message, void *user_data)
826 {
827         struct pbap_data *pbap = user_data;
828         char *field, *value;
829         guint8 attrib;
830
831         if (dbus_message_get_args(message, NULL,
832                         DBUS_TYPE_STRING, &field,
833                         DBUS_TYPE_STRING, &value,
834                         DBUS_TYPE_INVALID) == FALSE)
835                 return g_dbus_create_error(message,
836                                 ERROR_INF ".InvalidArguments", NULL);
837
838         if (!pbap->path)
839                 return g_dbus_create_error(message,
840                                 ERROR_INF ".Forbidden", "Call Select first of all");
841
842         if (!field || g_str_equal(field, ""))
843                 attrib = ATTRIB_NAME;
844         else if (!g_ascii_strcasecmp(field, "name"))
845                 attrib = ATTRIB_NAME;
846         else if (!g_ascii_strcasecmp(field, "number"))
847                 attrib = ATTRIB_NUMBER;
848         else if (!g_ascii_strcasecmp(field, "sound"))
849                 attrib = ATTRIB_SOUND;
850         else
851                 return g_dbus_create_error(message,
852                                 ERROR_INF ".InvalidArguments", NULL);
853
854         return pull_vcard_listing(pbap, message, "", pbap->order, value,
855                                         attrib, DEFAULT_COUNT, DEFAULT_OFFSET);
856 }
857
858 static DBusMessage *pbap_get_size(DBusConnection *connection,
859                                         DBusMessage *message, void *user_data)
860 {
861         struct pbap_data *pbap = user_data;
862         DBusMessage * err;
863         char *name;
864
865         if (!pbap->path)
866                 return g_dbus_create_error(message,
867                                 ERROR_INF ".Forbidden", "Call Select first of all");
868
869         name = g_strconcat(pbap->path, ".vcf", NULL);
870
871         err = pull_phonebook(pbap, message, GETPHONEBOOKSIZE, name,
872                                 pbap->filter, pbap->format, 0,
873                                 DEFAULT_OFFSET);
874         g_free(name);
875         return err;
876 }
877
878 static DBusMessage *pbap_set_format(DBusConnection *connection,
879                                         DBusMessage *message, void *user_data)
880 {
881         struct pbap_data *pbap = user_data;
882         const char *format;
883
884         if (dbus_message_get_args(message, NULL,
885                         DBUS_TYPE_STRING, &format,
886                         DBUS_TYPE_INVALID) == FALSE)
887                 return g_dbus_create_error(message,
888                                 ERROR_INF ".InvalidArguments", NULL);
889
890         if (set_format(pbap, format) < 0)
891                 return g_dbus_create_error(message,
892                                 ERROR_INF ".InvalidArguments", "InvalidFormat");
893
894         return dbus_message_new_method_return(message);
895 }
896
897 static DBusMessage *pbap_set_order(DBusConnection *connection,
898                                         DBusMessage *message, void *user_data)
899 {
900         struct pbap_data *pbap = user_data;
901         const char *order;
902
903         if (dbus_message_get_args(message, NULL,
904                         DBUS_TYPE_STRING, &order,
905                         DBUS_TYPE_INVALID) == FALSE)
906                 return g_dbus_create_error(message,
907                                 ERROR_INF ".InvalidArguments", NULL);
908
909         if (set_order(pbap, order) < 0)
910                 return g_dbus_create_error(message,
911                                 ERROR_INF ".InvalidArguments", "InvalidFilter");
912
913         return dbus_message_new_method_return(message);
914 }
915
916 static DBusMessage *pbap_set_filter(DBusConnection *connection,
917                                         DBusMessage *message, void *user_data)
918 {
919         struct pbap_data *pbap = user_data;
920         char **filters, **item;
921         gint size;
922         uint64_t oldfilter = pbap->filter;
923
924         if (dbus_message_get_args(message, NULL, DBUS_TYPE_ARRAY,
925                         DBUS_TYPE_STRING, &filters, &size,
926                         DBUS_TYPE_INVALID) == FALSE)
927                 return g_dbus_create_error(message,
928                                 ERROR_INF ".InvalidArguments", NULL);
929
930         remove_filter(pbap, "ALL");
931         if (size == 0)
932                 goto done;
933
934         for (item = filters; *item; item++) {
935                 if (add_filter(pbap, *item) < 0) {
936                         pbap->filter = oldfilter;
937                         g_strfreev(filters);
938                         return g_dbus_create_error(message,
939                                         ERROR_INF ".InvalidArguments", "InvalidFilters");
940                 }
941         }
942
943 done:
944         g_strfreev(filters);
945         return dbus_message_new_method_return(message);
946 }
947
948 static DBusMessage *pbap_get_filter(DBusConnection *connection,
949                                         DBusMessage *message, void *user_data)
950 {
951         struct pbap_data *pbap = user_data;
952         gchar **filters = NULL;
953         gint size;
954         DBusMessage *reply;
955
956         filters = get_filter_strs(pbap->filter, &size);
957         reply = dbus_message_new_method_return(message);
958         dbus_message_append_args(reply, DBUS_TYPE_ARRAY,
959                                 DBUS_TYPE_STRING, &filters, size,
960                                 DBUS_TYPE_INVALID);
961
962         g_strfreev(filters);
963         return reply;
964 }
965
966 static DBusMessage *pbap_list_filter_fields(DBusConnection *connection,
967                                         DBusMessage *message, void *user_data)
968 {
969         gchar **filters = NULL;
970         gint size;
971         DBusMessage *reply;
972
973         filters = get_filter_strs(FILTER_ALL, &size);
974         reply = dbus_message_new_method_return(message);
975         dbus_message_append_args(reply, DBUS_TYPE_ARRAY,
976                                 DBUS_TYPE_STRING, &filters, size,
977                                 DBUS_TYPE_INVALID);
978
979         g_strfreev(filters);
980         return reply;
981 }
982
983 static GDBusMethodTable pbap_methods[] = {
984         { "Select",     "ss",   "",     pbap_select,
985                                         G_DBUS_METHOD_FLAG_ASYNC },
986         { "PullAll",    "",     "s",    pbap_pull_all,
987                                         G_DBUS_METHOD_FLAG_ASYNC },
988         { "Pull",       "s",    "s",    pbap_pull_vcard,
989                                         G_DBUS_METHOD_FLAG_ASYNC },
990         { "List",       "",     "a(ss)",        pbap_list,
991                                         G_DBUS_METHOD_FLAG_ASYNC },
992         { "Search",     "ss",   "a(ss)",        pbap_search,
993                                         G_DBUS_METHOD_FLAG_ASYNC },
994         { "GetSize",    "",     "q",    pbap_get_size,
995                                         G_DBUS_METHOD_FLAG_ASYNC },
996         { "SetFormat",  "s",    "",     pbap_set_format },
997         { "SetOrder",   "s",    "",     pbap_set_order },
998         { "SetFilter",  "as",   "",     pbap_set_filter },
999         { "GetFilter",  "",     "as",   pbap_get_filter },
1000         { "ListFilterFields", "",       "as",   pbap_list_filter_fields },
1001         { }
1002 };
1003
1004 static void pbap_free(void *data)
1005 {
1006         struct pbap_data *pbap = data;
1007
1008         obc_session_unref(pbap->session);
1009         g_free(pbap->path);
1010         g_free(pbap);
1011 }
1012
1013 static int pbap_probe(struct obc_session *session)
1014 {
1015         struct pbap_data *pbap;
1016         const char *path;
1017
1018         path = obc_session_get_path(session);
1019
1020         DBG("%s", path);
1021
1022         pbap = g_try_new0(struct pbap_data, 1);
1023         if (!pbap)
1024                 return -ENOMEM;
1025
1026         pbap->session = obc_session_ref(session);
1027
1028         if (!g_dbus_register_interface(conn, path, PBAP_INTERFACE, pbap_methods,
1029                                                 NULL, NULL, pbap, pbap_free)) {
1030                 pbap_free(pbap);
1031                 return -ENOMEM;
1032         }
1033
1034         return 0;
1035 }
1036
1037 static void pbap_remove(struct obc_session *session)
1038 {
1039         const char *path = obc_session_get_path(session);
1040
1041         DBG("%s", path);
1042
1043         g_dbus_unregister_interface(conn, path, PBAP_INTERFACE);
1044 }
1045
1046 static struct obc_driver pbap = {
1047         .service = "PBAP",
1048         .uuid = PBAP_UUID,
1049         .target = OBEX_PBAP_UUID,
1050         .target_len = OBEX_PBAP_UUID_LEN,
1051         .probe = pbap_probe,
1052         .remove = pbap_remove
1053 };
1054
1055 int pbap_init(void)
1056 {
1057         int err;
1058
1059         DBG("");
1060
1061         conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
1062         if (!conn)
1063                 return -EIO;
1064
1065         err = obc_driver_register(&pbap);
1066         if (err < 0) {
1067                 dbus_connection_unref(conn);
1068                 conn = NULL;
1069                 return err;
1070         }
1071
1072         return 0;
1073 }
1074
1075 void pbap_exit(void)
1076 {
1077         DBG("");
1078
1079         dbus_connection_unref(conn);
1080         conn = NULL;
1081
1082         obc_driver_unregister(&pbap);
1083 }