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