upgrade obexd to 0.47
[profile/ivi/obexd.git] / plugins / pbap.c
1 /*
2  *
3  *  OBEX Server
4  *
5  *  Copyright (C) 2009-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 <stdio.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <glib.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <arpa/inet.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <inttypes.h>
40
41 #include <gobex.h>
42
43 #include "obexd.h"
44 #include "plugin.h"
45 #include "log.h"
46 #include "obex.h"
47 #include "service.h"
48 #include "phonebook.h"
49 #include "mimetype.h"
50 #include "filesystem.h"
51 #include "manager.h"
52
53 #define PHONEBOOK_TYPE          "x-bt/phonebook"
54 #define VCARDLISTING_TYPE       "x-bt/vcard-listing"
55 #define VCARDENTRY_TYPE         "x-bt/vcard"
56
57 #define ORDER_TAG               0x01
58 #define SEARCHVALUE_TAG         0x02
59 #define SEARCHATTRIB_TAG        0x03
60 #define MAXLISTCOUNT_TAG        0x04
61 #define LISTSTARTOFFSET_TAG     0x05
62 #define FILTER_TAG              0x06
63 #define FORMAT_TAG              0X07
64 #define PHONEBOOKSIZE_TAG       0X08
65 #define NEWMISSEDCALLS_TAG      0X09
66
67 /* The following length is in the unit of byte */
68 #define ORDER_LEN               1
69 #define SEARCHATTRIB_LEN        1
70 #define MAXLISTCOUNT_LEN        2
71 #define LISTSTARTOFFSET_LEN     2
72 #define FILTER_LEN              8
73 #define FORMAT_LEN              1
74 #define PHONEBOOKSIZE_LEN       2
75 #define NEWMISSEDCALLS_LEN      1
76
77 #define PBAP_CHANNEL    15
78
79 #define PBAP_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>        \
80 <record>                                                                \
81   <attribute id=\"0x0001\">                                             \
82     <sequence>                                                          \
83       <uuid value=\"0x112f\"/>                                          \
84     </sequence>                                                         \
85   </attribute>                                                          \
86                                                                         \
87   <attribute id=\"0x0004\">                                             \
88     <sequence>                                                          \
89       <sequence>                                                        \
90         <uuid value=\"0x0100\"/>                                        \
91       </sequence>                                                       \
92       <sequence>                                                        \
93         <uuid value=\"0x0003\"/>                                        \
94         <uint8 value=\"%u\" name=\"channel\"/>                          \
95       </sequence>                                                       \
96       <sequence>                                                        \
97         <uuid value=\"0x0008\"/>                                        \
98       </sequence>                                                       \
99     </sequence>                                                         \
100   </attribute>                                                          \
101                                                                         \
102   <attribute id=\"0x0009\">                                             \
103     <sequence>                                                          \
104       <sequence>                                                        \
105         <uuid value=\"0x1130\"/>                                        \
106         <uint16 value=\"0x0100\" name=\"version\"/>                     \
107       </sequence>                                                       \
108     </sequence>                                                         \
109   </attribute>                                                          \
110                                                                         \
111   <attribute id=\"0x0100\">                                             \
112     <text value=\"%s\" name=\"name\"/>                                  \
113   </attribute>                                                          \
114                                                                         \
115   <attribute id=\"0x0314\">                                             \
116     <uint8 value=\"0x01\"/>                                             \
117   </attribute>                                                          \
118 </record>"
119
120 struct aparam_header {
121         uint8_t tag;
122         uint8_t len;
123         uint8_t val[0];
124 } __attribute__ ((packed));
125
126 struct cache {
127         gboolean valid;
128         uint32_t index;
129         GSList *entries;
130 };
131
132 struct cache_entry {
133         uint32_t handle;
134         char *id;
135         char *name;
136         char *sound;
137         char *tel;
138 };
139
140 struct pbap_session {
141         struct apparam_field *params;
142         char *folder;
143         uint32_t find_handle;
144         struct cache cache;
145         struct pbap_object *obj;
146 };
147
148 struct pbap_object {
149         GString *buffer;
150         GByteArray *aparams;
151         gboolean firstpacket;
152         gboolean lastpart;
153         struct pbap_session *session;
154         void *request;
155 };
156
157 static const uint8_t PBAP_TARGET[TARGET_SIZE] = {
158                         0x79, 0x61, 0x35, 0xF0,  0xF0, 0xC5, 0x11, 0xD8,
159                         0x09, 0x66, 0x08, 0x00,  0x20, 0x0C, 0x9A, 0x66  };
160
161 typedef int (*cache_entry_find_f) (const struct cache_entry *entry,
162                         const char *value);
163
164 static void cache_entry_free(void *data)
165 {
166         struct cache_entry *entry = data;
167
168         g_free(entry->id);
169         g_free(entry->name);
170         g_free(entry->sound);
171         g_free(entry->tel);
172         g_free(entry);
173 }
174
175 static gboolean entry_name_find(const struct cache_entry *entry,
176                 const char *value)
177 {
178         char *name;
179         gboolean ret;
180
181         if (!entry->name)
182                 return FALSE;
183
184         if (strlen(value) == 0)
185                 return TRUE;
186
187         name = g_utf8_strdown(entry->name, -1);
188         ret = (g_strstr_len(name, -1, value) ? TRUE : FALSE);
189         g_free(name);
190
191         return ret;
192 }
193
194 static gboolean entry_sound_find(const struct cache_entry *entry,
195                 const char *value)
196 {
197         if (!entry->sound)
198                 return FALSE;
199
200         return (g_strstr_len(entry->sound, -1, value) ? TRUE : FALSE);
201 }
202
203 static gboolean entry_tel_find(const struct cache_entry *entry,
204                 const char *value)
205 {
206         if (!entry->tel)
207                 return FALSE;
208
209         return (g_strstr_len(entry->tel, -1, value) ? TRUE : FALSE);
210 }
211
212 static const char *cache_find(struct cache *cache, uint32_t handle)
213 {
214         GSList *l;
215
216         for (l = cache->entries; l; l = l->next) {
217                 struct cache_entry *entry = l->data;
218
219                 if (entry->handle == handle)
220                         return entry->id;
221         }
222
223         return NULL;
224 }
225
226 static void cache_clear(struct cache *cache)
227 {
228         g_slist_free_full(cache->entries, cache_entry_free);
229         cache->entries = NULL;
230 }
231
232 static GByteArray *append_aparam_header(GByteArray *buf, uint8_t tag,
233                                                         const void *val)
234 {
235         /* largest aparam is for phonebooksize (4 bytes) */
236         uint8_t aparam[sizeof(struct aparam_header) + PHONEBOOKSIZE_LEN];
237         struct aparam_header *hdr = (struct aparam_header *) aparam;
238
239         switch (tag) {
240         case PHONEBOOKSIZE_TAG:
241                 hdr->tag = PHONEBOOKSIZE_TAG;
242                 hdr->len = PHONEBOOKSIZE_LEN;
243                 memcpy(hdr->val, val, PHONEBOOKSIZE_LEN);
244
245                 return g_byte_array_append(buf, aparam,
246                         sizeof(struct aparam_header) + PHONEBOOKSIZE_LEN);
247         case NEWMISSEDCALLS_TAG:
248                 hdr->tag = NEWMISSEDCALLS_TAG;
249                 hdr->len = NEWMISSEDCALLS_LEN;
250                 memcpy(hdr->val, val, NEWMISSEDCALLS_LEN);
251
252                 return g_byte_array_append(buf, aparam,
253                         sizeof(struct aparam_header) + NEWMISSEDCALLS_LEN);
254         default:
255                 return buf;
256         }
257 }
258
259 static void phonebook_size_result(const char *buffer, size_t bufsize,
260                                         int vcards, int missed,
261                                         gboolean lastpart, void *user_data)
262 {
263         struct pbap_session *pbap = user_data;
264         uint16_t phonebooksize;
265
266         if (pbap->obj->request) {
267                 phonebook_req_finalize(pbap->obj->request);
268                 pbap->obj->request = NULL;
269         }
270
271         if (vcards < 0)
272                 vcards = 0;
273
274         DBG("vcards %d", vcards);
275
276         phonebooksize = htons(vcards);
277
278         pbap->obj->aparams = g_byte_array_new();
279         pbap->obj->aparams = append_aparam_header(pbap->obj->aparams,
280                                         PHONEBOOKSIZE_TAG, &phonebooksize);
281
282         if (missed > 0) {
283                 DBG("missed %d", missed);
284
285                 pbap->obj->aparams = append_aparam_header(pbap->obj->aparams,
286                                                 NEWMISSEDCALLS_TAG, &missed);
287         }
288
289         obex_object_set_io_flags(pbap->obj, G_IO_IN, 0);
290 }
291
292 static void query_result(const char *buffer, size_t bufsize, int vcards,
293                                 int missed, gboolean lastpart, void *user_data)
294 {
295         struct pbap_session *pbap = user_data;
296
297         DBG("");
298
299         if (pbap->obj->request && lastpart) {
300                 phonebook_req_finalize(pbap->obj->request);
301                 pbap->obj->request = NULL;
302         }
303
304         pbap->obj->lastpart = lastpart;
305
306         if (vcards < 0) {
307                 obex_object_set_io_flags(pbap->obj, G_IO_ERR, -ENOENT);
308                 return;
309         }
310
311         if (!pbap->obj->buffer)
312                 pbap->obj->buffer = g_string_new_len(buffer, bufsize);
313         else
314                 pbap->obj->buffer = g_string_append_len(pbap->obj->buffer,
315                                                         buffer, bufsize);
316
317         if (missed > 0) {
318                 DBG("missed %d", missed);
319
320                 pbap->obj->firstpacket = TRUE;
321
322                 pbap->obj->aparams = g_byte_array_new();
323                 pbap->obj->aparams = append_aparam_header(pbap->obj->aparams,
324                                                 NEWMISSEDCALLS_TAG, &missed);
325         }
326
327         obex_object_set_io_flags(pbap->obj, G_IO_IN, 0);
328 }
329
330 static void cache_entry_notify(const char *id, uint32_t handle,
331                                         const char *name, const char *sound,
332                                         const char *tel, void *user_data)
333 {
334         struct pbap_session *pbap = user_data;
335         struct cache_entry *entry = g_new0(struct cache_entry, 1);
336         struct cache *cache = &pbap->cache;
337
338         if (handle != PHONEBOOK_INVALID_HANDLE)
339                 entry->handle = handle;
340         else
341                 entry->handle = ++pbap->cache.index;
342
343         entry->id = g_strdup(id);
344         entry->name = g_strdup(name);
345         entry->sound = g_strdup(sound);
346         entry->tel = g_strdup(tel);
347
348         cache->entries = g_slist_append(cache->entries, entry);
349 }
350
351 static int alpha_sort(gconstpointer a, gconstpointer b)
352 {
353         const struct cache_entry *e1 = a;
354         const struct cache_entry *e2 = b;
355
356         return g_strcmp0(e1->name, e2->name);
357 }
358
359 static int indexed_sort(gconstpointer a, gconstpointer b)
360 {
361         const struct cache_entry *e1 = a;
362         const struct cache_entry *e2 = b;
363
364         return (e1->handle - e2->handle);
365 }
366
367 static int phonetical_sort(gconstpointer a, gconstpointer b)
368 {
369         const struct cache_entry *e1 = a;
370         const struct cache_entry *e2 = b;
371
372         /* SOUND attribute is optional. Use Indexed sort if not present. */
373         if (!e1->sound || !e2->sound)
374                 return indexed_sort(a, b);
375
376         return g_strcmp0(e1->sound, e2->sound);
377 }
378
379 static GSList *sort_entries(GSList *l, uint8_t order, uint8_t search_attrib,
380                                                         const char *value)
381 {
382         GSList *sorted = NULL;
383         cache_entry_find_f find;
384         GCompareFunc sort;
385         char *searchval;
386
387         /*
388          * Default sorter is "Indexed". Some backends doesn't inform the index,
389          * for this case a sequential internal index is assigned.
390          * 0x00 = indexed
391          * 0x01 = alphanumeric
392          * 0x02 = phonetic
393          */
394         switch (order) {
395         case 0x01:
396                 sort = alpha_sort;
397                 break;
398         case 0x02:
399                 sort = phonetical_sort;
400                 break;
401         default:
402                 sort = indexed_sort;
403                 break;
404         }
405
406         /*
407          * This implementation checks if the given field CONTAINS the
408          * search value(case insensitive). Name is the default field
409          * when the attribute is not provided.
410          */
411         switch (search_attrib) {
412                 /* Number */
413                 case 1:
414                         find = entry_tel_find;
415                         break;
416                 /* Sound */
417                 case 2:
418                         find = entry_sound_find;
419                         break;
420                 default:
421                         find = entry_name_find;
422                         break;
423         }
424
425         searchval = value ? g_utf8_strdown(value, -1) : NULL;
426         for (; l; l = l->next) {
427                 struct cache_entry *entry = l->data;
428
429                 if (searchval && !find(entry, (const char *) searchval))
430                         continue;
431
432                 sorted = g_slist_insert_sorted(sorted, entry, sort);
433         }
434
435         g_free(searchval);
436
437         return sorted;
438 }
439
440 static int generate_response(void *user_data)
441 {
442         struct pbap_session *pbap = user_data;
443         GSList *sorted;
444         GSList *l;
445         uint16_t max = pbap->params->maxlistcount;
446
447         DBG("");
448
449         if (max == 0) {
450                 /* Ignore all other parameter and return PhoneBookSize */
451                 uint16_t size = htons(g_slist_length(pbap->cache.entries));
452
453                 pbap->obj->aparams = g_byte_array_new();
454                 pbap->obj->aparams = append_aparam_header(pbap->obj->aparams,
455                                                 PHONEBOOKSIZE_TAG, &size);
456
457                 return 0;
458         }
459
460         /*
461          * Don't free the sorted list content: this list contains
462          * only the reference for the "real" cache entry.
463          */
464         sorted = sort_entries(pbap->cache.entries, pbap->params->order,
465                                 pbap->params->searchattrib,
466                                 (const char *) pbap->params->searchval);
467
468         /* Computing offset considering first entry of the phonebook */
469         l = g_slist_nth(sorted, pbap->params->liststartoffset);
470
471         pbap->obj->buffer = g_string_new(VCARD_LISTING_BEGIN);
472         for (; l && max; l = l->next, max--) {
473                 const struct cache_entry *entry = l->data;
474                 char *escaped_name = g_markup_escape_text(entry->name, -1);
475
476                 g_string_append_printf(pbap->obj->buffer,
477                         VCARD_LISTING_ELEMENT, entry->handle, escaped_name);
478
479                 g_free(escaped_name);
480         }
481
482         pbap->obj->buffer = g_string_append(pbap->obj->buffer,
483                                                         VCARD_LISTING_END);
484         g_slist_free(sorted);
485
486         return 0;
487 }
488
489 static void cache_ready_notify(void *user_data)
490 {
491         struct pbap_session *pbap = user_data;
492
493         DBG("");
494
495         phonebook_req_finalize(pbap->obj->request);
496         pbap->obj->request = NULL;
497
498         pbap->cache.valid = TRUE;
499
500         generate_response(pbap);
501         obex_object_set_io_flags(pbap->obj, G_IO_IN, 0);
502 }
503
504 static void cache_entry_done(void *user_data)
505 {
506         struct pbap_session *pbap = user_data;
507         const char *id;
508         int ret;
509
510         DBG("");
511
512         pbap->cache.valid = TRUE;
513
514         id = cache_find(&pbap->cache, pbap->find_handle);
515         if (id == NULL) {
516                 DBG("Entry %d not found on cache", pbap->find_handle);
517                 obex_object_set_io_flags(pbap->obj, G_IO_ERR, -ENOENT);
518                 return;
519         }
520
521         phonebook_req_finalize(pbap->obj->request);
522         pbap->obj->request = phonebook_get_entry(pbap->folder, id,
523                                 pbap->params, query_result, pbap, &ret);
524         if (ret < 0)
525                 obex_object_set_io_flags(pbap->obj, G_IO_ERR, ret);
526 }
527
528 static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen)
529 {
530         struct apparam_field *param;
531         struct aparam_header *hdr;
532         uint64_t val64;
533         uint32_t len = 0;
534         uint16_t val16;
535
536         param = g_new0(struct apparam_field, 1);
537
538         while (len < hlen) {
539                 hdr = (void *) buffer + len;
540
541                 switch (hdr->tag) {
542                 case ORDER_TAG:
543                         if (hdr->len != ORDER_LEN)
544                                 goto failed;
545
546                         param->order = hdr->val[0];
547                         break;
548
549                 case SEARCHATTRIB_TAG:
550                         if (hdr->len != SEARCHATTRIB_LEN)
551                                 goto failed;
552
553                         param->searchattrib = hdr->val[0];
554                         break;
555                 case SEARCHVALUE_TAG:
556                         if (hdr->len == 0)
557                                 goto failed;
558
559                         param->searchval = g_try_malloc0(hdr->len + 1);
560                         if (param->searchval)
561                                 memcpy(param->searchval, hdr->val, hdr->len);
562                         break;
563                 case FILTER_TAG:
564                         if (hdr->len != FILTER_LEN)
565                                 goto failed;
566
567                         memcpy(&val64, hdr->val, sizeof(val64));
568                         param->filter = GUINT64_FROM_BE(val64);
569
570                         break;
571                 case FORMAT_TAG:
572                         if (hdr->len != FORMAT_LEN)
573                                 goto failed;
574
575                         param->format = hdr->val[0];
576                         break;
577                 case MAXLISTCOUNT_TAG:
578                         if (hdr->len != MAXLISTCOUNT_LEN)
579                                 goto failed;
580
581                         memcpy(&val16, hdr->val, sizeof(val16));
582                         param->maxlistcount = GUINT16_FROM_BE(val16);
583                         break;
584                 case LISTSTARTOFFSET_TAG:
585                         if (hdr->len != LISTSTARTOFFSET_LEN)
586                                 goto failed;
587
588                         memcpy(&val16, hdr->val, sizeof(val16));
589                         param->liststartoffset = GUINT16_FROM_BE(val16);
590                         break;
591                 default:
592                         goto failed;
593                 }
594
595                 len += hdr->len + sizeof(struct aparam_header);
596         }
597
598         DBG("o %x sa %x sv %s fil %" G_GINT64_MODIFIER "x for %x max %x off %x",
599                         param->order, param->searchattrib, param->searchval,
600                         param->filter, param->format, param->maxlistcount,
601                         param->liststartoffset);
602
603         return param;
604
605 failed:
606         g_free(param);
607
608         return NULL;
609 }
610
611 static void *pbap_connect(struct obex_session *os, int *err)
612 {
613         struct pbap_session *pbap;
614
615         manager_register_session(os);
616
617         pbap = g_new0(struct pbap_session, 1);
618         pbap->folder = g_strdup("/");
619         pbap->find_handle = PHONEBOOK_INVALID_HANDLE;
620
621         if (err)
622                 *err = 0;
623
624         return pbap;
625 }
626
627 static int pbap_get(struct obex_session *os, void *user_data)
628 {
629         struct pbap_session *pbap = user_data;
630         const char *type = obex_get_type(os);
631         const char *name = obex_get_name(os);
632         struct apparam_field *params;
633         const uint8_t *buffer;
634         char *path;
635         ssize_t rsize;
636         int ret;
637
638         DBG("name %s type %s pbap %p", name, type, pbap);
639
640         if (type == NULL)
641                 return -EBADR;
642
643         rsize = obex_get_apparam(os, &buffer);
644         if (rsize < 0) {
645                 if (g_ascii_strcasecmp(type, VCARDENTRY_TYPE) != 0)
646                         return -EBADR;
647
648                 rsize = 0;
649         }
650
651         params = parse_aparam(buffer, rsize);
652         if (params == NULL)
653                 return -EBADR;
654
655         if (pbap->params) {
656                 g_free(pbap->params->searchval);
657                 g_free(pbap->params);
658         }
659
660         pbap->params = params;
661
662         if (g_ascii_strcasecmp(type, PHONEBOOK_TYPE) == 0) {
663                 /* Always contains the absolute path */
664                 if (g_path_is_absolute(name))
665                         path = g_strdup(name);
666                 else
667                         path = g_build_filename("/", name, NULL);
668
669         } else if (g_ascii_strcasecmp(type, VCARDLISTING_TYPE) == 0) {
670                 /* Always relative */
671                 if (!name || strlen(name) == 0)
672                         /* Current folder */
673                         path = g_strdup(pbap->folder);
674                 else
675                         /* Current folder + relative path */
676                         path = g_build_filename(pbap->folder, name, NULL);
677
678         } else if (g_ascii_strcasecmp(type, VCARDENTRY_TYPE) == 0) {
679                 /* File name only */
680                 path = g_strdup(name);
681         } else
682                 return -EBADR;
683
684         if (path == NULL)
685                 return -EBADR;
686
687         ret = obex_get_stream_start(os, path);
688
689         g_free(path);
690
691         return ret;
692 }
693
694 static int pbap_setpath(struct obex_session *os, void *user_data)
695 {
696         struct pbap_session *pbap = user_data;
697         const char *name;
698         const uint8_t *nonhdr;
699         char *fullname;
700         int err;
701
702         if (obex_get_non_header_data(os, &nonhdr) != 2) {
703                 error("Set path failed: flag and constants not found!");
704                 return -EBADMSG;
705         }
706
707         name = obex_get_name(os);
708
709         DBG("name %s folder %s nonhdr 0x%x%x", name, pbap->folder,
710                                                         nonhdr[0], nonhdr[1]);
711
712         fullname = phonebook_set_folder(pbap->folder, name, nonhdr[0], &err);
713         if (err < 0)
714                 return err;
715
716         g_free(pbap->folder);
717         pbap->folder = fullname;
718
719         /*
720          * FIXME: Define a criteria to mark the cache as invalid
721          */
722         pbap->cache.valid = FALSE;
723         pbap->cache.index = 0;
724         cache_clear(&pbap->cache);
725
726         return 0;
727 }
728
729 static void pbap_disconnect(struct obex_session *os, void *user_data)
730 {
731         struct pbap_session *pbap = user_data;
732
733         manager_unregister_session(os);
734
735         if (pbap->obj)
736                 pbap->obj->session = NULL;
737
738         if (pbap->params) {
739                 g_free(pbap->params->searchval);
740                 g_free(pbap->params);
741         }
742
743         cache_clear(&pbap->cache);
744         g_free(pbap->folder);
745         g_free(pbap);
746 }
747
748 static int pbap_chkput(struct obex_session *os, void *user_data)
749 {
750         /* Rejects all PUTs */
751         return -EBADR;
752 }
753
754 static struct obex_service_driver pbap = {
755         .name = "Phonebook Access server",
756         .service = OBEX_PBAP,
757         .channel = PBAP_CHANNEL,
758         .secure = TRUE,
759         .record = PBAP_RECORD,
760         .target = PBAP_TARGET,
761         .target_size = TARGET_SIZE,
762         .connect = pbap_connect,
763         .get = pbap_get,
764         .setpath = pbap_setpath,
765         .disconnect = pbap_disconnect,
766         .chkput = pbap_chkput
767 };
768
769 static struct pbap_object *vobject_create(struct pbap_session *pbap,
770                                                                 void *request)
771 {
772         struct pbap_object *obj;
773
774         obj = g_new0(struct pbap_object, 1);
775         obj->session = pbap;
776         pbap->obj = obj;
777         obj->request = request;
778
779         return obj;
780 }
781
782 static void *vobject_pull_open(const char *name, int oflag, mode_t mode,
783                                 void *context, size_t *size, int *err)
784 {
785         struct pbap_session *pbap = context;
786         phonebook_cb cb;
787         int ret;
788         void *request;
789
790         DBG("name %s context %p maxlistcount %d", name, context,
791                                                 pbap->params->maxlistcount);
792
793         if (oflag != O_RDONLY) {
794                 ret = -EPERM;
795                 goto fail;
796         }
797
798         if (name == NULL) {
799                 ret = -EBADR;
800                 goto fail;
801         }
802
803         if (pbap->params->maxlistcount == 0)
804                 cb = phonebook_size_result;
805         else
806                 cb = query_result;
807
808         request = phonebook_pull(name, pbap->params, cb, pbap, &ret);
809
810         if (ret < 0)
811                 goto fail;
812
813         /* reading first part of results from backend */
814         ret = phonebook_pull_read(request);
815         if (ret < 0)
816                 goto fail;
817
818         if (err)
819                 *err = 0;
820
821         return vobject_create(pbap, request);
822
823 fail:
824         if (err)
825                 *err = ret;
826
827         return NULL;
828 }
829
830 static int vobject_close(void *object)
831 {
832         struct pbap_object *obj = object;
833
834         DBG("");
835
836         if (obj->session)
837                 obj->session->obj = NULL;
838
839         if (obj->buffer)
840                 g_string_free(obj->buffer, TRUE);
841
842         if (obj->aparams)
843                 g_byte_array_free(obj->aparams, TRUE);
844
845         if (obj->request)
846                 phonebook_req_finalize(obj->request);
847
848         g_free(obj);
849
850         return 0;
851 }
852
853 static void *vobject_list_open(const char *name, int oflag, mode_t mode,
854                                 void *context, size_t *size, int *err)
855 {
856         struct pbap_session *pbap = context;
857         struct pbap_object *obj = NULL;
858         int ret;
859         void *request;
860
861         DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
862
863         if (oflag != O_RDONLY) {
864                 ret = -EPERM;
865                 goto fail;
866         }
867
868         if (name == NULL) {
869                 ret = -EBADR;
870                 goto fail;
871         }
872
873         /* PullvCardListing always get the contacts from the cache */
874
875         if (pbap->cache.valid) {
876                 obj = vobject_create(pbap, NULL);
877                 ret = generate_response(pbap);
878         } else {
879                 request = phonebook_create_cache(name, cache_entry_notify,
880                                         cache_ready_notify, pbap, &ret);
881                 if (ret == 0)
882                         obj = vobject_create(pbap, request);
883         }
884         if (ret < 0)
885                 goto fail;
886
887         if (err)
888                 *err = 0;
889
890         return obj;
891
892 fail:
893         if (obj)
894                 vobject_close(obj);
895
896         if (err)
897                 *err = ret;
898
899         return NULL;
900 }
901
902 static void *vobject_vcard_open(const char *name, int oflag, mode_t mode,
903                                         void *context, size_t *size, int *err)
904 {
905         struct pbap_session *pbap = context;
906         const char *id;
907         uint32_t handle;
908         int ret;
909         void *request;
910
911         DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
912
913         if (oflag != O_RDONLY) {
914                 ret = -EPERM;
915                 goto fail;
916         }
917
918         if (name == NULL || sscanf(name, "%u.vcf", &handle) != 1) {
919                 ret = -EBADR;
920                 goto fail;
921         }
922
923         if (pbap->cache.valid == FALSE) {
924                 pbap->find_handle = handle;
925                 request = phonebook_create_cache(pbap->folder,
926                         cache_entry_notify, cache_entry_done, pbap, &ret);
927                 goto done;
928         }
929
930         id = cache_find(&pbap->cache, handle);
931         if (!id) {
932                 ret = -ENOENT;
933                 goto fail;
934         }
935
936         request = phonebook_get_entry(pbap->folder, id, pbap->params,
937                                                 query_result, pbap, &ret);
938
939 done:
940         if (ret < 0)
941                 goto fail;
942
943         if (err)
944                 *err = 0;
945
946         return vobject_create(pbap, request);
947
948 fail:
949         if (err)
950                 *err = ret;
951
952         return NULL;
953 }
954
955 static ssize_t array_read(GByteArray *array, void *buf, size_t count)
956 {
957         ssize_t len;
958
959         if (array->len == 0)
960                 return 0;
961
962         len = MIN(array->len, count);
963         memcpy(buf, array->data, len);
964         g_byte_array_remove_range(array, 0, len);
965
966         return len;
967 }
968
969 static ssize_t vobject_pull_get_next_header(void *object, void *buf, size_t mtu,
970                                                                 uint8_t *hi)
971 {
972         struct pbap_object *obj = object;
973         struct pbap_session *pbap = obj->session;
974
975         if (!obj->buffer && !obj->aparams)
976                 return -EAGAIN;
977
978         *hi = G_OBEX_HDR_APPARAM;
979
980         if (pbap->params->maxlistcount == 0 || obj->firstpacket) {
981                 obj->firstpacket = FALSE;
982
983                 return array_read(obj->aparams, buf, mtu);
984         }
985
986         return 0;
987 }
988
989 static ssize_t vobject_pull_read(void *object, void *buf, size_t count)
990 {
991         struct pbap_object *obj = object;
992         struct pbap_session *pbap = obj->session;
993         int len, ret;
994
995         DBG("buffer %p maxlistcount %d", obj->buffer,
996                                                 pbap->params->maxlistcount);
997
998         if (!obj->buffer) {
999                 if (pbap->params->maxlistcount == 0)
1000                         return -ENOSTR;
1001
1002                 return -EAGAIN;
1003         }
1004
1005         len = string_read(obj->buffer, buf, count);
1006         if (len == 0 && !obj->lastpart) {
1007                 /* in case when buffer is empty and we know that more
1008                  * data is still available in backend, requesting new
1009                  * data part via phonebook_pull_read and returning
1010                  * -EAGAIN to suspend request for now */
1011                 ret = phonebook_pull_read(obj->request);
1012                 if (ret)
1013                         return -EPERM;
1014
1015                 return -EAGAIN;
1016         }
1017
1018         return len;
1019 }
1020
1021 static ssize_t vobject_list_get_next_header(void *object, void *buf, size_t mtu,
1022                                                                 uint8_t *hi)
1023 {
1024         struct pbap_object *obj = object;
1025         struct pbap_session *pbap = obj->session;
1026
1027         /* Backend still busy reading contacts */
1028         if (!pbap->cache.valid)
1029                 return -EAGAIN;
1030
1031         *hi = G_OBEX_HDR_APPARAM;
1032
1033         if (pbap->params->maxlistcount == 0)
1034                 return array_read(obj->aparams, buf, mtu);
1035
1036         return 0;
1037 }
1038
1039 static ssize_t vobject_list_read(void *object, void *buf, size_t count)
1040 {
1041         struct pbap_object *obj = object;
1042         struct pbap_session *pbap = obj->session;
1043
1044         DBG("valid %d maxlistcount %d", pbap->cache.valid,
1045                                                 pbap->params->maxlistcount);
1046
1047         if (pbap->params->maxlistcount == 0)
1048                 return -ENOSTR;
1049
1050         return string_read(obj->buffer, buf, count);
1051 }
1052
1053 static ssize_t vobject_vcard_read(void *object, void *buf, size_t count)
1054 {
1055         struct pbap_object *obj = object;
1056
1057         DBG("buffer %p", obj->buffer);
1058
1059         if (!obj->buffer)
1060                 return -EAGAIN;
1061
1062         return string_read(obj->buffer, buf, count);
1063 }
1064
1065 static struct obex_mime_type_driver mime_pull = {
1066         .target = PBAP_TARGET,
1067         .target_size = TARGET_SIZE,
1068         .mimetype = "x-bt/phonebook",
1069         .open = vobject_pull_open,
1070         .close = vobject_close,
1071         .read = vobject_pull_read,
1072         .get_next_header = vobject_pull_get_next_header,
1073 };
1074
1075 static struct obex_mime_type_driver mime_list = {
1076         .target = PBAP_TARGET,
1077         .target_size = TARGET_SIZE,
1078         .mimetype = "x-bt/vcard-listing",
1079         .open = vobject_list_open,
1080         .close = vobject_close,
1081         .read = vobject_list_read,
1082         .get_next_header = vobject_list_get_next_header,
1083 };
1084
1085 static struct obex_mime_type_driver mime_vcard = {
1086         .target = PBAP_TARGET,
1087         .target_size = TARGET_SIZE,
1088         .mimetype = "x-bt/vcard",
1089         .open = vobject_vcard_open,
1090         .close = vobject_close,
1091         .read = vobject_vcard_read,
1092 };
1093
1094 static int pbap_init(void)
1095 {
1096         int err;
1097
1098         err = phonebook_init();
1099         if (err < 0)
1100                 return err;
1101
1102         err = obex_mime_type_driver_register(&mime_pull);
1103         if (err < 0)
1104                 goto fail_mime_pull;
1105
1106         err = obex_mime_type_driver_register(&mime_list);
1107         if (err < 0)
1108                 goto fail_mime_list;
1109
1110         err = obex_mime_type_driver_register(&mime_vcard);
1111         if (err < 0)
1112                 goto fail_mime_vcard;
1113
1114         err = obex_service_driver_register(&pbap);
1115         if (err < 0)
1116                 goto fail_pbap_reg;
1117
1118         return 0;
1119
1120 fail_pbap_reg:
1121         obex_mime_type_driver_unregister(&mime_vcard);
1122 fail_mime_vcard:
1123         obex_mime_type_driver_unregister(&mime_list);
1124 fail_mime_list:
1125         obex_mime_type_driver_unregister(&mime_pull);
1126 fail_mime_pull:
1127         phonebook_exit();
1128
1129         return err;
1130 }
1131
1132 static void pbap_exit(void)
1133 {
1134         obex_service_driver_unregister(&pbap);
1135         obex_mime_type_driver_unregister(&mime_pull);
1136         obex_mime_type_driver_unregister(&mime_list);
1137         obex_mime_type_driver_unregister(&mime_vcard);
1138         phonebook_exit();
1139 }
1140
1141 OBEX_PLUGIN_DEFINE(pbap, pbap_init, pbap_exit)