2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <unistd.h> /* for usleep() */
20 #include <json-glib/json-glib.h>
29 #include "icd-ioty-ocprocess.h"
31 static int icd_ioty_alive;
33 typedef int (*_ocprocess_fn)(void *user_data);
35 struct icd_ioty_worker
44 ICD_TRANSPORT_IPV4_SECURE,
49 struct icd_req_context {
56 OCRequestHandle request_h;
57 OCResourceHandle resource_h;
58 GVariantBuilder *options;
59 GVariantBuilder *query;
63 struct icd_find_context {
72 struct icd_get_context {
75 GVariantBuilder *options;
76 GDBusMethodInvocation *invocation;
80 void icd_ioty_ocprocess_stop()
85 static void* _ocprocess_worker_thread(void *data)
88 struct icd_ioty_worker *worker = data;
91 ERR("worker is NULL");
95 ret = worker->fn(worker->ctx);
96 if (IOTCON_ERROR_NONE != ret)
97 ERR("fn() Fail(%d)", ret);
99 /* worker was allocated from _ocprocess_worker_start() */
102 /* GCC warning happen if use g_thread_exit() */
107 static int _ocprocess_worker_start(_ocprocess_fn fn, void *ctx)
111 struct icd_ioty_worker *worker;
113 RETV_IF(NULL == fn, IOTCON_ERROR_INVALID_PARAMETER);
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;
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);
130 return IOTCON_ERROR_SYSTEM;
133 /* DO NOT join thread. It was already detached by calling g_thread_unref() */
134 g_thread_unref(thread);
136 /* DO NOT FREE worker. It MUST be freed in the _ocprocess_worker_thread() */
138 return IOTCON_ERROR_NONE;
142 static int _ocprocess_response_signal(const char *dest, const char *signal,
143 unsigned int signum, GVariant *value)
146 char sig_name[IC_DBUS_SIGNAL_LENGTH] = {0};
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;
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);
160 return IOTCON_ERROR_NONE;
164 static inline GVariantBuilder* _ocprocess_parse_header_options(
165 OCHeaderOption *oic_option, int option_size)
168 GVariantBuilder *options;
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);
180 static int _worker_req_handler(void *context)
184 struct icd_req_context *ctx = context;
186 RETV_IF(NULL == ctx, IOTCON_ERROR_INVALID_PARAMETER);
188 value = g_variant_new("(ia(qs)a(ss)iisii)",
195 GPOINTER_TO_INT(ctx->request_h),
196 GPOINTER_TO_INT(ctx->resource_h));
198 ret = _ocprocess_response_signal(ctx->bus_name, IC_DBUS_SIGNAL_REQUEST_HANDLER,
200 if (IOTCON_ERROR_NONE != ret)
201 ERR("_ocprocess_response_signal() Fail(%d)", ret);
205 g_variant_builder_unref(ctx->options);
206 g_variant_builder_unref(ctx->query);
213 OCEntityHandlerResult icd_ioty_ocprocess_req_handler(OCEntityHandlerFlag flag,
214 OCEntityHandlerRequest *request)
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;
223 RETV_IF(NULL == request, OC_EH_ERROR);
225 req_ctx = calloc(1, sizeof(struct icd_req_context));
226 if (NULL == req_ctx) {
227 ERR("calloc() Fail(%d)", errno);
232 req_ctx->request_h = request->requestHandle;
233 req_ctx->resource_h = request->resource;
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);
242 /* signal number & bus_name */
243 req_ctx->signum = signal_number;
244 req_ctx->bus_name = bus_name;
247 if (OC_REQUEST_FLAG & flag) {
248 switch (request->method) {
250 req_ctx->types = IOTCON_REQUEST_GET;
251 req_ctx->payload = strdup(IC_STR_NULL);
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;
261 req_ctx->types = IOTCON_REQUEST_PUT;
262 req_ctx->payload = ic_utils_strdup(request->reqJSONPayload);
265 req_ctx->types = IOTCON_REQUEST_POST;
266 req_ctx->payload = ic_utils_strdup(request->reqJSONPayload);
269 req_ctx->types = IOTCON_REQUEST_DELETE;
270 req_ctx->payload = strdup(IC_STR_NULL);
273 free(req_ctx->bus_name);
280 req_ctx->options = _ocprocess_parse_header_options(
281 request->rcvdVendorSpecificHeaderOptions,
282 request->numRcvdVendorSpecificHeaderOptions);
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))) {
290 query_value = strtok_r(token, "=", &save_ptr2);
291 if (NULL == query_value)
294 g_variant_builder_add(req_ctx->query, "(ss)", query_key, query_value);
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);
310 /* DO NOT FREE req_ctx. It MUST be freed in the _worker_req_handler func */
316 gpointer icd_ioty_ocprocess_thread(gpointer data)
319 OCStackResult result;
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);
331 /* TODO : SHOULD revise time or usleep */
340 * returned string SHOULD be released by you
342 static inline char* _find_cb_get_address(OCDevAddr *address, int sec_type, int sec_port)
348 char addr[1024] = {0};
350 RETVM_IF(ICD_TRANSPORT_IPV4 != sec_type && ICD_TRANSPORT_IPV4_SECURE != sec_type,
351 NULL, "Invalid secure type(%d)", sec_type);
353 ret = OCDevAddrToIPv4Addr(address, &a, &b, &c, &d);
354 if (OC_STACK_OK != ret) {
355 ERR("OCDevAddrToIPv4Addr() Fail(%d)", ret);
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);
365 ret = snprintf(addr, sizeof(addr), ICD_IOTY_COAPS"%d.%d.%d.%d:%d", a, b, c, d,
368 ret = OCDevAddrToPort(address, &port);
369 if (OC_STACK_OK != ret) {
370 ERR("OCDevAddrToPort() Fail(%d)", ret);
374 ret = snprintf(addr, sizeof(addr), ICD_IOTY_COAP"%d.%d.%d.%d:%d", a, b, c, d,
378 WARN_IF(ret <= 0 || sizeof(addr) <= ret, "snprintf() Fail(%d)", ret);
380 return ic_utils_strdup(addr);
384 static inline int _find_cb_response(JsonObject *rsrc_obj,
385 struct icd_find_context *ctx)
390 char *host, *json_data;
391 JsonObject *property_obj;
392 int ret, secure, secure_type, secure_port;
394 RETV_IF(NULL == rsrc_obj, IOTCON_ERROR_INVALID_PARAMETER);
395 RETV_IF(NULL == ctx, IOTCON_ERROR_INVALID_PARAMETER);
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;
404 secure = json_object_get_int_member(property_obj, IC_JSON_KEY_SECURE);
406 secure_type = ICD_TRANSPORT_IPV4;
409 secure_type = ICD_TRANSPORT_IPV4_SECURE;
410 secure_port = json_object_get_int_member(property_obj, IC_JSON_KEY_PORT);
413 host = _find_cb_get_address(ctx->dev_addr, secure_type, secure_port);
415 ERR("_find_cb_get_address() Fail");
416 return IOTCON_ERROR_IOTIVITY;
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);
424 json_data = json_generator_to_data(gen, NULL);
425 json_node_free(root_node);
428 value = g_variant_new("(ssi)", json_data, host, ctx->conn_type);
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,
437 if (IOTCON_ERROR_NONE != ret) {
438 ERR("_ocprocess_response_signal() Fail(%d)", ret);
442 return IOTCON_ERROR_NONE;
446 static inline int _find_cb_handle_context(struct icd_find_context *ctx)
450 GError *error = NULL;
451 JsonObject *root_obj;
452 JsonArray *rsrc_array;
453 unsigned int rsrc_count, rsrc_index;
455 RETV_IF(NULL == ctx, IOTCON_ERROR_INVALID_PARAMETER);
456 RETV_IF(NULL == ctx->payload, IOTCON_ERROR_INVALID_PARAMETER);
458 parser = json_parser_new();
459 ret = json_parser_load_from_data(parser, ctx->payload, strlen(ctx->payload), &error);
461 ERR("json_parser_load_from_data() Fail(%s)", error->message);
463 return IOTCON_ERROR_INVALID_PARAMETER;
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;
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;
482 for (rsrc_index = 0; rsrc_index < rsrc_count; rsrc_index++) {
483 JsonObject *rsrc_obj = json_array_get_object_element(rsrc_array, rsrc_index);
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);
493 g_object_unref(parser);
495 return IOTCON_ERROR_NONE;
499 static int _worker_find_cb(void *context)
502 struct icd_find_context *ctx = context;
504 RETV_IF(NULL == ctx, IOTCON_ERROR_INVALID_PARAMETER);
506 ret = _find_cb_handle_context(ctx);
507 if (IOTCON_ERROR_NONE != ret)
508 ERR("_find_cb_handle_context() Fail(%d)", ret);
510 /* ctx was allocated from icd_ioty_ocprocess_find_cb() */
520 OCStackApplicationResult icd_ioty_ocprocess_find_cb(void *ctx, OCDoHandle handle,
521 OCClientResponse *resp)
525 struct icd_find_context *find_ctx;
526 icd_sig_ctx_s *sig_context = ctx;
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);
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;
538 dev_addr = calloc(1, sizeof(OCDevAddr));
539 if (NULL == dev_addr) {
540 ERR("calloc() Fail(%d)", errno);
542 return OC_STACK_KEEP_TRANSACTION;
544 memcpy(dev_addr, resp->addr, sizeof(OCDevAddr));
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;
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);
559 return OC_STACK_KEEP_TRANSACTION;
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 */
565 return OC_STACK_KEEP_TRANSACTION;
569 static int _worker_get_cb(void *context)
573 struct icd_get_context *ctx = context;
575 RETV_IF(NULL == ctx, IOTCON_ERROR_INVALID_PARAMETER);
577 value = g_variant_new("(a(qs)si)", ctx->options, ctx->payload, ctx->res);
578 icd_ioty_get_complete(ctx->invocation, value);
580 /* ctx was allocated from icd_ioty_ocprocess_get_cb() */
582 g_variant_builder_unref(ctx->options);
589 OCStackApplicationResult icd_ioty_ocprocess_get_cb(void *ctx, OCDoHandle handle,
590 OCClientResponse *resp)
594 OCStackResult result;
595 GVariantBuilder *options;
596 struct icd_get_context *get_ctx;
598 RETV_IF(NULL == ctx, OC_STACK_DELETE_TRANSACTION);
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;
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;
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);
619 WARN("resp error(%d)", result);
620 res = IOTCON_RESPONSE_RESULT_ERROR;
624 get_ctx->payload = strdup(resp->resJSONPayload);
626 get_ctx->options = options;
627 get_ctx->invocation = ctx;
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);
636 return OC_STACK_DELETE_TRANSACTION;
639 /* DO NOT FREE get_ctx. It MUST be freed in the _worker_get_cb func */
641 return OC_STACK_DELETE_TRANSACTION;