tizen beta release
[profile/ivi/obexd.git] / plugins / phonebook-ebook.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 <string.h>
30 #include <errno.h>
31 #include <glib.h>
32 #include <bluetooth/bluetooth.h>
33
34 #include <libebook/e-book.h>
35
36 #include "log.h"
37 #include "obex.h"
38 #include "service.h"
39 #include "phonebook.h"
40 #include "glib-helper.h"
41
42 #define QUERY_FN "(contains \"family_name\" \"%s\")"
43 #define QUERY_NAME "(contains \"given_name\" \"%s\")"
44 #define QUERY_PHONE "(contains \"phone\" \"%s\")"
45
46 struct query_context {
47         const struct apparam_field *params;
48         phonebook_cb contacts_cb;
49         phonebook_entry_cb entry_cb;
50         phonebook_cache_ready_cb ready_cb;
51         EBookQuery *query;
52         unsigned int count;
53         GString *buf;
54         char *id;
55         unsigned queued_calls;
56         void *user_data;
57         GSList *ebooks;
58         gboolean canceled;
59 };
60
61 static char *attribute_mask[] = {
62 /* 0 */         "VERSION",
63                 "FN",
64                 "N",
65                 "PHOTO",
66                 "BDAY",
67                 "ADR",
68                 "LABEL",
69                 "TEL",
70 /* 8 */         "EMAIL",
71                 "MAILER",
72                 "TZ",
73                 "GEO",
74                 "TITLE",
75                 "ROLE",
76                 "LOGO",
77                 "AGENT",
78 /* 16 */        "ORG",
79                 "NOTE",
80                 "REV",
81                 "SOUND",
82                 "URL",
83                 "UID",
84                 "KEY",
85                 "NICKNAME",
86 /* 24 */        "CATEGORIES",
87                 "PROID",
88                 "CLASS",
89                 "SORT-STRING",
90 /* 28 */        "X-IRMC-CALL-DATETIME",
91                 NULL
92
93 };
94
95 static void close_ebooks(GSList *ebooks)
96 {
97         g_slist_free_full(ebooks, g_object_unref);
98 }
99
100 static void free_query_context(struct query_context *data)
101 {
102         g_free(data->id);
103
104         if (data->buf != NULL)
105                 g_string_free(data->buf, TRUE);
106
107         if (data->query != NULL)
108                 e_book_query_unref(data->query);
109
110         close_ebooks(data->ebooks);
111
112         g_free(data);
113 }
114
115 static char *evcard_to_string(EVCard *evcard, unsigned int format,
116                                                         uint64_t filter)
117 {
118         EVCard *evcard2;
119         GList *l;
120         char *vcard;
121
122         if (!filter)
123                 return e_vcard_to_string(evcard, EVC_FORMAT_VCARD_30);
124                 /* XXX There is no support for VCARD 2.1 at this time */
125
126         /*
127          * Mandatory attributes for vCard 2.1 are VERSION ,N and TEL.
128          * Mandatory attributes for vCard 3.0 are VERSION, N, FN and TEL
129          */
130         filter = format == EVC_FORMAT_VCARD_30 ? filter | 0x87: filter | 0x85;
131
132         l = e_vcard_get_attributes(evcard);
133         evcard2 = e_vcard_new();
134         for (; l; l = g_list_next(l)) {
135                 EVCardAttribute *attrib = l->data;
136                 const char *name;
137                 int i;
138
139                 if (!attrib)
140                         continue;
141
142                 name = e_vcard_attribute_get_name(attrib);
143
144                 for (i = 0; attribute_mask[i] != NULL; i++) {
145                         if (!(filter & (1 << i)))
146                                 continue;
147                         if (g_strcmp0(name, attribute_mask[i]) != 0)
148                                 continue;
149
150                         e_vcard_add_attribute(evcard2,
151                                         e_vcard_attribute_copy(attrib));
152                 }
153         }
154
155         vcard = e_vcard_to_string(evcard2, format);
156         g_object_unref(evcard2);
157
158         return vcard;
159 }
160
161 static void ebookpull_cb(EBook *book, const GError *gerr, GList *contacts,
162                                                         void *user_data)
163 {
164         struct query_context *data = user_data;
165         GList *l;
166         unsigned int count, maxcount;
167
168         data->queued_calls--;
169
170         if (data->canceled)
171                 goto canceled;
172
173         if (gerr != NULL) {
174                 error("E-Book query failed: %s", gerr->message);
175                 goto done;
176         }
177
178         DBG("");
179
180         /*
181          * When MaxListCount is zero, PCE wants to know the number of used
182          * indexes in the phonebook of interest. All other parameters that
183          * may be present in the request shall be ignored.
184          */
185         maxcount = data->params->maxlistcount;
186         if (maxcount == 0) {
187                 data->count += g_list_length(contacts);
188                 goto done;
189         }
190
191         l = g_list_nth(contacts, data->params->liststartoffset);
192
193         for (count = 0; l && count + data->count < maxcount; l = g_list_next(l),
194                                                                 count++) {
195                 EContact *contact = E_CONTACT(l->data);
196                 EVCard *evcard = E_VCARD(contact);
197                 char *vcard;
198
199                 vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30,
200                                                 data->params->filter);
201
202                 data->buf = g_string_append(data->buf, vcard);
203                 data->buf = g_string_append(data->buf, "\r\n");
204                 g_free(vcard);
205         }
206
207         DBG("collected %d vcards", count);
208
209         data->count += count;
210
211         g_list_free_full(contacts, g_object_unref);
212
213 done:
214         if (data->queued_calls == 0) {
215                 GString *buf = data->buf;
216                 data->buf = NULL;
217
218                 data->contacts_cb(buf->str, buf->len, data->count,
219                                                 0, TRUE, data->user_data);
220
221                 g_string_free(buf, TRUE);
222
223         }
224
225         return;
226
227 canceled:
228         if (data->queued_calls == 0)
229                 free_query_context(data);
230 }
231
232 static void ebook_entry_cb(EBook *book, const GError *gerr,
233                                 EContact *contact, void *user_data)
234 {
235         struct query_context *data = user_data;
236         EVCard *evcard;
237         char *vcard;
238         size_t len;
239
240         data->queued_calls--;
241
242         if (data->canceled)
243                 goto done;
244
245         if (gerr != NULL) {
246                 error("E-Book query failed: %s", gerr->message);
247                 goto done;
248         }
249
250         DBG("");
251
252         evcard = E_VCARD(contact);
253
254         vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30,
255                                         data->params->filter);
256
257         len = vcard ? strlen(vcard) : 0;
258
259         data->count++;
260         data->contacts_cb(vcard, len, 1, 0, TRUE, data->user_data);
261
262         g_free(vcard);
263         g_object_unref(contact);
264
265         return;
266
267 done:
268         if (data->queued_calls == 0) {
269                 if (data->count == 0)
270                         data->contacts_cb(NULL, 0, 1, 0, TRUE,
271                                                 data->user_data);
272                 else if (data->canceled)
273                         free_query_context(data);
274         }
275 }
276
277 static char *evcard_name_attribute_to_string(EVCard *evcard)
278 {
279         EVCardAttribute *attrib;
280         GList *l;
281         GString *name = NULL;
282
283         attrib = e_vcard_get_attribute(evcard, EVC_N);
284         if (!attrib)
285                 return NULL;
286
287         for (l = e_vcard_attribute_get_values(attrib); l; l = l->next) {
288                 const char *value = l->data;
289
290                 if (!strlen(value))
291                         continue;
292
293                 if (!name)
294                         name = g_string_new(value);
295                 else {
296                         name = g_string_append(name, ";");
297                         name = g_string_append(name, l->data);
298                 }
299         }
300
301         if (!name)
302                 return NULL;
303
304         return g_string_free(name, FALSE);
305 }
306
307 static void cache_cb(EBook *book, const GError *gerr, GList *contacts,
308                                                         void *user_data)
309 {
310         struct query_context *data = user_data;
311         GList *l;
312
313         data->queued_calls--;
314
315         if (data->canceled)
316                 goto canceled;
317
318         if (gerr != NULL) {
319                 error("E-Book operation failed: %s", gerr->message);
320                 goto done;
321         }
322
323         DBG("");
324
325         for (l = contacts; l; l = g_list_next(l)) {
326                 EContact *contact = E_CONTACT(l->data);
327                 EVCard *evcard = E_VCARD(contact);
328                 EVCardAttribute *attrib;
329                 char *uid, *tel, *name;
330
331                 name = evcard_name_attribute_to_string(evcard);
332                 if (!name)
333                         continue;
334
335                 attrib = e_vcard_get_attribute(evcard, EVC_UID);
336                 if (!attrib)
337                         continue;
338
339                 uid = e_vcard_attribute_get_value(attrib);
340                 if (!uid)
341                         continue;
342
343                 attrib = e_vcard_get_attribute(evcard, EVC_TEL);
344                 if (attrib)
345                         tel = e_vcard_attribute_get_value(attrib);
346                 else
347                         tel = g_strdup("");
348
349                 data->entry_cb(uid, PHONEBOOK_INVALID_HANDLE, name, NULL,
350                                                         tel, data->user_data);
351
352                 g_free(name);
353                 g_free(uid);
354                 g_free(tel);
355         }
356
357         g_list_free_full(contacts, g_object_unref);
358
359 done:
360         if (data->queued_calls == 0)
361                 data->ready_cb(data->user_data);
362
363         return;
364
365 canceled:
366         if (data->queued_calls == 0)
367                 free_query_context(data);
368 }
369
370 static GSList *traverse_sources(GSList *ebooks, GSList *sources,
371                                                         char **default_src) {
372         GError *gerr = NULL;
373
374         for (; sources != NULL; sources = g_slist_next(sources)) {
375                 char *uri;
376                 ESource *source = E_SOURCE(sources->data);
377                 EBook *ebook = e_book_new(source, &gerr);
378
379                 if (ebook == NULL) {
380                         error("Can't create user's address book: %s",
381                                                                 gerr->message);
382                         g_clear_error(&gerr);
383                         continue;
384                 }
385
386                 uri = e_source_get_uri(source);
387                 if (g_strcmp0(*default_src, uri) == 0) {
388                         g_free(uri);
389                         continue;
390                 }
391                 g_free(uri);
392
393                 if (e_book_open(ebook, FALSE, &gerr) == FALSE) {
394                         error("Can't open e-book address book: %s",
395                                                         gerr->message);
396                         g_object_unref(ebook);
397                         g_clear_error(&gerr);
398                         continue;
399                 }
400
401                 if (*default_src == NULL)
402                         *default_src = e_source_get_uri(source);
403
404                 DBG("%s address book opened", e_source_peek_name(source));
405
406                 ebooks = g_slist_append(ebooks, ebook);
407         }
408
409         return ebooks;
410 }
411
412 int phonebook_init(void)
413 {
414         g_type_init();
415
416         return 0;
417 }
418
419 static GSList *open_ebooks(void)
420 {
421         GError *gerr = NULL;
422         ESourceList *src_list;
423         GSList *list;
424         gchar *default_src = NULL;
425         GSList *ebooks = NULL;
426
427         if (e_book_get_addressbooks(&src_list, &gerr) == FALSE) {
428                 error("Can't list user's address books: %s", gerr->message);
429                 g_error_free(gerr);
430                 return NULL;
431         }
432
433         list = e_source_list_peek_groups(src_list);
434         while (list != NULL) {
435                 ESourceGroup *group = E_SOURCE_GROUP(list->data);
436                 GSList *sources = e_source_group_peek_sources(group);
437
438                 ebooks = traverse_sources(ebooks, sources, &default_src);
439
440                 list = list->next;
441         }
442
443         g_free(default_src);
444         g_object_unref(src_list);
445
446         return ebooks;
447 }
448
449 void phonebook_exit(void)
450 {
451 }
452
453 char *phonebook_set_folder(const char *current_folder,
454                 const char *new_folder, uint8_t flags, int *err)
455 {
456         gboolean root, child;
457         char *fullname = NULL, *tmp1, *tmp2, *base;
458         int ret = 0, len;
459
460         root = (g_strcmp0("/", current_folder) == 0);
461         child = (new_folder && strlen(new_folder) != 0);
462
463         /* Evolution back-end will support telecom/pb folder only */
464
465         switch (flags) {
466         case 0x02:
467                 /* Go back to root */
468                 if (!child) {
469                         fullname = g_strdup("/");
470                         goto done;
471                 }
472
473                 /* Go down 1 level */
474                 fullname = g_build_filename(current_folder, new_folder, NULL);
475                 if (strcmp("/telecom", fullname) != 0 &&
476                                 strcmp("/telecom/pb", fullname) != 0) {
477                         g_free(fullname);
478                         fullname = NULL;
479                         ret = -ENOENT;
480                 }
481
482                 break;
483         case 0x03:
484                 /* Go up 1 level */
485                 if (root) {
486                         /* Already root */
487                         ret = -EBADR;
488                         goto done;
489                 }
490
491                 /*
492                  * Removing one level of the current folder. Current folder
493                  * contains AT LEAST one level since it is not at root folder.
494                  * Use glib utility functions to handle invalid chars in the
495                  * folder path properly.
496                  */
497                 tmp1 = g_path_get_basename(current_folder);
498                 tmp2 = g_strrstr(current_folder, tmp1);
499                 len = tmp2 - (current_folder + 1);
500
501                 g_free(tmp1);
502
503                 if (len == 0)
504                         base = g_strdup("/");
505                 else
506                         base = g_strndup(current_folder, len);
507
508                 /* Return one level only */
509                 if (!child) {
510                         fullname = base;
511                         goto done;
512                 }
513
514                 fullname = g_build_filename(base, new_folder, NULL);
515                 if (strcmp(fullname, "/telecom") != 0 &&
516                                 strcmp(fullname, "/telecom/pb") != 0) {
517                         g_free(fullname);
518                         fullname = NULL;
519                         ret = -ENOENT;
520                 }
521
522                 g_free(base);
523
524                 break;
525         default:
526                 ret = -EBADR;
527                 break;
528         }
529
530 done:
531         if (err)
532                 *err = ret;
533
534         return fullname;
535 }
536
537 void phonebook_req_finalize(void *request)
538 {
539         struct query_context *data = request;
540
541         if (data->queued_calls == 0)
542                 free_query_context(data);
543         else
544                 data->canceled = TRUE;
545 }
546
547 void *phonebook_pull(const char *name, const struct apparam_field *params,
548                                 phonebook_cb cb, void *user_data, int *err)
549 {
550         struct query_context *data;
551
552         if (g_strcmp0("/telecom/pb.vcf", name) != 0) {
553                 if (err)
554                         *err = -ENOENT;
555
556                 return NULL;
557         }
558
559         data = g_new0(struct query_context, 1);
560         data->contacts_cb = cb;
561         data->params = params;
562         data->user_data = user_data;
563         data->buf = g_string_new("");
564         data->query = e_book_query_any_field_contains("");
565         data->ebooks = open_ebooks();
566
567         if (err)
568                 *err = data->ebooks == NULL ? -EIO : 0;
569
570         return data;
571 }
572
573 int phonebook_pull_read(void *request)
574 {
575         struct query_context *data = request;
576         GSList *l;
577
578         if (!data)
579                 return -ENOENT;
580
581         for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
582                 EBook *ebook = l->data;
583
584                 if (e_book_is_opened(ebook) == FALSE)
585                         continue;
586
587                 if (e_book_get_contacts_async(ebook, data->query,
588                                                 ebookpull_cb, data) == TRUE)
589                         data->queued_calls++;
590         }
591
592         if (data->queued_calls == 0)
593                 return -ENOENT;
594
595         return 0;
596 }
597
598 void *phonebook_get_entry(const char *folder, const char *id,
599                                 const struct apparam_field *params,
600                                 phonebook_cb cb, void *user_data, int *err)
601 {
602         struct query_context *data;
603         GSList *l;
604
605         data = g_new0(struct query_context, 1);
606         data->contacts_cb = cb;
607         data->params = params;
608         data->user_data = user_data;
609         data->id = g_strdup(id);
610         data->ebooks = open_ebooks();
611
612         for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
613                 EBook *ebook = l->data;
614
615                 if (e_book_is_opened(ebook) == FALSE)
616                         continue;
617
618                 if (e_book_get_contact_async(ebook, data->id,
619                                                 ebook_entry_cb, data) == TRUE)
620                         data->queued_calls++;
621         }
622
623         if (err)
624                 *err = (data->queued_calls == 0 ? -ENOENT : 0);
625
626         return data;
627 }
628
629 void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
630                 phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
631 {
632         struct query_context *data;
633         EBookQuery *query;
634         GSList *l;
635         EContact *me;
636         EVCard *evcard;
637         GError *gerr = NULL;
638         EBook *eb;
639         EVCardAttribute *attrib;
640         char *uid, *tel, *cname;
641
642         if (g_strcmp0("/telecom/pb", name) != 0) {
643                 if (err)
644                         *err = -ENOENT;
645
646                 return NULL;
647         }
648
649         DBG("");
650
651         query = e_book_query_any_field_contains("");
652
653         data = g_new0(struct query_context, 1);
654         data->entry_cb = entry_cb;
655         data->ready_cb = ready_cb;
656         data->user_data = user_data;
657         data->query = query;
658         data->ebooks = open_ebooks();
659
660         /* Add 0.vcf */
661         if (e_book_get_self(&me, &eb, &gerr) == FALSE) {
662                 g_error_free(gerr);
663                 goto next;
664         }
665
666         evcard = E_VCARD(me);
667
668         cname = evcard_name_attribute_to_string(evcard);
669         if (!cname)
670                 cname = g_strdup("");
671
672         attrib = e_vcard_get_attribute(evcard, EVC_UID);
673         uid = e_vcard_attribute_get_value(attrib);
674         if (!uid)
675                 uid = g_strdup("");
676
677         attrib = e_vcard_get_attribute(evcard, EVC_TEL);
678         if (attrib)
679                 tel =  e_vcard_attribute_get_value(attrib);
680         else
681                 tel = g_strdup("");
682
683         data->entry_cb(uid, 0, cname, NULL, tel, data->user_data);
684
685         data->count++;
686
687         g_free(cname);
688         g_free(uid);
689         g_free(tel);
690         g_object_unref(eb);
691
692 next:
693         for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
694                 EBook *ebook = l->data;
695
696                 if (e_book_is_opened(ebook) == FALSE)
697                         continue;
698
699                 if (e_book_get_contacts_async(ebook, query,
700                                                 cache_cb, data) == TRUE)
701                         data->queued_calls++;
702         }
703
704         if (err)
705                 *err = (data->queued_calls == 0 ? -ENOENT : 0);
706
707         return data;
708 }