upgrade obexd to 0.47
[profile/ivi/obexd.git] / 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_SIZE        "GetPhonebookSize"
53 #define QUERY_GET_PHONEBOOK             "GetPhonebook"
54 #define QUERY_GET_PHONEBOOK_LIST        "GetPhonebookList"
55 #define QUERY_GET_PHONEBOOK_ENTRY       "GetPhonebookEntry"
56
57 struct phonebook_data {
58         phonebook_cb cb;
59         DBusPendingCallNotifyFunction reply_cb;
60         void *user_data;
61         const struct apparam_field *params;
62
63         phonebook_entry_cb entry_cb;
64         phonebook_cache_ready_cb ready_cb;
65
66         char *req_name;
67 };
68
69 struct phonebook_session {
70         DBusConnection *connection;
71         phonebook_cache_clear_cb clear_cb;
72         unsigned int clear_id;
73
74         void *user_data;
75 };
76
77 static gboolean folder_is_valid(const char *folder)
78 {
79         if (folder == NULL)
80                 return FALSE;
81
82         if (g_str_equal(folder, "/"))
83                 return TRUE;
84         else if (g_str_equal(folder, "/telecom"))
85                 return TRUE;
86         else if (g_str_equal(folder, "/telecom/pb"))
87                 return TRUE;
88         else if (g_str_equal(folder, "/telecom/ich"))
89                 return TRUE;
90         else if (g_str_equal(folder, "/telecom/och"))
91                 return TRUE;
92         else if (g_str_equal(folder, "/telecom/mch"))
93                 return TRUE;
94         else if (g_str_equal(folder, "/telecom/cch"))
95                 return TRUE;
96
97         return FALSE;
98 }
99
100 static gboolean phonebook_request(struct phonebook_data *data,
101                                 const gchar *method,
102                                 DBusPendingCallNotifyFunction function,
103                                 int first_arg_type,
104                                 ...)
105 {
106         DBusConnection *connection;
107         DBusPendingCall *call;
108         DBusMessage *message;
109
110         va_list args;
111
112         DBG("%s\n", method);
113
114         if (!method) {
115                 error("Can't get method name");
116                 return FALSE;
117         }
118
119         connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
120         if (!connection) {
121                 error("Can't get on session bus");
122                 return FALSE;
123         }
124
125         message = dbus_message_new_method_call(PHONEBOOK_DESTINATION,
126                         PHONEBOOK_PATH,
127                         PHONEBOOK_INTERFACE,
128                         method);
129         if (!message) {
130                 dbus_connection_unref(connection);
131                 error("Can't Allocate new message");
132                 return FALSE;
133         }
134
135         va_start(args, first_arg_type);
136         dbus_message_append_args_valist(message, first_arg_type, args);
137         va_end(args);
138
139         if (dbus_connection_send_with_reply(connection, message, &call, -1) == FALSE) {
140                 dbus_message_unref(message);
141                 dbus_connection_unref(connection);
142                 return FALSE;
143         }
144         dbus_pending_call_set_notify(call, function, data, NULL);
145
146         dbus_pending_call_unref(call);
147         dbus_message_unref(message);
148         dbus_connection_unref(connection);
149
150         return TRUE;
151 }
152
153 static void get_phonebook_size_reply(DBusPendingCall *call, void *user_data)
154 {
155         DBusMessage *reply = dbus_pending_call_steal_reply(call);
156         struct phonebook_data *s_data = user_data;
157         DBusError derr;
158         uint32_t phonebook_size;
159
160         DBG("");
161
162         if (!reply) {
163                 DBG("Reply Error\n");
164                 return;
165         }
166
167         dbus_error_init(&derr);
168         if (dbus_set_error_from_message(&derr, reply)) {
169                 error("Replied with an error: %s, %s", derr.name, derr.message);
170                 dbus_error_free(&derr);
171                 phonebook_size = 0;
172         } else {
173                 dbus_message_get_args(reply, NULL, DBUS_TYPE_UINT32,
174                                         &phonebook_size, DBUS_TYPE_INVALID);
175                 DBG("phonebooksize:%d\n", phonebook_size);
176         }
177
178         s_data->cb(NULL, 0, phonebook_size, 0, TRUE, s_data->user_data);
179
180         dbus_message_unref(reply);
181 }
182
183 static void get_phonebook_reply(DBusPendingCall *call, void *user_data)
184 {
185         DBusMessage *reply = dbus_pending_call_steal_reply(call);
186         struct phonebook_data *s_data = user_data;
187         DBusError derr;
188         GString *buffer;
189
190         guint new_missed_call = 0;
191
192         uint32_t count = 0;
193
194         DBG("");
195
196         if (!reply) {
197                 DBG("Reply Error\n");
198                 return;
199         }
200
201         buffer = g_string_new("");
202
203         dbus_error_init(&derr);
204         if (dbus_set_error_from_message(&derr, reply)) {
205                 error("Replied with an error: %s, %s", derr.name, derr.message);
206                 dbus_error_free(&derr);
207         } else {
208                 DBusMessageIter iter;
209
210                 dbus_message_iter_init(reply, &iter);
211
212                 if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
213                         DBusMessageIter recurse_iter;
214
215                         dbus_message_iter_recurse(&iter, &recurse_iter);
216                         while (dbus_message_iter_get_arg_type(&recurse_iter) == DBUS_TYPE_STRING) {
217                                 gchar *str;
218
219                                 dbus_message_iter_get_basic(&recurse_iter, &str);
220                                 dbus_message_iter_next(&recurse_iter);
221
222                                 g_string_append(buffer, str);
223
224                                 DBG("str:\n%s\n", str);
225
226                                 count++;
227                         }
228                         dbus_message_iter_next(&iter);
229                 }
230
231                 if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT16)
232                         dbus_message_iter_get_basic(&iter, &new_missed_call);
233
234                 DBG("new_missed_call %d\n", new_missed_call);
235         }
236
237         s_data->cb(buffer->str, buffer->len, count, new_missed_call, 1, s_data->user_data);
238
239         g_string_free(buffer, TRUE);
240         dbus_message_unref(reply);
241 }
242
243
244 static void get_phonebook_list_reply(DBusPendingCall *call, void *user_data)
245 {
246         DBusMessage *reply = dbus_pending_call_steal_reply(call);
247         DBusMessageIter iter, iter_struct, entry;
248         struct phonebook_data *data = user_data;
249         DBusError derr;
250         uint32_t handle = 0;
251         const char *name = NULL;
252         const char *tel = NULL;
253         char id[10];
254
255         if (!reply) {
256                 DBG("Reply Error\n");
257                 return;
258         }
259
260         dbus_error_init(&derr);
261         if (dbus_set_error_from_message(&derr, reply)) {
262                 error("Replied with an error: %s, %s", derr.name, derr.message);
263                 dbus_error_free(&derr);
264         } else {
265
266                 dbus_message_iter_init(reply, &iter);
267
268                 dbus_message_iter_recurse(&iter, &iter_struct);
269
270                 while (dbus_message_iter_get_arg_type(&iter_struct) ==
271                                                         DBUS_TYPE_STRUCT) {
272                         dbus_message_iter_recurse(&iter_struct, &entry);
273
274                         dbus_message_iter_get_basic(&entry, &name);
275                         dbus_message_iter_next(&entry);
276                         dbus_message_iter_get_basic(&entry, &tel);
277                         dbus_message_iter_next(&entry);
278                         dbus_message_iter_get_basic(&entry, &handle);
279
280                         /*
281                         DBG("[handle:%d name:%s tel:%s]\n", handle, name, tel);
282                         */
283
284                         snprintf(id, sizeof(id), "%d.vcf", handle);
285
286                         data->entry_cb(id, handle, name, NULL, tel,
287                                         data->user_data);
288
289                         dbus_message_iter_next(&iter_struct);
290                 }
291         }
292
293         data->ready_cb(data->user_data);
294
295         dbus_message_unref(reply);
296 }
297
298 static void get_phonebook_entry_reply(DBusPendingCall *call, void *user_data)
299 {
300         DBusMessage *reply = dbus_pending_call_steal_reply(call);
301         struct phonebook_data *s_data = user_data;
302         DBusError derr;
303         const char *phonebook_entry;
304
305         DBG("");
306
307         if (!reply) {
308                 DBG("Reply Error\n");
309                 return;
310         }
311
312         dbus_error_init(&derr);
313         if (dbus_set_error_from_message(&derr, reply)) {
314                 error("Replied with an error: %s, %s", derr.name, derr.message);
315                 dbus_error_free(&derr);
316         } else {
317                 dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING,
318                                         &phonebook_entry, DBUS_TYPE_INVALID);
319                 DBG("phonebook_entry:[%s]\n", phonebook_entry);
320         }
321
322         s_data->cb(phonebook_entry, strlen(phonebook_entry), 1, 0, TRUE,
323                                                         s_data->user_data);
324
325         dbus_message_unref(reply);
326 }
327
328 static gboolean get_sim_phonebook_reply(void *user_data)
329 {
330         struct phonebook_data *s_data = user_data;
331         uint32_t phonebook_size = 0;
332         int lastpart = 1;
333
334         DBG("");
335
336         /* We don't support phonebook of SIM
337          * Send dummy data */
338         s_data->cb(NULL, 0, phonebook_size, 0, lastpart, s_data->user_data);
339
340         return FALSE;
341 }
342
343 static gboolean clear_signal(DBusConnection *conn, DBusMessage *msg,
344                         void *user_data)
345 {
346         struct phonebook_session *session;
347
348         if (user_data == NULL)
349                 return FALSE;
350
351         DBG("");
352         session = user_data;
353
354         session->clear_cb(session->user_data);
355
356         g_dbus_remove_watch(session->connection, session->clear_id);
357         session->clear_id = 0;
358
359         return TRUE;
360 }
361
362
363 int phonebook_init(void)
364 {
365         return 0;
366 }
367
368 void phonebook_exit(void)
369 {
370 }
371
372 int phonebook_connect(void **user_data)
373 {
374         struct phonebook_session *session;
375
376         DBG("");
377
378         session = g_new0(struct phonebook_session, 1);
379
380         *user_data = session;
381
382         return 0;
383 }
384
385 void phonebook_disconnect(void *user_data)
386 {
387         struct phonebook_session *session;
388
389         DBG("");
390         session = user_data;
391
392         g_dbus_remove_watch(session->connection, session->clear_id);
393         dbus_connection_unref(session->connection);
394
395         g_free(session);
396 }
397
398 char *phonebook_set_folder(const char *current_folder,
399                 const char *new_folder, uint8_t flags, int *err)
400 {
401         char *tmp1, *tmp2, *base, *path = NULL;
402         gboolean root, child;
403         int ret = 0;
404         int len;
405
406         root = (g_strcmp0("/", current_folder) == 0);
407         child = (new_folder && strlen(new_folder) != 0);
408
409         switch (flags) {
410                 case 0x02:
411                         /* Go back to root */
412                         if (!child) {
413                                 path = g_strdup("/");
414                                 goto done;
415                         }
416
417                         path = g_build_filename(current_folder, new_folder, NULL);
418                         break;
419                 case 0x03:
420                         /* Go up 1 level */
421                         if (root) {
422                                 /* Already root */
423                                 path = g_strdup("/");
424                                 goto done;
425                         }
426
427                         /*
428                          * Removing one level of the current folder. Current folder
429                          * contains AT LEAST one level since it is not at root folder.
430                          * Use glib utility functions to handle invalid chars in the
431                          * folder path properly.
432                          */
433                         tmp1 = g_path_get_basename(current_folder);
434                         tmp2 = g_strrstr(current_folder, tmp1);
435                         len = tmp2 - (current_folder + 1);
436
437                         g_free(tmp1);
438
439                         if (len == 0)
440                                 base = g_strdup("/");
441                         else
442                                 base = g_strndup(current_folder, len);
443
444                         /* Return: one level only */
445                         if (!child) {
446                                 path = base;
447                                 goto done;
448                         }
449
450                         path = g_build_filename(base, new_folder, NULL);
451                         g_free(base);
452
453                         break;
454                 default:
455                         ret = -EBADR;
456                         break;
457         }
458
459 done:
460         if (path && !folder_is_valid(path))
461                 ret = -ENOENT;
462
463         if (ret < 0) {
464                 g_free(path);
465                 path = NULL;
466         }
467
468         if (err)
469                 *err = ret;
470
471         return path;
472 }
473
474 void phonebook_req_finalize(void *request)
475 {
476         struct phonebook_data *data = request;
477
478         DBG("");
479
480         if (!data)
481                 return;
482
483         g_free(data->req_name);
484         g_free(data);
485 }
486
487 void *phonebook_pull(const char *name, const struct apparam_field *params,
488                                 phonebook_cb cb, void *user_data, int *err)
489 {
490         struct phonebook_data *data;
491
492         DBG("name %s", name);
493
494         if (!g_str_has_suffix(name, ".vcf")) {
495                 DBG("invaild request");
496                 if (err)
497                         *err = -EBADR;
498                 return NULL;
499         }
500
501         data = g_new0(struct phonebook_data, 1);
502         data->params = params;
503         data->user_data = user_data;
504         data->cb = cb;
505         data->req_name = g_strdup(name);
506
507         if (err)
508                 *err = 0;
509
510         return data;
511 }
512
513 int phonebook_pull_read(void *request)
514 {
515         struct phonebook_data *data = request;
516
517         DBG("name %s", data->req_name);
518
519         if (data->params->maxlistcount == 0) {
520                 phonebook_request(data,
521                                 QUERY_GET_PHONEBOOK_SIZE,
522                                 get_phonebook_size_reply,
523                                 DBUS_TYPE_STRING, &data->req_name,
524                                 DBUS_TYPE_INVALID);
525                 return 0;
526         }
527
528         if (g_strcmp0(data->req_name, "/SIM1/telecom/pb.vcf") == 0) {
529                 g_idle_add(get_sim_phonebook_reply, data);
530                 return 0;
531         }
532
533         phonebook_request(data,
534                         QUERY_GET_PHONEBOOK,
535                         get_phonebook_reply,
536                         DBUS_TYPE_STRING, &data->req_name,
537                         DBUS_TYPE_UINT64, &data->params->filter,
538                         DBUS_TYPE_BYTE, &data->params->format,
539                         DBUS_TYPE_UINT16, &data->params->maxlistcount,
540                         DBUS_TYPE_UINT16, &data->params->liststartoffset,
541                         DBUS_TYPE_INVALID);
542
543         return 0;
544 }
545
546 void *phonebook_get_entry(const char *folder, const char *id,
547                         const struct apparam_field *params, phonebook_cb cb,
548                         void *user_data, int *err)
549 {
550         struct phonebook_data *data;
551
552         if (!g_str_has_suffix(id, ".vcf")) {
553                 DBG("invaild request");
554                 if (err)
555                         *err = -EBADR;
556                 return NULL;
557         }
558         DBG("folder %s id %s", folder, id);
559
560         data = g_new0(struct phonebook_data, 1);
561         data->params = params;
562         data->user_data = user_data;
563         data->cb = cb;
564
565         phonebook_request(data,
566                         QUERY_GET_PHONEBOOK_ENTRY,
567                         get_phonebook_entry_reply,
568                         DBUS_TYPE_STRING, &folder,
569                         DBUS_TYPE_STRING, &id,
570                         DBUS_TYPE_UINT64, &data->params->filter,
571                         DBUS_TYPE_BYTE, &data->params->format,
572                         DBUS_TYPE_INVALID);
573
574         return data;
575 }
576
577 void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
578                 phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
579 {
580         struct phonebook_data *data;
581         gboolean req = FALSE;
582
583         DBG("name %s", name);
584
585         data = g_new0(struct phonebook_data, 1);
586         data->user_data = user_data;
587         data->entry_cb = entry_cb;
588         data->ready_cb = ready_cb;
589
590         req = phonebook_request(data,
591                                 QUERY_GET_PHONEBOOK_LIST,
592                                 get_phonebook_list_reply,
593                                 DBUS_TYPE_STRING, &name,
594                                 DBUS_TYPE_INVALID);
595
596         if (*err) {
597                 if (req)
598                         *err = 0;
599                 else
600                         *err = -ENOENT;
601         }
602
603         return data;
604 }
605
606 void phonebook_set_cache_notification(void *session,
607                                 phonebook_cache_clear_cb clear_cb,
608                                 void *user_data)
609 {
610         struct phonebook_session *s = session;
611
612         DBG("");
613         s->clear_cb = clear_cb;
614
615         if (s->connection == NULL) {
616                 s->connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM,
617                                 NULL, NULL);
618
619                 if (s->connection == NULL) {
620                         error("Can't get on s bus");
621                         return;
622                 }
623         }
624
625         s->user_data = user_data;
626
627         if (s->clear_id) {
628                 g_dbus_remove_watch(s->connection, s->clear_id);
629                 s->clear_id = 0;
630         }
631
632         s->clear_id = g_dbus_add_signal_watch(s->connection,
633                         NULL, PHONEBOOK_PATH, PHONEBOOK_INTERFACE,
634                         "clear", clear_signal,
635                         s, NULL);
636 }