5 * Copyright (C) 2009-2010 Intel Corporation
6 * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
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.
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.
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
35 #include <arpa/inet.h>
36 #include <sys/types.h>
41 #include "gobex/gobex.h"
42 #include "gobex/gobex-apparam.h"
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"
54 #define PHONEBOOK_TYPE "x-bt/phonebook"
55 #define VCARDLISTING_TYPE "x-bt/vcard-listing"
56 #define VCARDENTRY_TYPE "x-bt/vcard"
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
68 #ifdef __TIZEN_PATCH__
69 #define PBAP_MAXLISTCOUNT_MAX_VALUE 65535
70 #endif /* __TIZEN_PATCH__ */
87 struct apparam_field *params;
91 struct pbap_object *obj;
92 #ifdef __TIZEN_PATCH__
99 GObexApparam *apparam;
100 gboolean firstpacket;
102 struct pbap_session *session;
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 };
110 typedef int (*cache_entry_find_f) (const struct cache_entry *entry,
113 static void cache_entry_free(void *data)
115 struct cache_entry *entry = data;
119 g_free(entry->sound);
124 static gboolean entry_name_find(const struct cache_entry *entry,
133 if (strlen(value) == 0)
136 name = g_utf8_strdown(entry->name, -1);
137 ret = (g_strstr_len(name, -1, value) ? TRUE : FALSE);
143 static gboolean entry_sound_find(const struct cache_entry *entry,
149 return (g_strstr_len(entry->sound, -1, value) ? TRUE : FALSE);
152 static gboolean entry_tel_find(const struct cache_entry *entry,
158 return (g_strstr_len(entry->tel, -1, value) ? TRUE : FALSE);
161 static const char *cache_find(struct cache *cache, uint32_t handle)
165 for (l = cache->entries; l; l = l->next) {
166 struct cache_entry *entry = l->data;
168 if (entry->handle == handle)
175 static void cache_clear(struct cache *cache)
177 g_slist_free_full(cache->entries, cache_entry_free);
178 cache->entries = NULL;
181 static void phonebook_size_result(const char *buffer, size_t bufsize,
182 int vcards, int missed,
183 gboolean lastpart, void *user_data)
185 struct pbap_session *pbap = user_data;
186 uint16_t phonebooksize;
188 if (pbap->obj->request) {
189 phonebook_req_finalize(pbap->obj->request);
190 pbap->obj->request = NULL;
196 DBG("vcards %d", vcards);
198 phonebooksize = vcards;
200 pbap->obj->apparam = g_obex_apparam_set_uint16(NULL, PHONEBOOKSIZE_TAG,
203 pbap->obj->firstpacket = TRUE;
204 #ifndef __TIZEN_PATCH__
207 if (pbap->params->required_missedcall_call_header == TRUE) {
208 #endif /* __TIZEN_PATCH__ */
209 DBG("missed %d", missed);
211 #ifndef __TIZEN_PATCH__
212 pbap->obj->apparam = g_obex_apparam_set_uint16(
214 pbap->obj->apparam = g_obex_apparam_set_uint8(
221 obex_object_set_io_flags(pbap->obj, G_IO_IN, 0);
224 static void query_result(const char *buffer, size_t bufsize, int vcards,
225 int missed, gboolean lastpart, void *user_data)
227 struct pbap_session *pbap = user_data;
231 if (pbap->obj->request && lastpart) {
232 phonebook_req_finalize(pbap->obj->request);
233 pbap->obj->request = NULL;
236 pbap->obj->lastpart = lastpart;
239 obex_object_set_io_flags(pbap->obj, G_IO_ERR, -ENOENT);
243 if (!pbap->obj->buffer)
244 pbap->obj->buffer = g_string_new_len(buffer, bufsize);
246 pbap->obj->buffer = g_string_append_len(pbap->obj->buffer,
248 #ifndef __TIZEN_PATCH__
251 if (pbap->params->required_missedcall_call_header == TRUE) {
252 #endif /* _TIZEN_PATCH__ */
253 DBG("missed %d", missed);
255 pbap->obj->firstpacket = TRUE;
257 #ifndef __TIZEN_PATCH__
258 pbap->obj->apparam = g_obex_apparam_set_uint16(
260 pbap->obj->apparam = g_obex_apparam_set_uint8(
267 obex_object_set_io_flags(pbap->obj, G_IO_IN, 0);
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)
274 struct pbap_session *pbap = user_data;
275 struct cache_entry *entry = g_new0(struct cache_entry, 1);
276 struct cache *cache = &pbap->cache;
278 if (handle != PHONEBOOK_INVALID_HANDLE)
279 entry->handle = handle;
281 entry->handle = ++pbap->cache.index;
283 entry->id = g_strdup(id);
284 entry->name = g_strdup(name);
285 entry->sound = g_strdup(sound);
286 entry->tel = g_strdup(tel);
288 cache->entries = g_slist_append(cache->entries, entry);
291 static int alpha_sort(gconstpointer a, gconstpointer b)
293 const struct cache_entry *e1 = a;
294 const struct cache_entry *e2 = b;
296 return g_strcmp0(e1->name, e2->name);
299 static int indexed_sort(gconstpointer a, gconstpointer b)
301 const struct cache_entry *e1 = a;
302 const struct cache_entry *e2 = b;
304 return (e1->handle - e2->handle);
307 static int phonetical_sort(gconstpointer a, gconstpointer b)
309 const struct cache_entry *e1 = a;
310 const struct cache_entry *e2 = b;
312 /* SOUND attribute is optional. Use Indexed sort if not present. */
313 if (!e1->sound || !e2->sound)
314 return indexed_sort(a, b);
316 return g_strcmp0(e1->sound, e2->sound);
319 static GSList *sort_entries(GSList *l, uint8_t order, uint8_t search_attrib,
322 GSList *sorted = NULL;
323 cache_entry_find_f find;
328 * Default sorter is "Indexed". Some backends doesn't inform the index,
329 * for this case a sequential internal index is assigned.
331 * 0x01 = alphanumeric
339 sort = phonetical_sort;
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.
351 switch (search_attrib) {
354 find = entry_tel_find;
358 find = entry_sound_find;
361 find = entry_name_find;
365 searchval = value ? g_utf8_strdown(value, -1) : NULL;
366 for (; l; l = l->next) {
367 struct cache_entry *entry = l->data;
369 if (searchval && !find(entry, (const char *) searchval))
372 sorted = g_slist_insert_sorted(sorted, entry, sort);
380 static int generate_response(void *user_data)
382 struct pbap_session *pbap = user_data;
385 uint16_t max = pbap->params->maxlistcount;
390 /* Ignore all other parameter and return PhoneBookSize */
391 uint16_t size = g_slist_length(pbap->cache.entries);
393 pbap->obj->apparam = g_obex_apparam_set_uint16(
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(
403 pbap->params->new_missed_calls);
405 #endif /* __TIZEN_PATCH__ */
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(
415 pbap->params->new_missed_calls);
417 #endif /* __TIZEN_PATCH__ */
420 * Don't free the sorted list content: this list contains
421 * only the reference for the "real" cache entry.
423 sorted = sort_entries(pbap->cache.entries, pbap->params->order,
424 pbap->params->searchattrib,
425 (const char *) pbap->params->searchval);
427 /* Computing offset considering first entry of the phonebook */
428 l = g_slist_nth(sorted, pbap->params->liststartoffset);
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);
435 g_string_append_printf(pbap->obj->buffer,
436 VCARD_LISTING_ELEMENT, entry->handle, escaped_name);
438 g_free(escaped_name);
441 pbap->obj->buffer = g_string_append(pbap->obj->buffer,
443 g_slist_free(sorted);
448 #ifndef __TIZEN_PATCH__
449 static void cache_ready_notify(void *user_data)
451 static void cache_ready_notify(void *user_data, unsigned int new_missed_call)
452 #endif /* __TIZEN_PATCH__ */
454 struct pbap_session *pbap = user_data;
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;
463 pbap->cache.valid = TRUE;
465 generate_response(pbap);
466 obex_object_set_io_flags(pbap->obj, G_IO_IN, 0);
469 static void cache_entry_done(void *user_data)
471 struct pbap_session *pbap = user_data;
477 pbap->cache.valid = TRUE;
479 id = cache_find(&pbap->cache, pbap->find_handle);
481 DBG("Entry %d not found on cache", pbap->find_handle);
482 obex_object_set_io_flags(pbap->obj, G_IO_ERR, -ENOENT);
486 phonebook_req_finalize(pbap->obj->request);
487 pbap->obj->request = phonebook_get_entry(pbap->folder, id,
488 pbap->params, query_result, pbap, &ret);
490 obex_object_set_io_flags(pbap->obj, G_IO_ERR, ret);
493 #ifdef __TIZEN_PATCH__
494 static void cache_clear_notify(void *user_data)
496 struct pbap_session *pbap = user_data;
501 pbap->cache.valid = FALSE;
502 pbap->cache.index = 0;
503 cache_clear(&pbap->cache);
507 static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen)
509 GObexApparam *apparam;
510 struct apparam_field *param;
511 #ifdef __TIZEN_PATCH__
512 gboolean bmaxlistCount = FALSE;
513 #endif /* __TIZEN_PATCH__ */
515 apparam = g_obex_apparam_decode(buffer, hlen);
519 param = g_new0(struct apparam_field, 1);
522 * As per spec when client doesn't include MAXLISTCOUNT_TAG then it
523 * should be assume as Maximum value in vcardlisting 65535
525 param->maxlistcount = UINT16_MAX;
527 g_obex_apparam_get_uint8(apparam, ORDER_TAG, ¶m->order);
528 g_obex_apparam_get_uint8(apparam, SEARCHATTRIB_TAG,
529 ¶m->searchattrib);
530 g_obex_apparam_get_uint8(apparam, FORMAT_TAG, ¶m->format);
531 #ifdef __TIZEN_PATCH__
532 bmaxlistCount = g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG,
533 ¶m->maxlistcount);
535 g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG,
536 ¶m->maxlistcount);
537 #endif /* __TIZEN_PATCH__ */
538 g_obex_apparam_get_uint16(apparam, LISTSTARTOFFSET_TAG,
539 ¶m->liststartoffset);
540 g_obex_apparam_get_uint64(apparam, FILTER_TAG, ¶m->filter);
541 param->searchval = g_obex_apparam_get_string(apparam, SEARCHVALUE_TAG);
543 #ifdef __TIZEN_PATCH__
544 if(bmaxlistCount == FALSE) {
545 param->maxlistcount = PBAP_MAXLISTCOUNT_MAX_VALUE;
548 /* Keep the MaxlistCount value as send in request from client */
550 #endif /* __TIZEN_PATCH__ */
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);
557 g_obex_apparam_free(apparam);
562 static void *pbap_connect(struct obex_session *os, int *err)
564 struct pbap_session *pbap;
566 manager_register_session(os);
568 pbap = g_new0(struct pbap_session, 1);
569 pbap->folder = g_strdup("/");
570 pbap->find_handle = PHONEBOOK_INVALID_HANDLE;
572 #ifdef __TIZEN_PATCH__
575 error = phonebook_connect(&pbap->backend_data);
588 #ifdef __TIZEN_PATCH__
596 static int pbap_get(struct obex_session *os, void *user_data)
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;
607 DBG("name %s type %s pbap %p", name, type, pbap);
612 rsize = obex_get_apparam(os, &buffer);
614 if (g_ascii_strcasecmp(type, VCARDENTRY_TYPE) != 0)
620 params = parse_aparam(buffer, rsize);
625 g_free(pbap->params->searchval);
626 g_free(pbap->params);
629 pbap->params = params;
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);
636 path = g_build_filename("/", name, NULL);
638 } else if (g_ascii_strcasecmp(type, VCARDLISTING_TYPE) == 0) {
639 /* Always relative */
640 if (!name || strlen(name) == 0)
642 path = g_strdup(pbap->folder);
644 /* Current folder + relative path */
645 path = g_build_filename(pbap->folder, name, NULL);
647 } else if (g_ascii_strcasecmp(type, VCARDENTRY_TYPE) == 0) {
649 path = g_strdup(name);
656 ret = obex_get_stream_start(os, path);
663 static int pbap_setpath(struct obex_session *os, void *user_data)
665 struct pbap_session *pbap = user_data;
667 const uint8_t *nonhdr;
671 if (obex_get_non_header_data(os, &nonhdr) != 2) {
672 error("Set path failed: flag and constants not found!");
676 name = obex_get_name(os);
678 DBG("name %s folder %s nonhdr 0x%x%x", name, pbap->folder,
679 nonhdr[0], nonhdr[1]);
681 fullname = phonebook_set_folder(pbap->folder, name, nonhdr[0], &err);
685 g_free(pbap->folder);
686 pbap->folder = fullname;
689 * FIXME: Define a criteria to mark the cache as invalid
691 pbap->cache.valid = FALSE;
692 pbap->cache.index = 0;
693 cache_clear(&pbap->cache);
698 static void pbap_disconnect(struct obex_session *os, void *user_data)
700 struct pbap_session *pbap = user_data;
702 manager_unregister_session(os);
704 #ifdef __TIZEN_PATCH__
708 phonebook_disconnect(pbap->backend_data);
712 pbap->obj->session = NULL;
715 g_free(pbap->params->searchval);
716 g_free(pbap->params);
719 cache_clear(&pbap->cache);
720 g_free(pbap->folder);
724 static int pbap_chkput(struct obex_session *os, void *user_data)
726 /* Rejects all PUTs */
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,
737 .setpath = pbap_setpath,
738 .disconnect = pbap_disconnect,
739 .chkput = pbap_chkput
742 static struct pbap_object *vobject_create(struct pbap_session *pbap,
745 struct pbap_object *obj;
747 obj = g_new0(struct pbap_object, 1);
750 obj->request = request;
755 static void *vobject_pull_open(const char *name, int oflag, mode_t mode,
756 void *context, size_t *size, int *err)
758 struct pbap_session *pbap = context;
763 DBG("name %s context %p maxlistcount %d", name, context,
764 pbap->params->maxlistcount);
766 if (oflag != O_RDONLY) {
775 #ifdef __TIZEN_PATCH__
776 if (strcmp(name, "/telecom/mch.vcf") == 0)
777 pbap->params->required_missedcall_call_header = TRUE;
779 DBG("[%s] - required_missedcall_call_header [%d] ",
780 name,pbap->params->required_missedcall_call_header);
781 #endif /* __TIZEN_PATCH__ */
783 if (pbap->params->maxlistcount == 0)
784 cb = phonebook_size_result;
788 request = phonebook_pull(name, pbap->params, cb, pbap, &ret);
793 /* reading first part of results from backend */
794 ret = phonebook_pull_read(request);
801 return vobject_create(pbap, request);
810 static int vobject_close(void *object)
812 struct pbap_object *obj = object;
817 obj->session->obj = NULL;
820 g_string_free(obj->buffer, TRUE);
823 g_obex_apparam_free(obj->apparam);
826 phonebook_req_finalize(obj->request);
833 static void *vobject_list_open(const char *name, int oflag, mode_t mode,
834 void *context, size_t *size, int *err)
836 struct pbap_session *pbap = context;
837 struct pbap_object *obj = NULL;
846 DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
848 if (oflag != O_RDONLY) {
852 #ifdef __TIZEN_PATCH__
853 if (strcmp(name, "/telecom/mch") == 0)
854 pbap->params->required_missedcall_call_header = TRUE;
856 DBG("[%s] - required_missedcall_call_header [%d] ",
857 name,pbap->params->required_missedcall_call_header);
858 #endif /* __TIZEN_PATCH__ */
860 /* PullvCardListing always get the contacts from the cache */
862 if (pbap->cache.valid) {
863 obj = vobject_create(pbap, NULL);
864 ret = generate_response(pbap);
866 request = phonebook_create_cache(name, cache_entry_notify,
867 cache_ready_notify, pbap, &ret);
869 obj = vobject_create(pbap, request);
870 #ifdef __TIZEN_PATCH__
871 phonebook_set_cache_notification(pbap->backend_data,
872 cache_clear_notify, pbap);
893 static void *vobject_vcard_open(const char *name, int oflag, mode_t mode,
894 void *context, size_t *size, int *err)
896 struct pbap_session *pbap = context;
899 #ifdef __TIZEN_PATCH__
906 DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
908 if (oflag != O_RDONLY) {
913 if (name == NULL || sscanf(name, "%u.vcf", &handle) != 1) {
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);
925 request = phonebook_create_cache(pbap->folder,
926 cache_entry_notify, cache_entry_done, pbap, &ret);
928 #ifdef __TIZEN_PATCH__
929 phonebook_set_cache_notification(pbap->backend_data,
930 cache_clear_notify, pbap);
935 id = cache_find(&pbap->cache, handle);
941 request = phonebook_get_entry(pbap->folder, id, pbap->params,
942 query_result, pbap, &ret);
951 return vobject_create(pbap, request);
960 static ssize_t vobject_pull_get_next_header(void *object, void *buf, size_t mtu,
963 struct pbap_object *obj = object;
965 if (!obj->buffer && !obj->apparam)
968 *hi = G_OBEX_HDR_APPARAM;
970 if (obj->firstpacket) {
971 obj->firstpacket = FALSE;
973 return g_obex_apparam_encode(obj->apparam, buf, mtu);
979 static ssize_t vobject_pull_read(void *object, void *buf, size_t count)
981 struct pbap_object *obj = object;
982 struct pbap_session *pbap = obj->session;
985 DBG("buffer %p maxlistcount %d", obj->buffer,
986 pbap->params->maxlistcount);
989 if (pbap->params->maxlistcount == 0)
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);
1011 static ssize_t vobject_list_get_next_header(void *object, void *buf, size_t mtu,
1014 struct pbap_object *obj = object;
1015 struct pbap_session *pbap = obj->session;
1017 /* Backend still busy reading contacts */
1018 if (!pbap->cache.valid)
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);
1028 if (obj->apparam != NULL)
1029 return g_obex_apparam_encode(obj->apparam, buf, mtu);
1032 #endif /* __TIZEN_PATCH__ */
1035 static ssize_t vobject_list_read(void *object, void *buf, size_t count)
1037 struct pbap_object *obj = object;
1038 struct pbap_session *pbap = obj->session;
1040 DBG("valid %d maxlistcount %d", pbap->cache.valid,
1041 pbap->params->maxlistcount);
1043 if (pbap->params->maxlistcount == 0)
1046 return string_read(obj->buffer, buf, count);
1049 static ssize_t vobject_vcard_read(void *object, void *buf, size_t count)
1051 struct pbap_object *obj = object;
1053 DBG("buffer %p", obj->buffer);
1058 return string_read(obj->buffer, buf, count);
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,
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,
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,
1090 static int pbap_init(void)
1094 err = phonebook_init();
1098 err = obex_mime_type_driver_register(&mime_pull);
1100 goto fail_mime_pull;
1102 err = obex_mime_type_driver_register(&mime_list);
1104 goto fail_mime_list;
1106 err = obex_mime_type_driver_register(&mime_vcard);
1108 goto fail_mime_vcard;
1110 err = obex_service_driver_register(&pbap);
1117 obex_mime_type_driver_unregister(&mime_vcard);
1119 obex_mime_type_driver_unregister(&mime_list);
1121 obex_mime_type_driver_unregister(&mime_pull);
1128 static void pbap_exit(void)
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);
1137 OBEX_PLUGIN_DEFINE(pbap, pbap_init, pbap_exit)