430b7db70b80ffb468d748cd984a153609688399
[platform/core/appfw/shortcut.git] / lib / src / shortcut_internal.c
1 /*
2  * Copyright (c) 2011 - 2016 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 <stdio.h>
28
29 #include "shortcut.h"
30 #include "shortcut_private.h"
31 #include "shortcut_internal.h"
32
33 #define SHORTCUT_PKGNAME_LEN 512
34
35 #define PROVIDER_BUS_NAME "org.tizen.data_provider_service"
36 #define PROVIDER_OBJECT_PATH "/org/tizen/data_provider_service"
37 #define PROVIDER_SHORTCUT_INTERFACE_NAME "org.tizen.data_provider_shortcut_service"
38
39 #define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
40 #define DBUS_PATH_DBUS "/org/freedesktop/DBus"
41 #define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
42
43 static GDBusConnection *_gdbus_conn = NULL;
44 static int monitor_id = 0;
45 static int provider_monitor_id = 0;
46
47 typedef struct _shortcut_request_cb_info {
48         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);
49         void *data;
50 } shortcut_request_cb_info;
51
52 typedef struct _shortcut_remove_cb_info {
53         int (*remove_cb)(const char *package_name, const char *name, int sender_pid, void *user_data);
54         void *data;
55 } shortcut_remove_cb_info;
56
57 static shortcut_request_cb_info _request_callback_info;
58 static shortcut_remove_cb_info _remove_callback_info;
59
60 static void _shortcut_send_return(int ret_val, const char *request_id)
61 {
62         int ret;
63         GDBusMessage *reply = NULL;
64
65         ret = _dbus_init();
66         if (ret != SHORTCUT_ERROR_NONE) {
67                 SHORTCUT_ERR("Can't init dbus %d", ret);
68                 return;
69         }
70
71         _send_sync_shortcut(g_variant_new("(is)", ret_val, request_id),
72                             &reply,
73                             "send_return_value");
74         if (reply)
75                 g_object_unref(reply);
76 }
77
78 /* LCOV_EXCL_START */
79 static void _add_shortcut_notify(GVariant *parameters)
80 {
81         int ret = SHORTCUT_ERROR_NOT_SUPPORTED;
82         const char *appid;
83         const char *name;
84         int type;
85         const char *content;
86         const char *icon;
87         int allow_duplicate;
88         int sender_pid;
89         const char *request_id;
90
91         g_variant_get(parameters, "(&si&s&si&s&si)", &request_id, &sender_pid, &appid, &name, &type, &content, &icon, &allow_duplicate);
92         SHORTCUT_DBG("_add_shortcut_notify sender pid : [%d] appid : [%s]", sender_pid, appid);
93
94         if (_request_callback_info.request_cb != NULL)
95                 ret = _request_callback_info.request_cb(appid, name, type, content, icon, sender_pid, -1.0f, allow_duplicate, _request_callback_info.data);
96         else
97                 SHORTCUT_DBG("request_cb is null.");
98         _shortcut_send_return(ret, request_id);
99
100 }
101 /* LCOV_EXCL_STOP */
102
103 /* LCOV_EXCL_START */
104 static void _add_shortcut_widget_notify(GVariant *parameters)
105 {
106         int ret = SHORTCUT_ERROR_NOT_SUPPORTED;
107         const char *appid;
108         const char *name;
109         int type;
110         const char *content;
111         const char *icon;
112         int allow_duplicate;
113         int sender_pid;
114         double period;
115         const char *request_id;
116
117         g_variant_get(parameters, "(&si&s&si&s&sdi)", &request_id, &sender_pid, &appid, &name, &type, &content, &icon, &period, &allow_duplicate);
118         SHORTCUT_DBG("_add_shortcut_widget_notify sender pid : [%d] appid : [%s]", sender_pid, appid);
119
120         if (_request_callback_info.request_cb != NULL)
121                 ret = _request_callback_info.request_cb(appid, name, type, content, icon, sender_pid, period, allow_duplicate, _request_callback_info.data);
122         else
123                 SHORTCUT_DBG("request_cb is null.");
124         _shortcut_send_return(ret, request_id);
125 }
126 /* LCOV_EXCL_STOP */
127
128 static void _remove_shortcut_notify(GVariant *parameters)
129 {
130         int ret = SHORTCUT_ERROR_NOT_SUPPORTED;
131         const char *appid;
132         const char *name;
133         int sender_pid;
134         const char *request_id;
135
136         g_variant_get(parameters, "(&si&s&s)", &request_id, &sender_pid, &appid, &name);
137         SHORTCUT_DBG("_remove_shortcut_notify sender pid : [%d] appid : [%s]", sender_pid, appid);
138
139         if (_remove_callback_info.remove_cb != NULL)
140                 ret = _remove_callback_info.remove_cb(appid, name, sender_pid, _remove_callback_info.data);
141         else
142                 SHORTCUT_DBG("remove_cb is null.");
143         _shortcut_send_return(ret, request_id);
144 }
145
146 /* LCOV_EXCL_START */
147 static void _handle_shortcut_notify(GDBusConnection *connection,
148                 const gchar     *sender_name,
149                 const gchar     *object_path,
150                 const gchar     *interface_name,
151                 const gchar     *signal_name,
152                 GVariant        *parameters,
153                 gpointer         user_data)
154 {
155         SHORTCUT_DBG("signal_name : [%s]", signal_name);
156         if (g_strcmp0(signal_name, "add_shortcut_notify") == 0)
157                 _add_shortcut_notify(parameters);
158         else if (g_strcmp0(signal_name, "add_shortcut_widget_notify") == 0)
159                 _add_shortcut_widget_notify(parameters);
160         else if (g_strcmp0(signal_name, "remove_shortcut_notify") == 0)
161                 _remove_shortcut_notify(parameters);
162 }
163 /* LCOV_EXCL_STOP */
164
165 int _dbus_init(void)
166 {
167         GError *error = NULL;
168
169         if (_gdbus_conn == NULL) {
170                 _gdbus_conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
171
172                 if (_gdbus_conn == NULL) {
173                         /* LCOV_EXCL_START */
174                         if (error != NULL) {
175
176                                 SHORTCUT_ERR("Failed to get dbus [%s]", error->message);
177                                 g_error_free(error);
178
179                         }
180                         return SHORTCUT_ERROR_IO_ERROR;
181                         /* LCOV_EXCL_STOP */
182                 }
183                 shortcut_error_quark();
184         }
185
186         return SHORTCUT_ERROR_NONE;
187 }
188
189 int _dbus_signal_init()
190 {
191         int ret = SHORTCUT_ERROR_NONE;
192         int id;
193
194         if (monitor_id == 0) {
195                 id = g_dbus_connection_signal_subscribe(
196                                 _gdbus_conn,
197                                 PROVIDER_BUS_NAME,
198                                 PROVIDER_SHORTCUT_INTERFACE_NAME,       /*    interface */
199                                 NULL,                                   /*    member */
200                                 PROVIDER_OBJECT_PATH,                   /*    path */
201                                 NULL,                                   /*    arg0 */
202                                 G_DBUS_SIGNAL_FLAGS_NONE,
203                                 _handle_shortcut_notify,
204                                 NULL,
205                                 NULL);
206                 SHORTCUT_DBG("subscribe id : %d", id);
207                 if (id == 0) {
208                         /* LCOV_EXCL_START */
209                         ret = SHORTCUT_ERROR_IO_ERROR;
210                         SHORTCUT_ERR("Failed to _register_noti_dbus_interface");
211                         /* LCOV_EXCL_STOP */
212                 } else {
213                         SHORTCUT_INFO("get dbus connection success");
214                         monitor_id = id;
215                 }
216         }
217
218         return ret;
219 }
220
221 char *_shortcut_get_pkgname_by_pid(void)
222 {
223         char pkgname[SHORTCUT_PKGNAME_LEN + 1] = { 0, };
224         char buf[SHORTCUT_PKGNAME_LEN + 1] = { 0, };
225         int pid, ret;
226         int fd;
227         char *dup_pkgname;
228
229         pid = getpid();
230
231         ret = aul_app_get_pkgname_bypid(pid, pkgname, sizeof(pkgname));
232         if (ret != 0) {
233                 /* LCOV_EXCL_START */
234                 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid);
235
236                 fd = open(buf, O_RDONLY);
237                 if (fd < 0)
238                         return NULL;
239
240                 ret = read(fd, pkgname, sizeof(pkgname) - 1);
241                 close(fd);
242
243                 if (ret <= 0)
244                         return NULL;
245
246                 pkgname[ret] = '\0';
247                 /* LCOV_EXCL_STOP */
248                 /*!
249                  * \NOTE
250                  * "ret" is not able to be larger than "sizeof(pkgname) - 1",
251                  * if the system is not going wrong.
252                  */
253         } else {
254                 if (strlen(pkgname) <= 0)
255                         return NULL;
256         }
257
258         dup_pkgname = strdup(pkgname);
259         if (!dup_pkgname)
260                 SHORTCUT_ERR("Heap: %d\n", errno); /* LCOV_EXCL_LINE */
261
262         return dup_pkgname;
263 }
264
265 /*
266  * implement user request
267  */
268 int _send_sync_shortcut(GVariant *body, GDBusMessage **reply, char *cmd)
269 {
270         GError *err = NULL;
271         GDBusMessage *msg ;
272         int ret = SHORTCUT_ERROR_NONE;
273
274         msg = g_dbus_message_new_method_call(
275                         PROVIDER_BUS_NAME,
276                         PROVIDER_OBJECT_PATH,
277                         PROVIDER_SHORTCUT_INTERFACE_NAME,
278                         cmd);
279         if (!msg) {
280                 /* LCOV_EXCL_START */
281                 SHORTCUT_ERR("Can't allocate new method call");
282                 if (body)
283                         g_variant_unref(body);
284                 return SHORTCUT_ERROR_OUT_OF_MEMORY;
285                 /* LCOV_EXCL_STOP */
286         }
287
288         if (body != NULL)
289                 g_dbus_message_set_body(msg, body);
290
291         *reply = g_dbus_connection_send_message_with_reply_sync(
292                         _gdbus_conn,
293                         msg,
294                         G_DBUS_SEND_MESSAGE_FLAGS_NONE,
295                         -1,
296                         NULL,
297                         NULL,
298                         &err);
299
300         g_object_unref(msg);
301
302         if (!*reply) {
303                 /* LCOV_EXCL_START */
304                 ret = SHORTCUT_ERROR_COMM;
305                 if (err != NULL) {
306                         SHORTCUT_ERR("No reply. cmd = %s,  error = %s", cmd, err->message);
307                         if (err->code == G_DBUS_ERROR_ACCESS_DENIED)
308                                 ret = SHORTCUT_ERROR_PERMISSION_DENIED;
309                         g_error_free(err);
310                 }
311                 return ret;
312                 /* LCOV_EXCL_STOP */
313         }
314
315         if (g_dbus_message_to_gerror(*reply, &err)) {
316                 /* LCOV_EXCL_START */
317                 if (err->code == G_DBUS_ERROR_ACCESS_DENIED)
318                         ret = SHORTCUT_ERROR_PERMISSION_DENIED;
319                 else
320                         ret = err->code;
321
322                 SHORTCUT_ERR("_send_sync_shortcut error %s err code: %d", err->message, ret);
323                 g_error_free(err);
324                 return ret;
325                 /* LCOV_EXCL_STOP */
326         }
327
328         SHORTCUT_DBG("_send_sync_shortcut done !!");
329         return SHORTCUT_ERROR_NONE;
330 }
331
332 int _send_service_register()
333 {
334         GDBusMessage *reply = NULL;
335         int result;
336
337         result = _send_sync_shortcut(g_variant_new("(i)", getuid()), &reply, "shortcut_service_register");
338         SHORTCUT_ERR("_send_service_register done");
339         return result;
340 }
341
342 static void _send_message_with_reply_sync_cb(GDBusConnection *connection,
343                 GAsyncResult *res,
344                 gpointer user_data)
345 {
346         int result;
347         GError *err = NULL;
348         GDBusMessage *reply = NULL;
349         GVariant *body;
350         struct result_cb_item *cb_item = (struct result_cb_item *)user_data;
351
352         if (cb_item == NULL) {
353                 /* LCOV_EXCL_START */
354                 SHORTCUT_ERR("Failed to get a callback item");
355                 return;
356                 /* LCOV_EXCL_STOP */
357         }
358
359         reply = g_dbus_connection_send_message_with_reply_finish(
360                         connection,
361                         res,
362                         &err);
363
364         if (!reply) {
365                 /* LCOV_EXCL_START */
366                 if (err != NULL) {
367                         SHORTCUT_ERR("No reply. error = %s", err->message);
368                         g_error_free(err);
369                 }
370                 result = SHORTCUT_ERROR_COMM;
371                 /* LCOV_EXCL_STOP */
372         }
373
374         if (g_dbus_message_to_gerror(reply, &err)) {
375                 /* LCOV_EXCL_START */
376                 if (err->code == G_DBUS_ERROR_ACCESS_DENIED)
377                         result = SHORTCUT_ERROR_PERMISSION_DENIED;
378                 else
379                         result = err->code;
380
381                 SHORTCUT_ERR("_send_message_with_reply_sync_cb error %s err code: %d", err->message, result);
382                 g_error_free(err);
383                 /* LCOV_EXCL_STOP */
384         } else {
385                 body = g_dbus_message_get_body(reply);
386                 g_variant_get(body, "(i)", &result);
387         }
388
389         if (cb_item->result_internal_cb)
390                 cb_item->result_internal_cb(result, getpid(), cb_item->data); /* LCOV_EXCL_LINE */
391         else if (cb_item->result_cb)
392                 cb_item->result_cb(result, cb_item->data);
393
394         if (reply)
395                 g_object_unref(reply);
396
397         free(cb_item);
398 }
399
400 int _send_async_shortcut(GVariant *body, struct result_cb_item *cb_item, char *cmd)
401 {
402         GDBusMessage *msg;
403         msg = g_dbus_message_new_method_call(
404                         PROVIDER_BUS_NAME,
405                         PROVIDER_OBJECT_PATH,
406                         PROVIDER_SHORTCUT_INTERFACE_NAME,
407                         cmd);
408         if (!msg) {
409                 /* LCOV_EXCL_START */
410                 SHORTCUT_ERR("Can't allocate new method call");
411                 return SHORTCUT_ERROR_OUT_OF_MEMORY;
412                 /* LCOV_EXCL_STOP */
413         }
414
415         if (g_variant_is_floating(body))
416                  g_variant_ref(body);
417
418         if (body != NULL)
419                 g_dbus_message_set_body(msg, body);
420
421         g_dbus_connection_send_message_with_reply(
422                         _gdbus_conn,
423                         msg,
424                         G_DBUS_SEND_MESSAGE_FLAGS_NONE,
425                         -1,
426                         NULL,
427                         NULL,
428                         (GAsyncReadyCallback)_send_message_with_reply_sync_cb,
429                         cb_item);
430
431         if (msg)
432                 g_object_unref(msg);
433
434         SHORTCUT_DBG("_send_async_shortcut done !!");
435         return SHORTCUT_ERROR_NONE;
436 }
437
438 int _check_privilege(void)
439 {
440         GDBusMessage *reply = NULL;
441         int ret = SHORTCUT_ERROR_NONE;
442
443         ret = _send_sync_shortcut(NULL, &reply, "check_privilege");
444
445         if (reply)
446                 g_object_unref(reply);
447
448         return ret;
449 }
450
451
452 /* LCOV_EXCL_START */
453 static void _on_name_appeared(GDBusConnection *connection,
454                 const gchar     *name,
455                 const gchar     *name_owner,
456                 gpointer         user_data)
457 {
458         SHORTCUT_DBG("name appeared : %s", name);
459         _send_service_register();
460 }
461 /* LCOV_EXCL_STOP */
462
463 /* LCOV_EXCL_START */
464 static void _on_name_vanished(GDBusConnection *connection,
465                 const gchar     *name,
466                 gpointer         user_data)
467 {
468         SHORTCUT_DBG("name vanished : %s", name);
469 }
470 /* LCOV_EXCL_STOP */
471
472 void _ipc_monitor_fini(void)
473 {
474         if (provider_monitor_id) {
475                 g_bus_unwatch_name(provider_monitor_id);
476                 provider_monitor_id = 0;
477         }
478
479         if (monitor_id) {
480                 g_dbus_connection_signal_unsubscribe(_gdbus_conn, monitor_id);
481                 monitor_id = 0;
482         }
483
484         if (_gdbus_conn) {
485                 g_object_unref(_gdbus_conn);
486                 _gdbus_conn = NULL;
487         }
488
489 }
490
491 void _set_request_cb(shortcut_request_cb request_cb, void *data)
492 {
493         _request_callback_info.request_cb = request_cb;
494         _request_callback_info.data = data;
495 }
496
497 void _unset_request_cb(void)
498 {
499         if (_remove_callback_info.remove_cb == NULL &&
500                 _remove_callback_info.data == NULL)
501                 _ipc_monitor_fini();
502
503         _request_callback_info.request_cb = NULL;
504         _request_callback_info.data = NULL;
505 }
506
507 void _set_remove_cb(shortcut_remove_cb remove_cb, void *data)
508 {
509         _remove_callback_info.remove_cb = remove_cb;
510         _remove_callback_info.data = data;
511 }
512
513 void _unset_remove_cb(void)
514 {
515         if (_request_callback_info.request_cb == NULL &&
516                 _request_callback_info.data == NULL)
517                 _ipc_monitor_fini();
518
519         _remove_callback_info.remove_cb = NULL;
520         _remove_callback_info.data = NULL;
521 }
522
523 int _dbus_set_watch_name()
524 {
525         if (provider_monitor_id == 0) {
526                 provider_monitor_id = g_bus_watch_name_on_connection(
527                                 _gdbus_conn,
528                                 PROVIDER_BUS_NAME,
529                                 G_BUS_NAME_WATCHER_FLAGS_NONE,
530                                 _on_name_appeared,
531                                 _on_name_vanished,
532                                 NULL,
533                                 NULL);
534
535                 if (provider_monitor_id == 0) {
536                         /* LCOV_EXCL_START */
537                         SHORTCUT_ERR("watch on name fail");
538                         return SHORTCUT_ERROR_IO_ERROR;
539                         /* LCOV_EXCL_STOP */
540                 }
541         }
542
543         return SHORTCUT_ERROR_NONE;
544 }