~ubuntu-branches/ubuntu/lucid/obexd/lucid

« back to all changes in this revision

Viewing changes to client/pbap.c

  • Committer: Bazaar Package Importer
  • Author(s): Alexander Sack
  • Date: 2009-07-27 19:02:30 UTC
  • Revision ID: james.westby@ubuntu.com-20090727190230-aid6tdvgzit4d8r4
Tags: upstream-0.14
ImportĀ upstreamĀ versionĀ 0.14

Show diffs side-by-side

added added

removed removed

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