Modify gdbus structure(related to CRUD) and gvariant builder
[platform/core/iot/iotcon.git] / daemon / icd-ioty-ocprocess.c
1 /*
2  * Copyright (c) 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 <stdlib.h>
18 #include <unistd.h> /* for usleep() */
19 #include <glib.h>
20 #include <json-glib/json-glib.h>
21
22 #include <ocstack.h>
23
24 #include "iotcon.h"
25 #include "ic-utils.h"
26 #include "icd.h"
27 #include "icd-dbus.h"
28 #include "icd-ioty.h"
29 #include "icd-ioty-ocprocess.h"
30
31 static int icd_ioty_alive;
32
33 typedef int (*_ocprocess_fn)(void *user_data);
34
35 struct icd_ioty_worker
36 {
37         void *ctx;
38         _ocprocess_fn fn;
39 };
40
41
42 enum _icd_secure_type
43 {
44         ICD_TRANSPORT_IPV4_SECURE,
45         ICD_TRANSPORT_IPV4
46 };
47
48
49 struct icd_req_context {
50         unsigned int signum;
51         char *bus_name;
52         char *payload;
53         int types;
54         int observer_id;
55         int observe_action;
56         OCRequestHandle request_h;
57         OCResourceHandle resource_h;
58         GVariantBuilder *options;
59         GVariantBuilder *query;
60 };
61
62
63 struct icd_find_context {
64         unsigned int signum;
65         char *bus_name;
66         char *payload;
67         OCDevAddr *dev_addr;
68         int conn_type;
69 };
70
71
72 struct icd_get_context {
73         int res;
74         char *payload;
75         GVariantBuilder *options;
76         GDBusMethodInvocation *invocation;
77 };
78
79
80 void icd_ioty_ocprocess_stop()
81 {
82         icd_ioty_alive = 0;
83 }
84
85 static void* _ocprocess_worker_thread(void *data)
86 {
87         int ret;
88         struct icd_ioty_worker *worker = data;
89
90         if (NULL == data) {
91                 ERR("worker is NULL");
92                 return NULL;
93         }
94
95         ret = worker->fn(worker->ctx);
96         if (IOTCON_ERROR_NONE != ret)
97                 ERR("fn() Fail(%d)", ret);
98
99         /* worker was allocated from _ocprocess_worker_start() */
100         free(worker);
101
102         /* GCC warning happen if use g_thread_exit() */
103         return NULL;
104 }
105
106
107 static int _ocprocess_worker_start(_ocprocess_fn fn, void *ctx)
108 {
109         GError *error;
110         GThread *thread;
111         struct icd_ioty_worker *worker;
112
113         RETV_IF(NULL == fn, IOTCON_ERROR_INVALID_PARAMETER);
114
115         worker = calloc(1, sizeof(struct icd_ioty_worker));
116         if (NULL == worker) {
117                 ERR("calloc() Fail(%d)", errno);
118                 return IOTCON_ERROR_OUT_OF_MEMORY;
119         }
120
121         worker->fn = fn;
122         worker->ctx = ctx;
123
124         /* TODO : consider thread pool mechanism */
125         thread = g_thread_try_new("worker_thread", _ocprocess_worker_thread, worker, &error);
126         if (NULL == thread) {
127                 ERR("g_thread_try_new() Fail(%s)", error->message);
128                 g_error_free(error);
129                 free(worker);
130                 return IOTCON_ERROR_SYSTEM;
131         }
132
133         /* DO NOT join thread. It was already detached by calling g_thread_unref() */
134         g_thread_unref(thread);
135
136         /* DO NOT FREE worker. It MUST be freed in the _ocprocess_worker_thread() */
137
138         return IOTCON_ERROR_NONE;
139 }
140
141
142 static int _ocprocess_response_signal(const char *dest, const char *signal,
143                 unsigned int signum, GVariant *value)
144 {
145         int ret;
146         char sig_name[IC_DBUS_SIGNAL_LENGTH] = {0};
147
148         ret = snprintf(sig_name, sizeof(sig_name), "%s_%u", signal, signum);
149         if (ret <= 0 || sizeof(sig_name) <= ret) {
150                 ERR("snprintf() Fail(%d)", ret);
151                 return IOTCON_ERROR_UNKNOWN;
152         }
153
154         ret = icd_dbus_emit_signal(dest, sig_name, value);
155         if (IOTCON_ERROR_NONE != ret) {
156                 ERR("icd_dbus_emit_signal() Fail(%d)", ret);
157                 return ret;
158         }
159
160         return IOTCON_ERROR_NONE;
161 }
162
163
164 static inline GVariantBuilder* _ocprocess_parse_header_options(
165                 OCHeaderOption *oic_option, int option_size)
166 {
167         int i;
168         GVariantBuilder *options;
169
170         options = g_variant_builder_new(G_VARIANT_TYPE("a(qs)"));
171         for (i = 0; i < option_size; i++) {
172                 g_variant_builder_add(options, "(qs)", oic_option[i].optionID,
173                                 oic_option[i].optionData);
174         }
175
176         return options;
177 }
178
179
180 static int _worker_req_handler(void *context)
181 {
182         int ret;
183         GVariant *value;
184         struct icd_req_context *ctx = context;
185
186         RETV_IF(NULL == ctx, IOTCON_ERROR_INVALID_PARAMETER);
187
188         value = g_variant_new("(ia(qs)a(ss)iisii)",
189                         ctx->types,
190                         ctx->options,
191                         ctx->query,
192                         ctx->observe_action,
193                         ctx->observer_id,
194                         ctx->payload,
195                         GPOINTER_TO_INT(ctx->request_h),
196                         GPOINTER_TO_INT(ctx->resource_h));
197
198         ret = _ocprocess_response_signal(ctx->bus_name, IC_DBUS_SIGNAL_REQUEST_HANDLER,
199                         ctx->signum, value);
200         if (IOTCON_ERROR_NONE != ret)
201                 ERR("_ocprocess_response_signal() Fail(%d)", ret);
202
203         free(ctx->bus_name);
204         free(ctx->payload);
205         g_variant_builder_unref(ctx->options);
206         g_variant_builder_unref(ctx->query);
207         free(ctx);
208
209         return ret;
210 }
211
212
213 OCEntityHandlerResult icd_ioty_ocprocess_req_handler(OCEntityHandlerFlag flag,
214                 OCEntityHandlerRequest *request)
215 {
216         int ret;
217         unsigned int signal_number;
218         char *query_str, *query_key, *query_value;
219         char *token, *save_ptr1, *save_ptr2;
220         char *bus_name = NULL;
221         struct icd_req_context *req_ctx;
222
223         RETV_IF(NULL == request, OC_EH_ERROR);
224
225         req_ctx = calloc(1, sizeof(struct icd_req_context));
226         if (NULL == req_ctx) {
227                 ERR("calloc() Fail(%d)", errno);
228                 return OC_EH_ERROR;
229         }
230
231         /* handle */
232         req_ctx->request_h = request->requestHandle;
233         req_ctx->resource_h = request->resource;
234
235         ret = icd_dbus_client_list_get_info(req_ctx->resource_h, &signal_number, &bus_name);
236         if (IOTCON_ERROR_NONE != ret) {
237                 ERR("icd_dbus_client_list_get_info() Fail(%d)", ret);
238                 free(req_ctx);
239                 return OC_EH_ERROR;
240         }
241
242         /* signal number & bus_name */
243         req_ctx->signum = signal_number;
244         req_ctx->bus_name = bus_name;
245
246         /* request type */
247         if (OC_REQUEST_FLAG & flag) {
248                 switch (request->method) {
249                 case OC_REST_GET:
250                         req_ctx->types = IOTCON_REQUEST_GET;
251                         req_ctx->payload = strdup(IC_STR_NULL);
252
253                         if (OC_OBSERVE_FLAG & flag) {
254                                 req_ctx->types |= IOTCON_REQUEST_OBSERVE;
255                                 /* observation info*/
256                                 req_ctx->observer_id = request->obsInfo.obsId;
257                                 req_ctx->observe_action = request->obsInfo.action;
258                         }
259                         break;
260                 case OC_REST_PUT:
261                         req_ctx->types = IOTCON_REQUEST_PUT;
262                         req_ctx->payload = ic_utils_strdup(request->reqJSONPayload);
263                         break;
264                 case OC_REST_POST:
265                         req_ctx->types = IOTCON_REQUEST_POST;
266                         req_ctx->payload = ic_utils_strdup(request->reqJSONPayload);
267                         break;
268                 case OC_REST_DELETE:
269                         req_ctx->types = IOTCON_REQUEST_DELETE;
270                         req_ctx->payload = strdup(IC_STR_NULL);
271                         break;
272                 default:
273                         free(req_ctx->bus_name);
274                         free(req_ctx);
275                         return OC_EH_ERROR;
276                 }
277         }
278
279         /* header options */
280         req_ctx->options = _ocprocess_parse_header_options(
281                         request->rcvdVendorSpecificHeaderOptions,
282                         request->numRcvdVendorSpecificHeaderOptions);
283
284         /* query */
285         req_ctx->query = g_variant_builder_new(G_VARIANT_TYPE("a(ss)"));
286         query_str = request->query;
287         while ((token = strtok_r(query_str, "&", &save_ptr1))) {
288                 while ((query_key = strtok_r(token, "=", &save_ptr2))) {
289                         token = NULL;
290                         query_value = strtok_r(token, "=", &save_ptr2);
291                         if (NULL == query_value)
292                                 break;
293
294                         g_variant_builder_add(req_ctx->query, "(ss)", query_key, query_value);
295                 }
296                 query_str = NULL;
297         }
298
299         ret = _ocprocess_worker_start(_worker_req_handler, req_ctx);
300         if (IOTCON_ERROR_NONE != ret) {
301                 ERR("_ocprocess_worker_start() Fail(%d)", ret);
302                 free(req_ctx->bus_name);
303                 free(req_ctx->payload);
304                 g_variant_builder_unref(req_ctx->options);
305                 g_variant_builder_unref(req_ctx->query);
306                 free(req_ctx);
307                 return OC_EH_ERROR;
308         }
309
310         /* DO NOT FREE req_ctx. It MUST be freed in the _worker_req_handler func */
311
312         return OC_EH_OK;
313 }
314
315
316 gpointer icd_ioty_ocprocess_thread(gpointer data)
317 {
318         FN_CALL;
319         OCStackResult result;
320
321         icd_ioty_alive = 1;
322         while (icd_ioty_alive) {
323                 icd_ioty_csdk_lock();
324                 result = OCProcess();
325                 icd_ioty_csdk_unlock();
326                 if (OC_STACK_OK != result) {
327                         ERR("OCProcess() Fail(%d)", result);
328                         break;
329                 }
330
331                 /* TODO : SHOULD revise time or usleep */
332                 usleep(10);
333         }
334
335         return NULL;
336 }
337
338
339 /*
340  * returned string SHOULD be released by you
341  */
342 static inline char* _find_cb_get_address(OCDevAddr *address, int sec_type, int sec_port)
343 {
344         FN_CALL;
345         int ret;
346         uint16_t port;
347         uint8_t a, b, c, d;
348         char addr[1024] = {0};
349
350         RETVM_IF(ICD_TRANSPORT_IPV4 != sec_type && ICD_TRANSPORT_IPV4_SECURE != sec_type,
351                         NULL, "Invalid secure type(%d)", sec_type);
352
353         ret = OCDevAddrToIPv4Addr(address, &a, &b, &c, &d);
354         if (OC_STACK_OK != ret) {
355                 ERR("OCDevAddrToIPv4Addr() Fail(%d)", ret);
356                 return NULL;
357         }
358
359         if (ICD_TRANSPORT_IPV4_SECURE == sec_type) {
360                 if (sec_port <= 0 || 65535 < sec_port) {
361                         SECURE_ERR("Invalid secure port(%d)", sec_port);
362                         return NULL;
363                 }
364
365                 ret = snprintf(addr, sizeof(addr), ICD_IOTY_COAPS"%d.%d.%d.%d:%d", a, b, c, d,
366                                 sec_port);
367         } else {
368                 ret = OCDevAddrToPort(address, &port);
369                 if (OC_STACK_OK != ret) {
370                         ERR("OCDevAddrToPort() Fail(%d)", ret);
371                         return NULL;
372                 }
373
374                 ret = snprintf(addr, sizeof(addr), ICD_IOTY_COAP"%d.%d.%d.%d:%d", a, b, c, d,
375                                 port);
376         }
377
378         WARN_IF(ret <= 0 || sizeof(addr) <= ret, "snprintf() Fail(%d)", ret);
379
380         return ic_utils_strdup(addr);
381 }
382
383
384 static inline int _find_cb_response(JsonObject *rsrc_obj,
385                 struct icd_find_context *ctx)
386 {
387         GVariant *value;
388         JsonGenerator *gen;
389         JsonNode *root_node;
390         char *host, *json_data;
391         JsonObject *property_obj;
392         int ret, secure, secure_type, secure_port;
393
394         RETV_IF(NULL == rsrc_obj, IOTCON_ERROR_INVALID_PARAMETER);
395         RETV_IF(NULL == ctx, IOTCON_ERROR_INVALID_PARAMETER);
396
397         /* parse secure secure_port */
398         property_obj = json_object_get_object_member(rsrc_obj, IC_JSON_KEY_PROPERTY);
399         if (NULL == property_obj) {
400                 ERR("json_object_get_object_member() Fail");
401                 return IOTCON_ERROR_INVALID_PARAMETER;
402         }
403
404         secure = json_object_get_int_member(property_obj, IC_JSON_KEY_SECURE);
405         if (0 == secure) {
406                 secure_type = ICD_TRANSPORT_IPV4;
407                 secure_port = 0;
408         } else {
409                 secure_type = ICD_TRANSPORT_IPV4_SECURE;
410                 secure_port = json_object_get_int_member(property_obj, IC_JSON_KEY_PORT);
411         }
412
413         host = _find_cb_get_address(ctx->dev_addr, secure_type, secure_port);
414         if (NULL == host) {
415                 ERR("_find_cb_get_address() Fail");
416                 return IOTCON_ERROR_IOTIVITY;
417         }
418
419         gen = json_generator_new();
420         root_node = json_node_new(JSON_NODE_OBJECT);
421         json_node_set_object(root_node, rsrc_obj);
422         json_generator_set_root(gen, root_node);
423
424         json_data = json_generator_to_data(gen, NULL);
425         json_node_free(root_node);
426         g_object_unref(gen);
427
428         value = g_variant_new("(ssi)", json_data, host, ctx->conn_type);
429         free(json_data);
430         free(host);
431
432         /* TODO : If one device has multi resources, it comes as bulk data.
433          * To reduce the number of emit_signal, let's send signal only one time for one device.
434          * for ex, client list. */
435         ret = _ocprocess_response_signal(ctx->bus_name, IC_DBUS_SIGNAL_FOUND_RESOURCE,
436                         ctx->signum, value);
437         if (IOTCON_ERROR_NONE != ret) {
438                 ERR("_ocprocess_response_signal() Fail(%d)", ret);
439                 return ret;
440         }
441
442         return IOTCON_ERROR_NONE;
443 }
444
445
446 static inline int _find_cb_handle_context(struct icd_find_context *ctx)
447 {
448         int ret;
449         JsonParser *parser;
450         GError *error = NULL;
451         JsonObject *root_obj;
452         JsonArray *rsrc_array;
453         unsigned int rsrc_count, rsrc_index;
454
455         RETV_IF(NULL == ctx, IOTCON_ERROR_INVALID_PARAMETER);
456         RETV_IF(NULL == ctx->payload, IOTCON_ERROR_INVALID_PARAMETER);
457
458         parser = json_parser_new();
459         ret = json_parser_load_from_data(parser, ctx->payload, strlen(ctx->payload), &error);
460         if (FALSE == ret) {
461                 ERR("json_parser_load_from_data() Fail(%s)", error->message);
462                 g_error_free(error);
463                 return IOTCON_ERROR_INVALID_PARAMETER;
464         }
465
466         /* parse 'oc' prefix */
467         root_obj = json_node_get_object(json_parser_get_root(parser));
468         rsrc_array = json_object_get_array_member(root_obj, IC_JSON_KEY_OC);
469         if (NULL == rsrc_array) {
470                 ERR("json_object_get_array_member() Fail");
471                 g_object_unref(parser);
472                 return IOTCON_ERROR_INVALID_PARAMETER;
473         }
474
475         rsrc_count = json_array_get_length(rsrc_array);
476         if (0 == rsrc_count) {
477                 ERR("Invalid count(%d)", rsrc_count);
478                 g_object_unref(parser);
479                 return IOTCON_ERROR_INVALID_PARAMETER;
480         }
481
482         for (rsrc_index = 0; rsrc_index < rsrc_count; rsrc_index++) {
483                 JsonObject *rsrc_obj = json_array_get_object_element(rsrc_array, rsrc_index);
484
485                 ret = _find_cb_response(rsrc_obj, ctx);
486                 if (IOTCON_ERROR_NONE != ret) {
487                         ERR("_find_cb_response() Fail(%d)", ret);
488                         g_object_unref(parser);
489                         return ret;
490                 }
491         }
492
493         g_object_unref(parser);
494
495         return IOTCON_ERROR_NONE;
496 }
497
498
499 static int _worker_find_cb(void *context)
500 {
501         int ret;
502         struct icd_find_context *ctx = context;
503
504         RETV_IF(NULL == ctx, IOTCON_ERROR_INVALID_PARAMETER);
505
506         ret = _find_cb_handle_context(ctx);
507         if (IOTCON_ERROR_NONE != ret)
508                 ERR("_find_cb_handle_context() Fail(%d)", ret);
509
510         /* ctx was allocated from icd_ioty_ocprocess_find_cb() */
511         free(ctx->bus_name);
512         free(ctx->payload);
513         free(ctx->dev_addr);
514         free(ctx);
515
516         return ret;
517 }
518
519
520 OCStackApplicationResult icd_ioty_ocprocess_find_cb(void *ctx, OCDoHandle handle,
521                 OCClientResponse *resp)
522 {
523         int ret;
524         OCDevAddr *dev_addr;
525         struct icd_find_context *find_ctx;
526         icd_sig_ctx_s *sig_context = ctx;
527
528         RETV_IF(NULL == ctx, OC_STACK_KEEP_TRANSACTION);
529         RETV_IF(NULL == resp, OC_STACK_KEEP_TRANSACTION);
530         RETV_IF(NULL == resp->resJSONPayload, OC_STACK_KEEP_TRANSACTION);
531
532         find_ctx = calloc(1, sizeof(struct icd_find_context));
533         if (NULL == find_ctx) {
534                 ERR("calloc() Fail(%d)", errno);
535                 return OC_STACK_KEEP_TRANSACTION;
536         }
537
538         dev_addr = calloc(1, sizeof(OCDevAddr));
539         if (NULL == dev_addr) {
540                 ERR("calloc() Fail(%d)", errno);
541                 free(find_ctx);
542                 return OC_STACK_KEEP_TRANSACTION;
543         }
544         memcpy(dev_addr, resp->addr, sizeof(OCDevAddr));
545
546         find_ctx->signum = sig_context->signum;
547         find_ctx->bus_name = ic_utils_strdup(sig_context->bus_name);
548         find_ctx->payload = ic_utils_strdup(resp->resJSONPayload);
549         find_ctx->dev_addr = dev_addr;
550         find_ctx->conn_type = resp->connType;
551
552         ret = _ocprocess_worker_start(_worker_find_cb, find_ctx);
553         if (IOTCON_ERROR_NONE != ret) {
554                 ERR("_ocprocess_worker_start() Fail(%d)", ret);
555                 free(find_ctx->bus_name);
556                 free(find_ctx->payload);
557                 free(find_ctx->dev_addr);
558                 free(find_ctx);
559                 return OC_STACK_KEEP_TRANSACTION;
560         }
561
562         /* DO NOT FREE sig_context. It MUST be freed in the ocstack */
563         /* DO NOT FREE find_ctx. It MUST be freed in the _worker_find_cb func */
564
565         return OC_STACK_KEEP_TRANSACTION;
566 }
567
568
569 static int _worker_get_cb(void *context)
570 {
571         int ret;
572         GVariant *value;
573         struct icd_get_context *ctx = context;
574
575         RETV_IF(NULL == ctx, IOTCON_ERROR_INVALID_PARAMETER);
576
577         value = g_variant_new("(a(qs)si)", ctx->options, ctx->payload, ctx->res);
578         icd_ioty_get_complete(ctx->invocation, value);
579
580         /* ctx was allocated from icd_ioty_ocprocess_get_cb() */
581         free(ctx->payload);
582         g_variant_builder_unref(ctx->options);
583         free(ctx);
584
585         return ret;
586 }
587
588
589 OCStackApplicationResult icd_ioty_ocprocess_get_cb(void *ctx, OCDoHandle handle,
590                 OCClientResponse *resp)
591 {
592         FN_CALL;
593         int ret, res;
594         OCStackResult result;
595         GVariantBuilder *options;
596         struct icd_get_context *get_ctx;
597
598         RETV_IF(NULL == ctx, OC_STACK_DELETE_TRANSACTION);
599
600         if (NULL == resp->resJSONPayload || '\0' == resp->resJSONPayload[0]) {
601                 ERR("json payload is empty");
602                 icd_ioty_get_complete_error(ctx, IOTCON_ERROR_IOTIVITY);
603                 return OC_STACK_DELETE_TRANSACTION;
604         }
605
606         get_ctx = calloc(1, sizeof(struct icd_get_context));
607         if (NULL == get_ctx) {
608                 ERR("calloc() Fail(%d)", errno);
609                 icd_ioty_get_complete_error(ctx, IOTCON_ERROR_OUT_OF_MEMORY);
610                 return OC_STACK_DELETE_TRANSACTION;
611         }
612
613         result = resp->result;
614         if (result == OC_STACK_OK) {
615                 res = IOTCON_RESPONSE_RESULT_OK;
616                 options = _ocprocess_parse_header_options(resp->rcvdVendorSpecificHeaderOptions,
617                                 resp->numRcvdVendorSpecificHeaderOptions);
618         } else {
619                 WARN("resp error(%d)", result);
620                 res = IOTCON_RESPONSE_RESULT_ERROR;
621                 options = NULL;
622         }
623
624         get_ctx->payload = strdup(resp->resJSONPayload);
625         get_ctx->res = res;
626         get_ctx->options = options;
627         get_ctx->invocation = ctx;
628
629         ret = _ocprocess_worker_start(_worker_get_cb, get_ctx);
630         if (IOTCON_ERROR_NONE != ret) {
631                 ERR("_ocprocess_worker_start() Fail(%d)", ret);
632                 icd_ioty_get_complete_error(ctx, ret);
633                 free(get_ctx->payload);
634                 g_variant_builder_unref(get_ctx->options);
635                 free(get_ctx);
636                 return OC_STACK_DELETE_TRANSACTION;
637         }
638
639         /* DO NOT FREE get_ctx. It MUST be freed in the _worker_get_cb func */
640
641         return OC_STACK_DELETE_TRANSACTION;
642 }
643