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