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