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

1 by Alexander Sack
Import upstream version 0.14
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
}