Add dbus permission check logic
[platform/core/appfw/shortcut.git] / lib / src / shortcut_manager.c
1 /*
2  * Copyright (c) 2011 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16 */
17 #include <gio/gio.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <string.h>
23
24 #include <aul.h>
25 #include <dlog.h>
26 #include <glib.h>
27 #include <db-util.h>
28
29 #include "shortcut.h"
30 #include "shortcut_private.h"
31 #include "shortcut_db.h"
32 #include "shortcut_manager.h"
33
34 #define SHORTCUT_PKGNAME_LEN 512
35 #define SHORTCUT_IS_WIDGET_SIZE(size)           (!!((size) & WIDGET_SIZE_DEFAULT))
36
37 #define PROVIDER_BUS_NAME "org.tizen.data_provider_service"
38 #define PROVIDER_OBJECT_PATH "/org/tizen/data_provider_service"
39 #define PROVIDER_SHORTCUT_INTERFACE_NAME "org.tizen.data_provider_shortcut_service"
40
41 #define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
42 #define DBUS_PATH_DBUS "/org/freedesktop/DBus"
43 #define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
44
45 static GDBusConnection *_gdbus_conn = NULL;
46 static int monitor_id = 0;
47 static int provider_monitor_id = 0;
48
49 static const GDBusErrorEntry dbus_error_entries[] =
50 {
51         {SHORTCUT_ERROR_INVALID_PARAMETER, "org.freedesktop.Shortcut.Error.INVALID_PARAMETER"},
52         {SHORTCUT_ERROR_OUT_OF_MEMORY,     "org.freedesktop.Shortcut.Error.OUT_OF_MEMORY"},
53         {SHORTCUT_ERROR_IO_ERROR,          "org.freedesktop.Shortcut.Error.IO_ERROR"},
54         {SHORTCUT_ERROR_PERMISSION_DENIED, "org.freedesktop.Shortcut.Error.PERMISSION_DENIED"},
55         {SHORTCUT_ERROR_NOT_SUPPORTED,           "org.freedesktop.Shortcut.Error.NOT_SUPPORTED"},
56         {SHORTCUT_ERROR_RESOURCE_BUSY,  "org.freedesktop.Shortcut.Error.RESOURCE_BUSY"},
57         {SHORTCUT_ERROR_NO_SPACE,         "org.freedesktop.Shortcut.Error.NO_SPACE"},
58         {SHORTCUT_ERROR_EXIST,      "org.freedesktop.Shortcut.Error.EXIST"},
59         {SHORTCUT_ERROR_FAULT, "org.freedesktop.Shortcut.Error.FAULT"},
60         {SHORTCUT_ERROR_COMM, "org.freedesktop.Shortcut.Error.COMM"},
61 };
62
63 struct result_cb_item {
64         result_internal_cb_t result_internal_cb;
65         result_cb_t result_cb;
66         void *data;
67 };
68
69 typedef struct _shortcut_cb_info {
70         int (*request_cb)(const char *appid, const char *name, int type, const char *content, const char *icon, pid_t pid, double period, int allow_duplicate, void *data);
71         void *data;
72 } shortcut_cb_info;
73
74 static shortcut_cb_info _callback_info;
75 static int _dbus_init();
76 static char *_shortcut_get_pkgname_by_pid(void);
77
78 EXPORT_API GQuark shortcut_error_quark (void)
79 {
80         static volatile gsize quark_volatile = 0;
81         g_dbus_error_register_error_domain ("shortcut-error-quark",
82                         &quark_volatile,
83                         dbus_error_entries,
84                         G_N_ELEMENTS(dbus_error_entries));
85         return (GQuark) quark_volatile;
86 }
87
88 static void _add_shortcut_notify(GVariant *parameters)
89 {
90         const char *appid;
91         const char *name;
92         int type;
93         const char *content;
94         const char *icon;
95         int allow_duplicate;
96         int sender_pid;
97
98         g_variant_get(parameters, "(i&s&si&s&si)", &sender_pid, &appid, &name, &type, &content, &icon, &allow_duplicate);
99         DbgPrint("_add_shortcut_notify sender_pid: %d ", sender_pid);
100         _callback_info.request_cb(appid, name, type, content, icon, sender_pid, -1.0f, allow_duplicate, _callback_info.data);
101 }
102
103 static void _add_shortcut_widget_notify(GVariant *parameters)
104 {
105         const char *appid;
106         const char *name;
107         int type;
108         const char *content;
109         const char *icon;
110         int allow_duplicate;
111         int sender_pid;
112         double period;
113
114         g_variant_get(parameters, "(i&s&si&s&sdi)", &sender_pid, &appid, &name, &type, &content, &icon, &period, &allow_duplicate);
115         _callback_info.request_cb(appid, name, type, content, icon, sender_pid, period, allow_duplicate, _callback_info.data);
116 }
117
118 static void _handle_shortcut_notify(GDBusConnection *connection,
119                 const gchar     *sender_name,
120                 const gchar     *object_path,
121                 const gchar     *interface_name,
122                 const gchar     *signal_name,
123                 GVariant        *parameters,
124                 gpointer         user_data)
125 {
126         DbgPrint("signal_name: %s", signal_name);
127         if (g_strcmp0(signal_name, "add_shortcut_notify") == 0)
128                 _add_shortcut_notify(parameters);
129         else if (g_strcmp0(signal_name, "add_shortcut_widget_notify") == 0)
130                 _add_shortcut_widget_notify(parameters);
131 }
132
133 static int _dbus_init(void)
134 {
135         GError *error = NULL;
136
137         if (_gdbus_conn == NULL) {
138                 _gdbus_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
139
140                 if (_gdbus_conn == NULL) {
141                         if (error != NULL) {
142                                 ErrPrint("Failed to get dbus [%s]", error->message);
143                                 g_error_free(error);
144                         }
145                         return SHORTCUT_ERROR_IO_ERROR;
146                 }
147                 shortcut_error_quark();
148         }
149
150         return SHORTCUT_ERROR_NONE;
151 }
152
153 static int _dbus_signal_init()
154 {
155         int ret = SHORTCUT_ERROR_NONE;
156         int id;
157
158         if (monitor_id == 0) {
159                 DbgPrint("get dbus connection success");
160                 id = g_dbus_connection_signal_subscribe(
161                                 _gdbus_conn,
162                                 PROVIDER_BUS_NAME,
163                                 PROVIDER_SHORTCUT_INTERFACE_NAME,       /*    interface */
164                                 NULL,                                   /*    member */
165                                 PROVIDER_OBJECT_PATH,                   /*    path */
166                                 NULL,                                   /*    arg0 */
167                                 G_DBUS_SIGNAL_FLAGS_NONE,
168                                 _handle_shortcut_notify,
169                                 NULL,
170                                 NULL);
171
172                 DbgPrint("subscribe id : %d", id);
173                 if (id == 0) {
174                         ret = SHORTCUT_ERROR_IO_ERROR;
175                         ErrPrint("Failed to _register_noti_dbus_interface");
176                 } else {
177                         monitor_id = id;
178                 }
179         }
180
181         return ret;
182 }
183
184 static char *_shortcut_get_pkgname_by_pid(void)
185 {
186         char pkgname[SHORTCUT_PKGNAME_LEN + 1] = { 0, };
187         char buf[SHORTCUT_PKGNAME_LEN + 1] = { 0, };
188         int pid, ret;
189         int fd;
190         char *dup_pkgname;
191
192         pid = getpid();
193
194         ret = aul_app_get_pkgname_bypid(pid, pkgname, sizeof(pkgname));
195         if (ret != 0) {
196                 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
197
198                 fd = open(buf, O_RDONLY);
199                 if (fd < 0)
200                         return NULL;
201
202                 ret = read(fd, pkgname, sizeof(pkgname) - 1);
203                 close(fd);
204
205                 if (ret <= 0)
206                         return NULL;
207
208                 pkgname[ret] = '\0';
209                 /*!
210                  * \NOTE
211                  * "ret" is not able to be larger than "sizeof(pkgname) - 1",
212                  * if the system is not going wrong.
213                  */
214         } else {
215                 if (strlen(pkgname) <= 0)
216                         return NULL;
217         }
218
219         dup_pkgname = strdup(pkgname);
220         if (!dup_pkgname)
221                 ErrPrint("Heap: %d\n", errno);
222
223         return dup_pkgname;
224 }
225
226 /*
227  * implement user request
228  */
229 static int _send_sync_shortcut(GVariant *body, GDBusMessage **reply, char *cmd)
230 {
231         GError *err = NULL;
232         GDBusMessage *msg ;
233         int ret = SHORTCUT_ERROR_NONE;
234
235         msg = g_dbus_message_new_method_call(
236                         PROVIDER_BUS_NAME,
237                         PROVIDER_OBJECT_PATH,
238                         PROVIDER_SHORTCUT_INTERFACE_NAME,
239                         cmd);
240         if (!msg) {
241                 ErrPrint("Can't allocate new method call");
242                 if (body)
243                         g_variant_unref(body);
244                 return SHORTCUT_ERROR_OUT_OF_MEMORY;
245         }
246
247         if (body != NULL)
248                 g_dbus_message_set_body(msg, body);
249
250         *reply = g_dbus_connection_send_message_with_reply_sync(
251                         _gdbus_conn,
252                         msg,
253                         G_DBUS_SEND_MESSAGE_FLAGS_NONE,
254                         -1,
255                         NULL,
256                         NULL,
257                         &err);
258
259         g_object_unref(msg);
260
261         if (!*reply) {
262                 ret = SHORTCUT_ERROR_COMM;
263                 if (err != NULL) {
264                         ErrPrint("No reply. cmd = %s,  error = %s", cmd, err->message);
265                         if (err->code == G_DBUS_ERROR_ACCESS_DENIED)
266                                 ret = SHORTCUT_ERROR_PERMISSION_DENIED;
267                         g_error_free(err);
268                 }
269                 return ret;
270         }
271
272         if (g_dbus_message_to_gerror(*reply, &err)) {
273                 ret = err->code;
274                 ErrPrint("_send_sync_shortcut error %s", err->message);
275                 g_error_free(err);
276                 return ret;
277         }
278         DbgPrint("_send_sync_shortcut done !!");
279         return SHORTCUT_ERROR_NONE;
280 }
281
282 static int _send_service_register()
283 {
284         GDBusMessage *reply = NULL;
285         int result;
286
287         result = _send_sync_shortcut(NULL, &reply, "shortcut_service_register");
288         ErrPrint("_send_service_register done");
289         return result;
290 }
291
292 static void _send_message_with_reply_sync_cb(GDBusConnection *connection,
293                 GAsyncResult *res,
294                 gpointer user_data)
295 {
296         int result = SHORTCUT_ERROR_NONE;
297         GError *err = NULL;
298         GDBusMessage *reply = NULL;
299         struct result_cb_item *cb_item = (struct result_cb_item *)user_data;
300
301         if (cb_item == NULL) {
302                 ErrPrint("Failed to get a callback item");
303                 return;
304         }
305
306         reply = g_dbus_connection_send_message_with_reply_finish(
307                         connection,
308                         res,
309                         &err);
310
311         if (!reply) {
312                 if (err != NULL) {
313                         ErrPrint("No reply. error = %s", err->message);
314                         g_error_free(err);
315                 }
316                 result = SHORTCUT_ERROR_COMM;
317
318         } else if (g_dbus_message_to_gerror(reply, &err)) {
319                 result = err->code;
320                 g_error_free(err);
321                 ErrPrint("_send_async_noti error %s", err->message);
322         }
323
324         if (cb_item->result_internal_cb)
325                 result = cb_item->result_internal_cb(result, getpid(), cb_item->data);
326         else if (cb_item->result_cb)
327                 result = cb_item->result_cb(result, cb_item->data);
328
329         if (reply)
330                 g_object_unref(reply);
331
332         free(cb_item);
333 }
334
335 static int _send_async_noti(GVariant *body, struct result_cb_item *cb_item, char *cmd)
336 {
337         GDBusMessage *msg;
338         msg = g_dbus_message_new_method_call(
339                         PROVIDER_BUS_NAME,
340                         PROVIDER_OBJECT_PATH,
341                         PROVIDER_SHORTCUT_INTERFACE_NAME,
342                         cmd);
343         if (!msg) {
344                 ErrPrint("Can't allocate new method call");
345                 return SHORTCUT_ERROR_OUT_OF_MEMORY;
346         }
347
348         if (body != NULL)
349                 g_dbus_message_set_body(msg, body);
350
351         g_dbus_connection_send_message_with_reply(
352                         _gdbus_conn,
353                         msg,
354                         G_DBUS_SEND_MESSAGE_FLAGS_NONE,
355                         -1,
356                         NULL,
357                         NULL,
358                         (GAsyncReadyCallback)_send_message_with_reply_sync_cb,
359                         cb_item);
360
361         g_object_unref(msg);
362
363         DbgPrint("_send_async_noti done !!");
364         return SHORTCUT_ERROR_NONE;
365 }
366
367 static void _on_name_appeared(GDBusConnection *connection,
368                 const gchar     *name,
369                 const gchar     *name_owner,
370                 gpointer         user_data)
371 {
372         DbgPrint("name appeared : %s", name);
373         _send_service_register();
374 }
375
376 static void _on_name_vanished(GDBusConnection *connection,
377                 const gchar     *name,
378                 gpointer         user_data)
379 {
380         DbgPrint("name vanished : %s", name);
381 }
382
383 EAPI int shortcut_set_request_cb(shortcut_request_cb request_cb, void *data)
384 {
385         int ret = _dbus_init();
386
387         if (ret != SHORTCUT_ERROR_NONE) {
388                 ErrPrint("Can't init dbus %d", ret);
389                 return ret;
390         }
391
392         ret = _dbus_signal_init();
393         if (ret != SHORTCUT_ERROR_NONE) {
394                 ErrPrint("Can't init dbus_signal %d", ret);
395                 return ret;
396         }
397
398         ret = _send_service_register();
399         if (ret != SHORTCUT_ERROR_NONE) {
400                 ErrPrint("Can't init ipc_monitor_register %d", ret);
401                 return ret;
402         }
403
404         if (request_cb == NULL)
405                 return SHORTCUT_ERROR_INVALID_PARAMETER;
406
407         if (provider_monitor_id == 0) {
408                 provider_monitor_id = g_bus_watch_name_on_connection(
409                                 _gdbus_conn,
410                                 PROVIDER_BUS_NAME,
411                                 G_BUS_NAME_WATCHER_FLAGS_NONE,
412                                 _on_name_appeared,
413                                 _on_name_vanished,
414                                 NULL,
415                                 NULL);
416
417                 if (provider_monitor_id == 0) {
418                         ErrPrint("watch on name fail");
419                         return SHORTCUT_ERROR_IO_ERROR;
420                 }
421         }
422
423         _callback_info.request_cb = request_cb;
424         _callback_info.data = data;
425
426         return SHORTCUT_ERROR_NONE;
427 }
428
429 EAPI int shortcut_add_to_home(const char *name, shortcut_type type, const char *uri,
430                 const char *icon, int allow_duplicate, result_cb_t result_cb, void *data)
431 {
432         struct result_cb_item *item;
433         char *appid;
434         int ret;
435         GVariant *body;
436
437         if (ADD_TO_HOME_IS_DYNAMICBOX(type)) {
438                 ErrPrint("Invalid type used for adding a shortcut\n");
439                 return SHORTCUT_ERROR_INVALID_PARAMETER;
440         }
441
442         ret = _dbus_init();
443         if (ret != SHORTCUT_ERROR_NONE) {
444                 ErrPrint("Can't init dbus %d", ret);
445                 return ret;
446         }
447
448         appid = _shortcut_get_pkgname_by_pid();
449         item = malloc(sizeof(struct result_cb_item));
450         if (!item) {
451                 if (appid)
452                         free(appid);
453
454                 ErrPrint("Heap: %d\n", errno);
455                 return SHORTCUT_ERROR_OUT_OF_MEMORY;
456         }
457
458         item->result_cb = result_cb;
459         item->result_internal_cb = NULL;
460         item->data = data;
461
462         if (!name)
463                 name = "";
464
465         if (!uri)
466                 uri = "";
467
468         if (!icon)
469                 icon = "";
470
471         body = g_variant_new("(ississi)", getpid(), appid, name, type, uri, icon, allow_duplicate);
472         ret = _send_async_noti(body, item, "add_shortcut");
473         if (ret != SHORTCUT_ERROR_NONE) {
474                 free(item);
475                 item = NULL;
476         }
477
478         if (appid)
479                 free(appid);
480         if (body)
481                 g_variant_unref(body);
482
483         return ret;
484 }
485
486 EAPI int shortcut_add_to_home_widget(const char *name, shortcut_widget_size_e size, const char *widget_id,
487                 const char *icon, double period, int allow_duplicate, result_cb_t result_cb, void *data)
488 {
489         struct result_cb_item *item;
490         char *appid;
491         int ret;
492         GVariant *body;
493
494         if (name == NULL) {
495                 ErrPrint("AppID is null\n");
496                 return SHORTCUT_ERROR_INVALID_PARAMETER;
497         }
498
499         if (!SHORTCUT_IS_WIDGET_SIZE(size)) {
500                 ErrPrint("Invalid type used for adding a widget\n");
501                 return SHORTCUT_ERROR_INVALID_PARAMETER;
502         }
503
504         ret = _dbus_init();
505         if (ret != SHORTCUT_ERROR_NONE) {
506                 ErrPrint("Can't init dbus %d", ret);
507                 return ret;
508         }
509
510         appid = _shortcut_get_pkgname_by_pid();
511         item = malloc(sizeof(struct result_cb_item));
512         if (!item) {
513                 if (appid)
514                         free(appid);
515
516                 ErrPrint("Heap: %d\n", errno);
517                 return SHORTCUT_ERROR_OUT_OF_MEMORY;
518         }
519
520         item->result_cb = result_cb;
521         item->result_internal_cb = NULL;
522         item->data = data;
523
524         body = g_variant_new("(ississdi)", getpid(), widget_id, name, size, NULL, icon, period, allow_duplicate);
525         ret = _send_async_noti(body, item, "add_shortcut_widget");
526
527         if (ret != SHORTCUT_ERROR_NONE) {
528                 free(item);
529                 item = NULL;
530         }
531
532         if (appid)
533                 free(appid);
534         if (body)
535                 g_variant_unref(body);
536
537         return ret;
538 }
539
540 EAPI int add_to_home_shortcut(const char *appid, const char *name, int type, const char *content,
541                 const char *icon, int allow_duplicate, result_internal_cb_t result_cb, void *data)
542 {
543         /*Deprecated API */
544         return SHORTCUT_ERROR_NONE;
545 }
546
547 EAPI int add_to_home_dynamicbox(const char *appid, const char *name, int type, const char *content, const char *icon, double period, int allow_duplicate, result_internal_cb_t result_cb, void *data)
548 {
549         /*Deprecated API */
550         return SHORTCUT_ERROR_NONE;
551 }
552
553
554 EAPI int shortcut_get_list(const char *package_name, shortcut_list_cb list_cb, void *data)
555 {
556         GDBusMessage *reply = NULL;
557         int result;
558         int count = 0;
559         GVariant *body;
560         GVariant *reply_body;
561         GVariant *iter_body;
562         GVariantIter *iter;
563         GVariantBuilder *b;
564         shortcut_info_s shortcut;
565
566         if (list_cb == NULL)
567                 return SHORTCUT_ERROR_INVALID_PARAMETER;
568
569         result = _dbus_init();
570         if (result != SHORTCUT_ERROR_NONE) {
571                 ErrPrint("Can't init dbus %d", result);
572                 return result;
573         }
574
575         b = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
576         if (package_name)
577                 g_variant_builder_add(b, "{sv}", "package_name", g_variant_new_string(package_name));
578         body = g_variant_builder_end(b);
579         result = _send_sync_shortcut(g_variant_new("(v)", body), &reply, "get_list");
580
581         if (result == SHORTCUT_ERROR_NONE) {
582                 reply_body = g_dbus_message_get_body(reply);
583                 g_variant_get(reply_body, "(ia(v))", &count, &iter);
584
585                 while (g_variant_iter_loop(iter, "(v)", &iter_body)) {
586                         g_variant_get(reply_body, "(&s&s&s&s&s)",
587                                 &shortcut.package_name, &shortcut.icon, &shortcut.name, &shortcut.extra_key, &shortcut.extra_data);
588                         list_cb(shortcut.package_name, shortcut.icon, shortcut.name, shortcut.extra_key, shortcut.extra_data, data);
589                 }
590                 g_variant_iter_free(iter);
591         }
592
593         if(reply)
594                 g_object_unref(reply);
595
596         return count;
597 }
598
599 /* End of a file */