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