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