add project name as argument for create token
[apps/native/tizen-things-daemon.git] / lib / things-service / src / things-service.c
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Flora License, Version 1.1 (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://floralicense.org/license/
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 <sys/types.h>
18 #include <unistd.h>
19 #include <time.h>
20 #include <glib.h>
21 #include <gio/gio.h>
22 #include <gio/gunixsocketaddress.h>
23 #include <app_common.h>
24 #include <json.h>
25 #include "log-private.h"
26 #include "things-service.h"
27 #include "common-app-inf.h"
28 #include "common-util.h"
29
30 #define TEST_DEVICE_ID "test-page-device"
31
32 struct _ts_data {
33         char *app_id;
34         char *token;
35         char *project;
36         GSocket *socket;
37         GSocketAddress *addr;
38         GSocket *cl_socket;
39         GDBusConnection *bus;
40         GDBusProxy *proxy;
41         GThread *accept_thread;
42         int accept_thread_running;
43 };
44
45 struct __timebased_data {
46         ts_value_type_e type;
47         unsigned int length;
48         struct json_object *arr_obj;
49 };
50
51 static void __quit_n_join_accept_thread(ts_handle handle);
52 static int _create_accept_thread(ts_handle handle);
53
54 static char *_get_socket_addr_name(const char *appID, const char *token)
55 {
56         return common_make_socket_addr_name(token, appID, (guint)getpid());
57 }
58
59 static char *_get_app_id(void)
60 {
61         char *app_id = NULL;
62         app_get_id(&app_id);
63
64         return app_id;
65 }
66
67 static char *_get_token(const char *appID, const char *project)
68 {
69         char *checksum = NULL;
70         char *key = NULL;
71
72         key = g_strdup_printf("%s-%s-%u-%lld",
73                         appID, project, getpid(), common_get_monotonic_coarse_time());
74         checksum = g_compute_checksum_for_string(G_CHECKSUM_MD5, key, -1);
75         g_free(key);
76
77         return checksum;
78 }
79
80 static gboolean
81 __on_disconnect(GIOChannel *ch, GIOCondition cond, gpointer data)
82 {
83         ts_handle handle = data;
84         gboolean connected = FALSE;
85
86         connected = g_socket_is_connected(handle->cl_socket);
87         if (!connected)
88                 _E("client socket is disconnected");
89
90         if (cond == G_IO_ERR)
91                 _E("connection has error");
92
93         if (cond == G_IO_HUP)
94                 _E("connection has been broken");
95
96         g_socket_shutdown(handle->cl_socket, FALSE, TRUE, NULL);
97         g_socket_close(handle->cl_socket, NULL);
98         g_object_unref(handle->cl_socket);
99
100         handle->cl_socket = NULL;
101
102         _create_accept_thread(handle);
103
104         return FALSE;
105 }
106
107 static void _ts_handle_free(ts_handle handle)
108 {
109         ret_if(!handle);
110
111         __quit_n_join_accept_thread(handle);
112
113         if (handle->app_id)
114                 g_free(handle->app_id);
115
116         if (handle->project)
117                 g_free(handle->project);
118
119         if (handle->token)
120                 g_free(handle->token);
121
122         if (handle->socket) {
123                 g_socket_close(handle->socket, NULL);
124                 g_object_unref(handle->socket);
125         }
126
127         if (handle->addr)
128                 g_object_unref(handle->addr);
129
130         if (handle->bus)
131                 g_object_unref(handle->bus);
132
133         if (handle->proxy)
134                 g_object_unref(handle->proxy);
135
136         g_free(handle);
137 }
138
139 static void __quit_n_join_accept_thread(ts_handle handle)
140 {
141         if (handle->accept_thread) {
142                 g_atomic_int_set(&(handle->accept_thread_running), 0);
143                 g_thread_join(handle->accept_thread);
144                 handle->accept_thread = NULL;
145         }
146 }
147
148 static gpointer accept_thread(gpointer data)
149 {
150         ts_handle handle = data;
151         retv_if(!handle, NULL);
152
153         while (TRUE) {
154                 GSocket *new_socket = NULL;
155                 GIOChannel *new_ch = NULL;
156                 int fd = 0;
157                 GError *error = NULL;
158
159                 if (!g_atomic_int_get(&(handle->accept_thread_running)))
160                         break;
161
162                 new_socket = g_socket_accept(handle->socket, NULL, &error);
163                 if (!new_socket) {
164                         if (error) {
165                                 if (error->code !=  G_IO_ERROR_WOULD_BLOCK)
166                                         _E("err on g_socket_accept() - %s", error->message);
167
168                                 g_error_free(error);
169                         }
170                         continue;
171                 }
172
173                 _D("accepted new socket[%p]", new_socket);
174                 fd = g_socket_get_fd(new_socket);
175                 new_ch = g_io_channel_unix_new(fd);
176                 g_io_add_watch(new_ch, (GIOCondition) (G_IO_ERR | G_IO_HUP),
177                         __on_disconnect, handle);
178                 handle->cl_socket = new_socket;
179                 break; /* accept and exit thread */
180         }
181         _D("exiting accept thread");
182
183         return NULL;
184 }
185
186 static int _create_accept_thread(ts_handle handle)
187 {
188         GThread *new_thread = NULL;
189         GError *error = NULL;
190
191         retv_if(!handle, -1);
192
193         if (handle->accept_thread)
194                 __quit_n_join_accept_thread(handle);
195
196         g_atomic_int_set(&(handle->accept_thread_running), 1);
197         new_thread = g_thread_try_new(NULL, accept_thread, handle, &error);
198         if (!new_thread) {
199                 _E("failed to create accept thread - %s", error->message);
200                 g_error_free(error);
201                 return -1;
202         }
203         handle->accept_thread = new_thread;
204
205         return 0;
206 }
207
208 static int __client_method_call(const char *method, ts_handle handle)
209 {
210         GVariant *response = NULL;
211         int ret = 0;
212         const char *ret_msg = 0;
213         GError *error = NULL;
214
215         _D("call method [%s]", method);
216         response = g_dbus_proxy_call_sync(handle->proxy,
217                 method,
218                 g_variant_new("(sssu)",
219                         handle->project, handle->token, handle->app_id, (guint)getpid()),
220                 G_DBUS_CALL_FLAGS_NONE,
221                 -1, /* The timeout in milliseconds or -1 to use the proxy default timeout. */
222                 NULL, /* cancellable */
223                 &error);
224         if (error) {
225                 _E("failed to g_dbus_proxy_call_sync() - %s", error->message);
226                 g_error_free(error);
227                 return -1;
228         }
229         if (!response)
230                 return -1;
231
232         g_variant_get(response, "(i&s)", ret, &ret_msg);
233         _D("method[%s] get response ret[%d] - %s", method, ret, ret_msg);
234         g_variant_unref(response);
235
236         return ret;
237 }
238
239 static int _client_call_register(ts_handle handle)
240 {
241         return __client_method_call(TTD_APP_INF_METHOD_REG, handle);
242 }
243
244 static int _client_call_unregister(ts_handle handle)
245 {
246         return __client_method_call(TTD_APP_INF_METHOD_UNREG, handle);
247 }
248
249 int things_service_init(ts_handle *handle, const char *project)
250 {
251         ts_handle _handle = NULL;
252         GError *error = NULL;
253         char *socket_name = NULL;
254
255         retv_if(!handle, -1);
256
257         _handle = g_try_malloc0(sizeof(struct _ts_data));
258         _handle->project = g_strdup(project);
259         _handle->app_id = _get_app_id();
260         if (!_handle->app_id) {
261                 _E("failed to get app id");
262                 _ts_handle_free(_handle);
263                 return -1;
264         }
265         _D("libthings_service with - project[%s], app[%s]", _handle->app_id);
266
267         _handle->token = _get_token(_handle->app_id, project);
268         if (!_handle->token) {
269                 _E("failed to get token");
270                 _ts_handle_free(_handle);
271                 return -1;
272         }
273         _D("get token - %s", _handle->token);
274         _handle->cl_socket = NULL;
275         _handle->socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
276                                 G_SOCKET_TYPE_STREAM,
277                                 G_SOCKET_PROTOCOL_DEFAULT,
278                                 &error);
279         if (!_handle->socket) {
280                 _E("failed to create socket - %s", error->message);
281                 g_error_free(error);
282                 _ts_handle_free(_handle);
283                 return -1;
284         }
285         g_socket_set_blocking(_handle->socket, FALSE);
286
287         socket_name = _get_socket_addr_name(_handle->app_id, _handle->token);
288         if (!socket_name) {
289                 _E("failed to get socket_name");
290                 _ts_handle_free(_handle);
291                 return -1;
292         }
293         _handle->addr = g_unix_socket_address_new_with_type(
294                         socket_name, -1, G_UNIX_SOCKET_ADDRESS_ABSTRACT);
295         _D("got socket addr - [%s]", socket_name);
296         g_free(socket_name);
297
298         g_socket_bind(_handle->socket, _handle->addr, TRUE, &error);
299         if (error) {
300                 _E("failed to g_socket_bind() - %s", error->message);
301                 g_error_free(error);
302                 _ts_handle_free(_handle);
303                 return -1;
304         }
305
306         g_socket_listen(_handle->socket, &error);
307         if (error) {
308                 _E("failed to g_socket_listen() - %s", error->message);
309                 g_error_free(error);
310                 _ts_handle_free(_handle);
311                 return -1;
312         }
313
314         _handle->bus =
315                 g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
316         if (!_handle->bus) {
317                 _E("failed to g_bus_get_sync() - %s", error->message);
318                 g_error_free(error);
319                 _ts_handle_free(_handle);
320                 return -1;
321         }
322
323         _handle->proxy = g_dbus_proxy_new_sync(_handle->bus,
324                 G_DBUS_PROXY_FLAGS_NONE,
325                 NULL, /* GDBusInterfaceInfo */
326                 TTD_APP_INF_BUS_NAME,
327                 TTD_APP_INF_OBJECT_PATH,
328                 TTD_APP_INF_BUS_INF,
329                 NULL, /* cancellable */
330                 &error);
331         if (!_handle->proxy) {
332                 _E("failed to g_dbus_proxy_new_sync() - %s",
333                         error ? error->message : "error not set");
334                 _ts_handle_free(_handle);
335                 return -1;
336         }
337
338         if (_create_accept_thread(_handle)) {
339                 _E("failed to _create_accept_thread()");
340                 _ts_handle_free(_handle);
341                 return -1;
342         }
343
344         if (_client_call_register(_handle)) {
345                 _E("failed to call register");
346                 _ts_handle_free(_handle);
347                 return -1;
348         }
349
350         *handle = _handle;
351
352         return 0;
353 }
354
355 int things_service_fini(ts_handle handle)
356 {
357         retv_if(!handle, -1);
358
359         _client_call_unregister(handle);
360         _ts_handle_free(handle);
361
362         return 0;
363 }
364
365 static int __ts_send_data(ts_handle handle, const char *json_data)
366 {
367         gboolean connected = FALSE;
368         char *msg = NULL;
369         GError *error = NULL;
370
371         retv_if(!handle, -1);
372         retv_if(!handle->socket, -1);
373         retv_if(!handle->cl_socket, -1);
374         retv_if(!json_data, -1);
375
376         connected = g_socket_is_connected(handle->cl_socket);
377         retv_if(!connected, -1);
378
379         msg = g_strdup_printf("%s %s", json_data, TTD_APP_MSG_DELIMITER);
380         g_socket_send(handle->cl_socket, msg, strlen(msg), NULL, &error);
381         if (error) {
382                 _E("failed to g_socket_send() - %s", error->message);
383                 g_error_free(error);
384                 return -1;
385         }
386         _D("msg is sent - %s", msg);
387         g_free(msg);
388
389         return 0;
390 }
391
392 int things_service_send_data(ts_handle handle, const char *json_data)
393 {
394         return __ts_send_data(handle, json_data);
395 }
396
397 timebased_data *things_service_timebased_data_new(ts_value_type_e type)
398 {
399         timebased_data *tb_data = NULL;
400
401         retvm_if(!((type == TS_VALUE_TYPE_INT) || (type == TS_VALUE_TYPE_DOUBLE)),
402                 NULL, "invalid type[%d]", type);
403
404         tb_data = g_try_malloc(sizeof(timebased_data));
405         retv_if(!tb_data, NULL);
406
407         tb_data->type = type;
408         tb_data->length = 0;
409         tb_data->arr_obj = NULL;
410
411         return tb_data;
412 }
413
414 int things_service_timebased_data_reset(
415         timebased_data *tb_data, ts_value_type_e type)
416 {
417         retv_if(!tb_data, -1);
418
419         retvm_if(!((type == TS_VALUE_TYPE_INT) || (type == TS_VALUE_TYPE_DOUBLE)),
420                 -1, "invalid type[%d]", type);
421
422         tb_data->type = type;
423         tb_data->length = 0;
424
425         if (tb_data->arr_obj) {
426                 json_object_put(tb_data->arr_obj);
427                 tb_data->arr_obj = NULL;
428         }
429         return 0;
430 }
431
432
433 void things_service_timebased_data_free(timebased_data *tb_data)
434 {
435         ret_if(!tb_data);
436
437         if (tb_data->arr_obj)
438                 json_object_put(tb_data->arr_obj);
439
440         g_free(tb_data);
441 }
442
443 int things_service_timebased_data_get_length(timebased_data *tb_data)
444 {
445         retv_if(!tb_data, -1);
446
447         return tb_data->length;
448 }
449
450 int
451 things_service_timebased_data_append_int(timebased_data *tb_data, int ivalue)
452 {
453         struct json_object *tmp_o = NULL;
454         struct json_object *time_o = NULL;
455         struct json_object *value_o = NULL;
456
457         retv_if(!tb_data, -1);
458         retv_if(tb_data->type != TS_VALUE_TYPE_INT, -1);
459
460         if (!tb_data->arr_obj) {
461                 struct json_object *obj = NULL;
462                 obj = json_object_new_array();
463                 retvm_if(!obj, -1, "failed to create json arry object");
464                 tb_data->arr_obj = obj;
465         }
466
467         tmp_o = json_object_new_object();
468         retvm_if (!tmp_o, -1, "failed to create json object");
469
470         time_o = json_object_new_int64(common_get_epoch_coarse_time());
471         goto_if(!time_o, ERROR);
472         value_o = json_object_new_int64(ivalue);
473         goto_if(!value_o, ERROR);
474
475         json_object_object_add(tmp_o, "x", time_o);
476         json_object_object_add(tmp_o, "y", value_o);
477         json_object_array_add(tb_data->arr_obj, tmp_o);
478
479         tb_data->length++;
480
481         return 0;
482
483 ERROR:
484         if (tmp_o)
485                 json_object_put(tmp_o);
486
487         if (time_o)
488                 json_object_put(time_o);
489
490         return -1;
491 }
492
493 int things_service_timebased_data_append_double(
494         timebased_data *tb_data, double dvalue)
495 {
496         struct json_object *tmp_o = NULL;
497         struct json_object *time_o = NULL;
498         struct json_object *value_o = NULL;
499
500         retv_if(!tb_data, -1);
501         retv_if(tb_data->type != TS_VALUE_TYPE_DOUBLE, -1);
502
503         if (!tb_data->arr_obj) {
504                 struct json_object *obj = NULL;
505                 obj = json_object_new_array();
506                 retvm_if(!obj, -1, "failed to create json arry object");
507                 tb_data->arr_obj = obj;
508         }
509
510         tmp_o = json_object_new_object();
511         retvm_if (!tmp_o, -1, "failed to create json object");
512
513         time_o = json_object_new_int64(common_get_epoch_coarse_time());
514         goto_if(!time_o, ERROR);
515         value_o = json_object_new_double(dvalue);
516         goto_if(!value_o, ERROR);
517
518         json_object_object_add(tmp_o, "x", time_o);
519         json_object_object_add(tmp_o, "y", value_o);
520         json_object_array_add(tb_data->arr_obj, tmp_o);
521
522         tb_data->length++;
523
524         return 0;
525
526 ERROR:
527         if (tmp_o)
528                 json_object_put(tmp_o);
529
530         if (time_o)
531                 json_object_put(time_o);
532
533         return -1;
534 }
535
536 int things_service_send_timebased_data(
537         ts_handle handle, timebased_data *tb_data, const char *project_key)
538 {
539         int ret = 0;
540         struct json_object *msg_o = NULL;
541         struct json_object *key_o = NULL;
542
543         retv_if(!handle, -1);
544         retv_if(!tb_data, -1);
545         retv_if(!tb_data->arr_obj, -1);
546         retv_if(!project_key, -1);
547
548         msg_o = json_object_new_object();
549         retvm_if(!msg_o, -1, "failed to create json object for message");
550
551         key_o = json_object_new_string(project_key);
552         if (!key_o) {
553                 _E("failed to create json object for project_key");
554                 json_object_put(msg_o);
555                 return -1;
556         }
557
558         json_object_object_add(msg_o, "key", key_o);
559         json_object_object_add(msg_o, "data", tb_data->arr_obj);
560 #ifdef TEST_DEVICE_ID
561 /* only for test, SHOULD BE REMOVED after device auth method is applied on cloud */
562         struct json_object *did_o = json_object_new_string(TEST_DEVICE_ID);
563         json_object_object_add(msg_o, "deviceId", did_o);
564 #endif /* TEST_DEVICE_ID */
565
566         ret  = __ts_send_data(handle, json_object_to_json_string(msg_o));
567         if (ret) {
568                 _E("failed to send data");
569                 /* keep appended data */
570                 json_object_get(tb_data->arr_obj);
571         } else { /* if success to send data, remove all items in tb_data */
572                 tb_data->arr_obj = NULL;
573                 tb_data->length = 0;
574         }
575
576         json_object_put(msg_o);
577
578         return ret;
579 }