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
32 #include <bluetooth/bluetooth.h>
34 #include <libebook/e-book.h>
39 #include "phonebook.h"
40 #include "glib-helper.h"
42 #define QUERY_FN "(contains \"family_name\" \"%s\")"
43 #define QUERY_NAME "(contains \"given_name\" \"%s\")"
44 #define QUERY_PHONE "(contains \"phone\" \"%s\")"
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;
55 unsigned queued_calls;
61 static char *attribute_mask[] = {
86 /* 24 */ "CATEGORIES",
90 /* 28 */ "X-IRMC-CALL-DATETIME",
95 static void close_ebooks(GSList *ebooks)
97 g_slist_free_full(ebooks, g_object_unref);
100 static void free_query_context(struct query_context *data)
104 if (data->buf != NULL)
105 g_string_free(data->buf, TRUE);
107 if (data->query != NULL)
108 e_book_query_unref(data->query);
110 close_ebooks(data->ebooks);
115 static char *evcard_to_string(EVCard *evcard, unsigned int format,
123 return e_vcard_to_string(evcard, EVC_FORMAT_VCARD_30);
124 /* XXX There is no support for VCARD 2.1 at this time */
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
130 filter = format == EVC_FORMAT_VCARD_30 ? filter | 0x87: filter | 0x85;
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;
142 name = e_vcard_attribute_get_name(attrib);
144 for (i = 0; attribute_mask[i] != NULL; i++) {
145 if (!(filter & (1 << i)))
147 if (g_strcmp0(name, attribute_mask[i]) != 0)
150 e_vcard_add_attribute(evcard2,
151 e_vcard_attribute_copy(attrib));
155 vcard = e_vcard_to_string(evcard2, format);
156 g_object_unref(evcard2);
161 static void ebookpull_cb(EBook *book, const GError *gerr, GList *contacts,
164 struct query_context *data = user_data;
166 unsigned int count, maxcount;
168 data->queued_calls--;
174 error("E-Book query failed: %s", gerr->message);
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.
185 maxcount = data->params->maxlistcount;
187 data->count += g_list_length(contacts);
191 l = g_list_nth(contacts, data->params->liststartoffset);
193 for (count = 0; l && count + data->count < maxcount; l = g_list_next(l),
195 EContact *contact = E_CONTACT(l->data);
196 EVCard *evcard = E_VCARD(contact);
199 vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30,
200 data->params->filter);
202 data->buf = g_string_append(data->buf, vcard);
203 data->buf = g_string_append(data->buf, "\r\n");
207 DBG("collected %d vcards", count);
209 data->count += count;
211 g_list_free_full(contacts, g_object_unref);
214 if (data->queued_calls == 0) {
215 GString *buf = data->buf;
218 data->contacts_cb(buf->str, buf->len, data->count,
219 0, TRUE, data->user_data);
221 g_string_free(buf, TRUE);
228 if (data->queued_calls == 0)
229 free_query_context(data);
232 static void ebook_entry_cb(EBook *book, const GError *gerr,
233 EContact *contact, void *user_data)
235 struct query_context *data = user_data;
240 data->queued_calls--;
246 error("E-Book query failed: %s", gerr->message);
252 evcard = E_VCARD(contact);
254 vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30,
255 data->params->filter);
257 len = vcard ? strlen(vcard) : 0;
260 data->contacts_cb(vcard, len, 1, 0, TRUE, data->user_data);
263 g_object_unref(contact);
268 if (data->queued_calls == 0) {
269 if (data->count == 0)
270 data->contacts_cb(NULL, 0, 1, 0, TRUE,
272 else if (data->canceled)
273 free_query_context(data);
277 static char *evcard_name_attribute_to_string(EVCard *evcard)
279 EVCardAttribute *attrib;
281 GString *name = NULL;
283 attrib = e_vcard_get_attribute(evcard, EVC_N);
287 for (l = e_vcard_attribute_get_values(attrib); l; l = l->next) {
288 const char *value = l->data;
294 name = g_string_new(value);
296 name = g_string_append(name, ";");
297 name = g_string_append(name, l->data);
304 return g_string_free(name, FALSE);
307 static void cache_cb(EBook *book, const GError *gerr, GList *contacts,
310 struct query_context *data = user_data;
313 data->queued_calls--;
319 error("E-Book operation failed: %s", gerr->message);
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;
331 name = evcard_name_attribute_to_string(evcard);
335 attrib = e_vcard_get_attribute(evcard, EVC_UID);
339 uid = e_vcard_attribute_get_value(attrib);
343 attrib = e_vcard_get_attribute(evcard, EVC_TEL);
345 tel = e_vcard_attribute_get_value(attrib);
349 data->entry_cb(uid, PHONEBOOK_INVALID_HANDLE, name, NULL,
350 tel, data->user_data);
357 g_list_free_full(contacts, g_object_unref);
360 if (data->queued_calls == 0)
361 data->ready_cb(data->user_data);
366 if (data->queued_calls == 0)
367 free_query_context(data);
370 static GSList *traverse_sources(GSList *ebooks, GSList *sources,
371 char **default_src) {
374 for (; sources != NULL; sources = g_slist_next(sources)) {
376 ESource *source = E_SOURCE(sources->data);
377 EBook *ebook = e_book_new(source, &gerr);
380 error("Can't create user's address book: %s",
382 g_clear_error(&gerr);
386 uri = e_source_get_uri(source);
387 if (g_strcmp0(*default_src, uri) == 0) {
393 if (e_book_open(ebook, FALSE, &gerr) == FALSE) {
394 error("Can't open e-book address book: %s",
396 g_object_unref(ebook);
397 g_clear_error(&gerr);
401 if (*default_src == NULL)
402 *default_src = e_source_get_uri(source);
404 DBG("%s address book opened", e_source_peek_name(source));
406 ebooks = g_slist_append(ebooks, ebook);
412 int phonebook_init(void)
419 static GSList *open_ebooks(void)
422 ESourceList *src_list;
424 gchar *default_src = NULL;
425 GSList *ebooks = NULL;
427 if (e_book_get_addressbooks(&src_list, &gerr) == FALSE) {
428 error("Can't list user's address books: %s", gerr->message);
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);
438 ebooks = traverse_sources(ebooks, sources, &default_src);
444 g_object_unref(src_list);
449 void phonebook_exit(void)
453 char *phonebook_set_folder(const char *current_folder,
454 const char *new_folder, uint8_t flags, int *err)
456 gboolean root, child;
457 char *fullname = NULL, *tmp1, *tmp2, *base;
460 root = (g_strcmp0("/", current_folder) == 0);
461 child = (new_folder && strlen(new_folder) != 0);
463 /* Evolution back-end will support telecom/pb folder only */
467 /* Go back to root */
469 fullname = g_strdup("/");
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) {
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.
497 tmp1 = g_path_get_basename(current_folder);
498 tmp2 = g_strrstr(current_folder, tmp1);
499 len = tmp2 - (current_folder + 1);
504 base = g_strdup("/");
506 base = g_strndup(current_folder, len);
508 /* Return one level only */
514 fullname = g_build_filename(base, new_folder, NULL);
515 if (strcmp(fullname, "/telecom") != 0 &&
516 strcmp(fullname, "/telecom/pb") != 0) {
537 void phonebook_req_finalize(void *request)
539 struct query_context *data = request;
541 if (data->queued_calls == 0)
542 free_query_context(data);
544 data->canceled = TRUE;
547 void *phonebook_pull(const char *name, const struct apparam_field *params,
548 phonebook_cb cb, void *user_data, int *err)
550 struct query_context *data;
552 if (g_strcmp0("/telecom/pb.vcf", name) != 0) {
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();
568 *err = data->ebooks == NULL ? -EIO : 0;
573 int phonebook_pull_read(void *request)
575 struct query_context *data = request;
581 for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
582 EBook *ebook = l->data;
584 if (e_book_is_opened(ebook) == FALSE)
587 if (e_book_get_contacts_async(ebook, data->query,
588 ebookpull_cb, data) == TRUE)
589 data->queued_calls++;
592 if (data->queued_calls == 0)
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)
602 struct query_context *data;
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();
612 for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
613 EBook *ebook = l->data;
615 if (e_book_is_opened(ebook) == FALSE)
618 if (e_book_get_contact_async(ebook, data->id,
619 ebook_entry_cb, data) == TRUE)
620 data->queued_calls++;
624 *err = (data->queued_calls == 0 ? -ENOENT : 0);
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)
632 struct query_context *data;
639 EVCardAttribute *attrib;
640 char *uid, *tel, *cname;
642 if (g_strcmp0("/telecom/pb", name) != 0) {
651 query = e_book_query_any_field_contains("");
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;
658 data->ebooks = open_ebooks();
661 if (e_book_get_self(&me, &eb, &gerr) == FALSE) {
666 evcard = E_VCARD(me);
668 cname = evcard_name_attribute_to_string(evcard);
670 cname = g_strdup("");
672 attrib = e_vcard_get_attribute(evcard, EVC_UID);
673 uid = e_vcard_attribute_get_value(attrib);
677 attrib = e_vcard_get_attribute(evcard, EVC_TEL);
679 tel = e_vcard_attribute_get_value(attrib);
683 data->entry_cb(uid, 0, cname, NULL, tel, data->user_data);
693 for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
694 EBook *ebook = l->data;
696 if (e_book_is_opened(ebook) == FALSE)
699 if (e_book_get_contacts_async(ebook, query,
700 cache_cb, data) == TRUE)
701 data->queued_calls++;
705 *err = (data->queued_calls == 0 ? -ENOENT : 0);