tizen 2.3.1 release
[framework/connectivity/bluez.git] / obexd / plugins / phonebook-tizen.c
1 /*
2  * OBEX Server
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact:  Hocheol Seo <hocheol.seo@samsung.com>
7  *               Girishashok Joshi <girish.joshi@samsung.com>
8  *               Chanyeol Park <chanyeol.park@samsung.com>
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *              http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  */
23
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <dirent.h>
30 #include <errno.h>
31 #include <stdio.h>
32 #include <stdint.h>
33 #include <string.h>
34 #include <stdarg.h>
35 #include <glib.h>
36 #include <stdlib.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41
42 #include "log.h"
43 #include "phonebook.h"
44
45 #include <dbus/dbus.h>
46 #include <gdbus.h>
47
48 #define PHONEBOOK_DESTINATION           "org.bluez.pb_agent"
49 #define PHONEBOOK_PATH                  "/org/bluez/pb_agent"
50 #define PHONEBOOK_INTERFACE             "org.bluez.PbAgent"
51
52 #define QUERY_GET_PHONEBOOK_FOLDER_LIST "GetPhonebookFolderList"
53 #define QUERY_GET_PHONEBOOK_SIZE        "GetPhonebookSize"
54 #define QUERY_GET_PHONEBOOK             "GetPhonebook"
55 #define QUERY_GET_PHONEBOOK_LIST        "GetPhonebookList"
56 #define QUERY_GET_PHONEBOOK_ENTRY       "GetPhonebookEntry"
57 #define QUERY_DESTROY_AGENT "DestroyAgent"
58
59 static GPtrArray *folder_list = NULL;
60
61 struct phonebook_data {
62         phonebook_cb cb;
63         DBusPendingCallNotifyFunction reply_cb;
64         DBusPendingCall *call;
65         void *user_data;
66         const struct apparam_field *params;
67
68         phonebook_entry_cb entry_cb;
69         phonebook_cache_ready_cb ready_cb;
70
71         char *req_name;
72 };
73
74 struct phonebook_session {
75         DBusConnection *connection;
76         phonebook_cache_clear_cb clear_cb;
77         unsigned int clear_id;
78
79         void *user_data;
80 };
81
82 static gboolean folder_is_valid(const gchar *folder,
83                                 gboolean leaf_only)
84 {
85         int i;
86         int folder_len;
87
88         if (folder_list == NULL || folder == NULL)
89                 return FALSE;
90
91         folder_len = strlen(folder);
92
93         for (i = 0 ; i < folder_list->len; i++) {
94                 char *str = (char *)g_ptr_array_index(folder_list, i);
95
96                 if (folder_len > strlen(str))
97                         continue;
98
99                 if (g_strcmp0(str, folder) == 0)
100                         return TRUE;
101
102                 if (leaf_only == TRUE)
103                         continue;
104
105                 if (strncmp(str, folder,  folder_len) == 0) {
106
107                         if (*(folder + folder_len - 1) == '/')
108                                 return TRUE;    /* folder is ended with '/' */
109
110                         if (*(str + folder_len) == '/')
111                                 return TRUE;    /* folder is matched */
112                 }
113         }
114
115         return FALSE;
116 }
117
118 static DBusPendingCall* phonebook_request(struct phonebook_data *data,
119                                 const gchar *method,
120                                 DBusPendingCallNotifyFunction function,
121                                 int first_arg_type,
122                                 ...)
123 {
124         DBusConnection *connection;
125         DBusPendingCall *call;
126         DBusMessage *message;
127
128         va_list args;
129
130         DBG("%s\n", method);
131
132         if (!method) {
133                 error("Can't get method name");
134                 return NULL;
135         }
136
137         connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
138         if (!connection) {
139                 error("Can't get on session bus");
140                 return NULL;
141         }
142
143         message = dbus_message_new_method_call(PHONEBOOK_DESTINATION,
144                         PHONEBOOK_PATH,
145                         PHONEBOOK_INTERFACE,
146                         method);
147         if (!message) {
148                 dbus_connection_unref(connection);
149                 error("Can't Allocate new message");
150                 return NULL;
151         }
152
153         va_start(args, first_arg_type);
154         dbus_message_append_args_valist(message, first_arg_type, args);
155         va_end(args);
156
157         if (dbus_connection_send_with_reply(connection, message, &call, -1) == FALSE) {
158                 dbus_message_unref(message);
159                 dbus_connection_unref(connection);
160                 DBG("-");
161                 return NULL;
162         }
163         dbus_pending_call_set_notify(call, function, data, NULL);
164
165         dbus_message_unref(message);
166         dbus_connection_unref(connection);
167
168         DBG("-");
169         return call;
170 }
171
172 static void get_phonebook_size_reply(DBusPendingCall *call, void *user_data)
173 {
174         DBusMessage *reply = dbus_pending_call_steal_reply(call);
175         struct phonebook_data *s_data = user_data;
176         DBusError derr;
177         uint32_t phonebook_size;
178
179         unsigned int new_missed_call = 0;
180
181         DBG("");
182         dbus_pending_call_unref(s_data->call);
183         s_data->call = NULL;
184
185         if (!reply) {
186                 DBG("Reply Error\n");
187                 return;
188         }
189
190         dbus_error_init(&derr);
191         if (dbus_set_error_from_message(&derr, reply)) {
192                 error("Replied with an error: %s, %s", derr.name, derr.message);
193                 dbus_error_free(&derr);
194                 phonebook_size = 0;
195         } else {
196                 dbus_message_get_args(reply, NULL,
197                                 DBUS_TYPE_UINT32, &phonebook_size,
198                                 DBUS_TYPE_UINT32, &new_missed_call,
199                                 DBUS_TYPE_INVALID);
200                 DBG("phonebooksize:%d\n", phonebook_size);
201         }
202
203         s_data->cb(NULL, 0, phonebook_size, new_missed_call, TRUE, s_data->user_data);
204
205         dbus_message_unref(reply);
206 }
207
208 static void get_phonebook_reply(DBusPendingCall *call, void *user_data)
209 {
210         DBusMessage *reply = dbus_pending_call_steal_reply(call);
211         struct phonebook_data *s_data = user_data;
212         DBusError derr;
213         GString *buffer;
214
215         unsigned int new_missed_call = 0;
216
217         uint32_t count = 0;
218
219         DBG("");
220         dbus_pending_call_unref(s_data->call);
221         s_data->call = NULL;
222
223         if (!reply) {
224                 DBG("Reply Error\n");
225                 return;
226         }
227
228         buffer = g_string_new("");
229
230         dbus_error_init(&derr);
231         if (dbus_set_error_from_message(&derr, reply)) {
232                 error("Replied with an error: %s, %s", derr.name, derr.message);
233                 dbus_error_free(&derr);
234         } else {
235                 DBusMessageIter iter;
236
237                 dbus_message_iter_init(reply, &iter);
238
239                 if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
240                         DBusMessageIter recurse_iter;
241
242                         dbus_message_iter_recurse(&iter, &recurse_iter);
243                         while (dbus_message_iter_get_arg_type(&recurse_iter) == DBUS_TYPE_STRING) {
244                                 gchar *str;
245
246                                 dbus_message_iter_get_basic(&recurse_iter, &str);
247                                 dbus_message_iter_next(&recurse_iter);
248
249                                 g_string_append(buffer, str);
250
251                                 DBG("str:\n%s\n", str);
252
253                                 count++;
254                         }
255                         dbus_message_iter_next(&iter);
256                 }
257
258                 if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32)
259                         dbus_message_iter_get_basic(&iter, &new_missed_call);
260
261                 DBG("new_missed_call %d\n", new_missed_call);
262         }
263
264         s_data->cb(buffer->str, buffer->len, count, new_missed_call, 1, s_data->user_data);
265
266         g_string_free(buffer, TRUE);
267         dbus_message_unref(reply);
268 }
269
270
271 static void get_phonebook_list_reply(DBusPendingCall *call, void *user_data)
272 {
273         DBusMessage *reply = dbus_pending_call_steal_reply(call);
274         DBusMessageIter iter, iter_struct, entry;
275         struct phonebook_data *data = user_data;
276         DBusError derr;
277         uint32_t handle = 0;
278         const char *name = NULL;
279         const char *tel = NULL;
280         char id[10];
281         unsigned int new_missed_call = 0;
282
283         dbus_pending_call_unref(data->call);
284         data->call = NULL;
285         if (!reply) {
286                 DBG("Reply Error\n");
287                 return;
288         }
289
290         dbus_error_init(&derr);
291         if (dbus_set_error_from_message(&derr, reply)) {
292                 error("Replied with an error: %s, %s", derr.name, derr.message);
293                 dbus_error_free(&derr);
294         } else {
295
296                 dbus_message_iter_init(reply, &iter);
297
298                 dbus_message_iter_recurse(&iter, &iter_struct);
299
300                 while (dbus_message_iter_get_arg_type(&iter_struct) ==
301                                                         DBUS_TYPE_STRUCT) {
302                         dbus_message_iter_recurse(&iter_struct, &entry);
303
304                         dbus_message_iter_get_basic(&entry, &name);
305                         dbus_message_iter_next(&entry);
306                         dbus_message_iter_get_basic(&entry, &tel);
307                         dbus_message_iter_next(&entry);
308                         dbus_message_iter_get_basic(&entry, &handle);
309
310                         /*
311                         DBG("[handle:%d name:%s tel:%s]\n", handle, name, tel);
312                         */
313
314                         snprintf(id, sizeof(id), "%d.vcf", handle);
315
316                         data->entry_cb(id, handle, name, NULL, tel,
317                                         data->user_data);
318
319                         dbus_message_iter_next(&iter_struct);
320                 }
321
322                 dbus_message_iter_next(&iter);
323                 if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) {
324                         dbus_message_iter_get_basic(&iter, &new_missed_call);
325                 }
326
327                 DBG("new_missed_call %d\n", new_missed_call);
328         }
329 #ifdef __TIZEN_PATCH__
330         data->ready_cb(data->user_data, new_missed_call);
331 #else
332         data->ready_cb(data->user_data);
333 #endif /* __TIZEN_PATCH__ */
334
335         dbus_message_unref(reply);
336 }
337
338 static void get_phonebook_entry_reply(DBusPendingCall *call, void *user_data)
339 {
340         DBusMessage *reply = dbus_pending_call_steal_reply(call);
341         struct phonebook_data *s_data = user_data;
342         DBusError derr;
343         const char *phonebook_entry;
344
345         DBG("");
346         dbus_pending_call_unref(s_data->call);
347         s_data->call = NULL;
348
349         if (!reply) {
350                 DBG("Reply Error\n");
351                 return;
352         }
353
354         dbus_error_init(&derr);
355         if (dbus_set_error_from_message(&derr, reply)) {
356                 error("Replied with an error: %s, %s", derr.name, derr.message);
357                 dbus_error_free(&derr);
358         } else {
359                 dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING,
360                                         &phonebook_entry, DBUS_TYPE_INVALID);
361                 DBG("phonebook_entry:[%s]\n", phonebook_entry);
362         }
363
364         s_data->cb(phonebook_entry, strlen(phonebook_entry), 1, 0, TRUE,
365                                                         s_data->user_data);
366
367         dbus_message_unref(reply);
368 }
369
370 static gboolean clear_signal(DBusConnection *conn, DBusMessage *msg,
371                         void *user_data)
372 {
373         struct phonebook_session *session;
374
375         if (user_data == NULL)
376                 return FALSE;
377
378         DBG("");
379         session = user_data;
380
381         session->clear_cb(session->user_data);
382
383         g_dbus_remove_watch(session->connection, session->clear_id);
384         session->clear_id = 0;
385
386         return TRUE;
387 }
388
389 static gboolean phonebook_folder_list_init(struct phonebook_session *session)
390 {
391         DBusMessage *message;
392         DBusMessage *reply;
393
394         DBusMessageIter iter;
395         DBusMessageIter recurse_iter;
396
397         DBusError error;
398
399         if (session->connection == NULL) {
400                 session->connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM,
401                                                         NULL, NULL);
402         }
403
404         if (session->connection == NULL) {
405                 DBG("Can't get on s bus");
406                 return FALSE;
407         }
408
409         message = dbus_message_new_method_call(PHONEBOOK_DESTINATION,
410                                         PHONEBOOK_PATH,
411                                         PHONEBOOK_INTERFACE,
412                                         QUERY_GET_PHONEBOOK_FOLDER_LIST);
413
414         if (message == NULL) {
415                 DBG("Can't allocate dbus message");
416                 return FALSE;
417         }
418
419         dbus_error_init(&error);
420
421         reply = dbus_connection_send_with_reply_and_block(session->connection,
422                                                         message, -1, &error);
423
424         if (!reply) {
425                 if (dbus_error_is_set(&error) == TRUE) {
426                         DBG("%s", error.message);
427                         dbus_error_free(&error);
428                 } else {
429                         DBG("Failed to get contacts");
430                 }
431                 dbus_message_unref(message);
432                 return FALSE;
433         }
434
435         dbus_message_iter_init(reply, &iter);
436
437         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
438                 dbus_message_unref(message);
439                 dbus_message_unref(reply);
440                 return FALSE;
441         }
442
443         if (folder_list) {
444                 g_ptr_array_free(folder_list, TRUE);
445                 folder_list = NULL;
446         }
447
448         folder_list = g_ptr_array_new();
449
450         dbus_message_iter_recurse(&iter, &recurse_iter);
451
452         while (dbus_message_iter_get_arg_type(&recurse_iter)
453                         == DBUS_TYPE_STRING) {
454                 gchar *str;
455
456                 dbus_message_iter_get_basic(&recurse_iter, &str);
457                 DBG("folder list %s\n", str);
458                 g_ptr_array_add(folder_list, g_strdup(str));
459
460                 dbus_message_iter_next(&recurse_iter);
461         }
462
463         dbus_message_unref(message);
464         dbus_message_unref(reply);
465         return TRUE;
466 }
467
468 int phonebook_init(void)
469 {
470         return 0;
471 }
472
473 void phonebook_exit(void)
474 {
475 }
476
477 int phonebook_connect(void **user_data)
478 {
479         struct phonebook_session *session;
480         gboolean ret;
481
482         DBG("");
483
484         session = g_new0(struct phonebook_session, 1);
485
486         *user_data = session;
487
488         ret = phonebook_folder_list_init(session);
489
490         if (ret)
491                 return 0;
492
493         return -1;
494 }
495
496 void phonebook_disconnect(void *user_data)
497 {
498         struct phonebook_session *session;
499         DBusMessage *message;
500         DBG("");
501         session = user_data;
502
503         if (folder_list) {
504                 g_ptr_array_free(folder_list, TRUE);
505                 folder_list = NULL;
506         }
507
508         if (!session->connection) {
509                 g_free(session);
510                 return;
511         }
512
513
514         if (session->clear_id)
515                 g_dbus_remove_watch(session->connection, session->clear_id);
516
517         message = dbus_message_new_method_call(PHONEBOOK_DESTINATION,
518                                                 PHONEBOOK_PATH,
519                                                 PHONEBOOK_INTERFACE,
520                                                 QUERY_DESTROY_AGENT);
521
522         if (message) {
523                 if (dbus_connection_send(session->connection, message,
524                                                                 NULL) == FALSE)
525                         error("Could not send dbus message");
526
527                 dbus_message_unref(message);
528         }
529
530         dbus_connection_unref(session->connection);
531         g_free(session);
532 }
533
534 char *phonebook_set_folder(const char *current_folder,
535                 const char *new_folder, uint8_t flags, int *err)
536 {
537         char *tmp1, *tmp2, *base, *path = NULL;
538         gboolean root, child;
539         int ret = 0;
540         int len;
541
542         root = (g_strcmp0("/", current_folder) == 0);
543         child = (new_folder && strlen(new_folder) != 0);
544
545         switch (flags) {
546                 case 0x02:
547                         /* Go back to root */
548                         if (!child) {
549                                 path = g_strdup("/");
550                                 goto done;
551                         }
552
553                         path = g_build_filename(current_folder, new_folder, NULL);
554                         break;
555                 case 0x03:
556                         /* Go up 1 level */
557                         if (root) {
558                                 /* Already root */
559                                 path = g_strdup("/");
560                                 goto done;
561                         }
562
563                         /*
564                          * Removing one level of the current folder. Current folder
565                          * contains AT LEAST one level since it is not at root folder.
566                          * Use glib utility functions to handle invalid chars in the
567                          * folder path properly.
568                          */
569                         tmp1 = g_path_get_basename(current_folder);
570                         tmp2 = g_strrstr(current_folder, tmp1);
571                         len = tmp2 - (current_folder + 1);
572
573                         g_free(tmp1);
574
575                         if (len == 0)
576                                 base = g_strdup("/");
577                         else
578                                 base = g_strndup(current_folder, len);
579
580                         /* Return: one level only */
581                         if (!child) {
582                                 path = base;
583                                 goto done;
584                         }
585
586                         path = g_build_filename(base, new_folder, NULL);
587                         g_free(base);
588
589                         break;
590                 default:
591                         ret = -EBADR;
592                         break;
593         }
594
595 done:
596         if (path && !folder_is_valid(path, FALSE))
597                 ret = -ENOENT;
598
599         if (ret < 0) {
600                 g_free(path);
601                 path = NULL;
602         }
603
604         if (err)
605                 *err = ret;
606
607         DBG("path : %s\n", path);
608
609         return path;
610 }
611
612 void phonebook_req_finalize(void *request)
613 {
614         struct phonebook_data *data = request;
615
616         DBG("");
617
618         if (!data)
619                 return;
620
621         if (data->call) {
622                 dbus_pending_call_cancel(data->call);
623                 dbus_pending_call_unref(data->call);
624         }
625         g_free(data->req_name);
626         g_free(data);
627 }
628
629 void *phonebook_pull(const char *name, const struct apparam_field *params,
630                                 phonebook_cb cb, void *user_data, int *err)
631 {
632         struct phonebook_data *data;
633         char *folder;
634
635         DBG("name %s", name);
636
637         if (!g_str_has_suffix(name, ".vcf")) {
638                 if (err)
639                         *err = -ENOENT;
640
641                 return NULL;
642         }
643
644         folder = g_strndup(name, strlen(name) - 4);
645
646         if (!folder_is_valid(folder, TRUE)) {
647                 if (err)
648                         *err = -ENOENT;
649
650                 g_free(folder);
651                 return NULL;
652         }
653
654         g_free(folder);
655
656         data = g_new0(struct phonebook_data, 1);
657         data->params = params;
658         data->user_data = user_data;
659         data->cb = cb;
660         data->req_name = g_strdup(name);
661
662         if (err)
663                 *err = 0;
664
665         return data;
666 }
667
668 int phonebook_pull_read(void *request)
669 {
670         struct phonebook_data *data = request;
671
672         DBG("name %s", data->req_name);
673
674         if (data->params->maxlistcount == 0) {
675                 data->call = phonebook_request(data,
676                                 QUERY_GET_PHONEBOOK_SIZE,
677                                 get_phonebook_size_reply,
678                                 DBUS_TYPE_STRING, &data->req_name,
679                                 DBUS_TYPE_INVALID);
680                 return 0;
681         }
682
683         data->call = phonebook_request(data,
684                         QUERY_GET_PHONEBOOK,
685                         get_phonebook_reply,
686                         DBUS_TYPE_STRING, &data->req_name,
687                         DBUS_TYPE_UINT64, &data->params->filter,
688                         DBUS_TYPE_BYTE, &data->params->format,
689                         DBUS_TYPE_UINT16, &data->params->maxlistcount,
690                         DBUS_TYPE_UINT16, &data->params->liststartoffset,
691                         DBUS_TYPE_INVALID);
692
693         return 0;
694 }
695
696 void *phonebook_get_entry(const char *folder, const char *id,
697                         const struct apparam_field *params, phonebook_cb cb,
698                         void *user_data, int *err)
699 {
700         struct phonebook_data *data;
701
702         DBG("");
703         if (!g_str_has_suffix(id, ".vcf")) {
704                 if (err)
705                         *err = -ENOENT;
706
707                 return NULL;
708         }
709
710         if (!folder_is_valid(folder, TRUE)) {
711                 if (err)
712                         *err = - ENOENT;
713
714                 return NULL;
715         }
716
717         DBG("folder %s id %s", folder, id);
718
719         data = g_new0(struct phonebook_data, 1);
720         data->params = params;
721         data->user_data = user_data;
722         data->cb = cb;
723
724         data->call = phonebook_request(data,
725                         QUERY_GET_PHONEBOOK_ENTRY,
726                         get_phonebook_entry_reply,
727                         DBUS_TYPE_STRING, &folder,
728                         DBUS_TYPE_STRING, &id,
729                         DBUS_TYPE_UINT64, &data->params->filter,
730                         DBUS_TYPE_BYTE, &data->params->format,
731                         DBUS_TYPE_INVALID);
732
733         if (*err) {
734                 if (data->call)
735                         *err = 0;
736                 else {
737                         *err = -ENOENT;
738                         g_free(data);
739                         data = NULL;
740                 }
741         }
742
743         return data;
744 }
745
746 void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
747                 phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
748 {
749         struct phonebook_data *data;
750
751         if (!folder_is_valid(name, TRUE)) {
752                 if (err)
753                         *err = - ENOENT;
754
755                 return NULL;
756         }
757
758         DBG("name %s", name);
759
760         data = g_new0(struct phonebook_data, 1);
761         data->user_data = user_data;
762         data->entry_cb = entry_cb;
763         data->ready_cb = ready_cb;
764
765         data->call = phonebook_request(data,
766                                 QUERY_GET_PHONEBOOK_LIST,
767                                 get_phonebook_list_reply,
768                                 DBUS_TYPE_STRING, &name,
769                                 DBUS_TYPE_INVALID);
770
771         if (*err) {
772                 if (data->call)
773                         *err = 0;
774                 else {
775                         *err = -ENOENT;
776                         g_free(data);
777                         data = NULL;
778                 }
779         }
780
781         return data;
782 }
783
784 void phonebook_set_cache_notification(void *session,
785                                 phonebook_cache_clear_cb clear_cb,
786                                 void *user_data)
787 {
788         struct phonebook_session *s = session;
789
790         DBG("");
791         s->clear_cb = clear_cb;
792
793         if (s->connection == NULL) {
794                 s->connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM,
795                                 NULL, NULL);
796
797                 if (s->connection == NULL) {
798                         error("Can't get on s bus");
799                         return;
800                 }
801         }
802
803         s->user_data = user_data;
804
805         if (s->clear_id) {
806                 g_dbus_remove_watch(s->connection, s->clear_id);
807                 s->clear_id = 0;
808         }
809
810         s->clear_id = g_dbus_add_signal_watch(s->connection,
811                         NULL, PHONEBOOK_PATH, PHONEBOOK_INTERFACE,
812                         "clear", clear_signal,
813                         s, NULL);
814 }