send response differently by interface type
[platform/core/iot/iotcon.git] / daemon / icd-ioty.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 <stdio.h>
18 #include <stdlib.h>
19 #include <stdint.h> /* for uint8_t etc */
20 #include <stdbool.h>
21 #include <errno.h>
22 #include <glib.h>
23 #include <json-glib/json-glib.h>
24
25 #include <octypes.h>
26 #include <ocstack.h>
27
28 #include "iotcon.h"
29 #include "ic-dbus.h"
30 #include "ic-utils.h"
31 #include "icd.h"
32 #include "icd-dbus.h"
33 #include "icd-ioty.h"
34 #include "icd-ioty-ocprocess.h"
35
36 static GMutex icd_csdk_mutex;
37
38 void icd_ioty_csdk_lock()
39 {
40         g_mutex_lock(&icd_csdk_mutex);
41 }
42
43
44 void icd_ioty_csdk_unlock()
45 {
46         g_mutex_unlock(&icd_csdk_mutex);
47 }
48
49
50 GThread* icd_ioty_init(const char *addr, unsigned short port)
51 {
52         FN_CALL;
53         GError *error;
54         GThread *thread;
55
56         OCStackResult result = OCInit(addr, port, OC_CLIENT_SERVER);
57         if (OC_STACK_OK != result) {
58                 ERR("OCInit() Fail(%d)", result);
59                 return NULL;
60         }
61
62         DBG("OCInit() Success");
63
64         thread = g_thread_try_new("packet_receive_thread", icd_ioty_ocprocess_thread,
65                         NULL, &error);
66         if (NULL == thread) {
67                 ERR("g_thread_try_new() Fail(%s)", error->message);
68                 g_error_free(error);
69                 return NULL;
70         }
71
72         return thread;
73 }
74
75
76 void icd_ioty_deinit(GThread *thread)
77 {
78         OCStackResult result;
79
80         icd_ioty_ocprocess_stop();
81         g_thread_join(thread);
82
83         result = OCStop();
84         if (OC_STACK_OK != result)
85                 ERR("OCStop() Fail(%d)", result);
86 }
87
88
89 OCResourceHandle icd_ioty_register_resource(const char *uri_path,
90                 const char* const* res_types, int ifaces, uint8_t properties)
91 {
92         FN_CALL;
93         int i;
94         OCStackResult ret;
95         OCResourceHandle handle;
96         const char *resInterface = NULL;
97
98         if (IOTCON_INTERFACE_DEFAULT & ifaces) {
99                 resInterface = IC_INTERFACE_DEFAULT;
100                 ifaces ^= IOTCON_INTERFACE_DEFAULT;
101         } else if (IOTCON_INTERFACE_LINK & ifaces) {
102                 resInterface = IC_INTERFACE_LINK;
103                 ifaces ^= IOTCON_INTERFACE_LINK;
104         } else if (IOTCON_INTERFACE_BATCH & ifaces) {
105                 resInterface = IC_INTERFACE_BATCH;
106                 ifaces ^= IOTCON_INTERFACE_BATCH;
107         } else if (IOTCON_INTERFACE_GROUP & ifaces) {
108                 resInterface = IC_INTERFACE_GROUP;
109                 ifaces ^= IOTCON_INTERFACE_GROUP;
110         } else {
111                 ERR("Invalid interface type(%d)", ifaces);
112                 return NULL;
113         }
114
115         icd_ioty_csdk_lock();
116         ret = OCCreateResource(&handle, res_types[0], resInterface, uri_path,
117                         icd_ioty_ocprocess_req_handler, properties);
118         icd_ioty_csdk_unlock();
119         if (OC_STACK_OK != ret) {
120                 ERR("OCCreateResource() Fail(%d)", ret);
121                 return NULL;
122         }
123
124         for (i = 1; res_types[i]; i++)
125                 icd_ioty_bind_type(handle, res_types[i]);
126
127         if (IOTCON_INTERFACE_DEFAULT & ifaces)
128                 icd_ioty_bind_interface(handle, IOTCON_INTERFACE_DEFAULT);
129         if (IOTCON_INTERFACE_LINK & ifaces)
130                 icd_ioty_bind_interface(handle, IOTCON_INTERFACE_LINK);
131         if (IOTCON_INTERFACE_BATCH & ifaces)
132                 icd_ioty_bind_interface(handle, IOTCON_INTERFACE_BATCH);
133         if (IOTCON_INTERFACE_GROUP & ifaces)
134                 icd_ioty_bind_interface(handle, IOTCON_INTERFACE_GROUP);
135
136         return handle;
137 }
138
139
140 int icd_ioty_unregister_resource(OCResourceHandle resource_handle)
141 {
142         OCStackResult ret;
143
144         icd_ioty_csdk_lock();
145         ret = OCDeleteResource(resource_handle);
146         icd_ioty_csdk_unlock();
147         if (OC_STACK_OK != ret) {
148                 ERR("OCDeleteResource() Fail(%d)", ret);
149                 return IOTCON_ERROR_IOTIVITY;
150         }
151
152         return IOTCON_ERROR_NONE;
153 }
154
155
156 int icd_ioty_bind_interface(OCResourceHandle resourceHandle, iotcon_interface_e iface)
157 {
158         int ret;
159         OCStackResult result;
160         char *resource_interface;
161
162         ret = ic_utils_convert_interface_flag(iface, &resource_interface);
163         if (IOTCON_ERROR_NONE != ret) {
164                 ERR("ic_utils_convert_interface_flag(%d) Fail(%d)", iface, ret);
165                 return ret;
166         }
167
168         icd_ioty_csdk_lock();
169         result = OCBindResourceInterfaceToResource(resourceHandle, resource_interface);
170         icd_ioty_csdk_unlock();
171         if (OC_STACK_OK != result) {
172                 ERR("OCBindResourceInterfaceToResource() Fail(%d)", result);
173                 return IOTCON_ERROR_IOTIVITY;
174         }
175
176         return IOTCON_ERROR_NONE;
177 }
178
179
180 int icd_ioty_bind_type(OCResourceHandle resource_handle, const char *resource_type)
181 {
182         OCStackResult ret;
183
184         icd_ioty_csdk_lock();
185         ret = OCBindResourceTypeToResource(resource_handle, resource_type);
186         icd_ioty_csdk_unlock();
187         if (OC_STACK_OK != ret) {
188                 ERR("OCBindResourceTypeToResource() Fail(%d)", ret);
189                 return IOTCON_ERROR_IOTIVITY;
190         }
191
192         return IOTCON_ERROR_NONE;
193 }
194
195
196 int icd_ioty_bind_resource(OCResourceHandle parent, OCResourceHandle child)
197 {
198         OCStackResult ret;
199
200         icd_ioty_csdk_lock();
201         ret = OCBindResource(parent, child);
202         icd_ioty_csdk_unlock();
203         if (OC_STACK_OK != ret) {
204                 ERR("OCBindResource() Fail(%d)", ret);
205                 return IOTCON_ERROR_IOTIVITY;
206         }
207
208         return IOTCON_ERROR_NONE;
209 }
210
211
212 int icd_ioty_unbind_resource(OCResourceHandle parent, OCResourceHandle child)
213 {
214         OCStackResult ret;
215
216         icd_ioty_csdk_lock();
217         ret = OCUnBindResource(parent, child);
218         icd_ioty_csdk_unlock();
219         if (OC_STACK_OK != ret) {
220                 ERR("OCUnBindResource() Fail(%d)", ret);
221                 return IOTCON_ERROR_IOTIVITY;
222         }
223
224         return IOTCON_ERROR_NONE;
225 }
226
227
228 int icd_ioty_notify_list_of_observers(void *handle, GVariant *msg, GVariant *observers)
229 {
230         int i, error_code, obs_length;
231         char *repr_json = NULL;
232         GVariantIter obs_iter, msg_iter;
233         OCStackResult ret;
234
235         g_variant_iter_init(&obs_iter, observers);
236         obs_length = g_variant_iter_n_children(&obs_iter);
237
238         /* Variable-length Array */
239         OCObservationId obs_ids[obs_length];
240
241         for (i = 0; i < obs_length; i++)
242                 g_variant_iter_loop(&obs_iter, "i", &obs_ids[i]);
243
244         g_variant_iter_init(&msg_iter, msg);
245         g_variant_iter_loop(&msg_iter, "(i&s)", &error_code, &repr_json);
246         /* TODO : How to use error_code. */
247
248         icd_ioty_csdk_lock();
249         /* TODO : QoS is come from lib. And user can set QoS to client structure.  */
250         ret = OCNotifyListOfObservers(handle, obs_ids, obs_length, repr_json, OC_HIGH_QOS);
251         icd_ioty_csdk_unlock();
252
253         if (OC_STACK_NO_OBSERVERS == ret) {
254                 WARN("No Observers. Stop Notifying");
255                 return IOTCON_ERROR_NONE;
256         } else if (OC_STACK_OK != ret) {
257                 ERR("OCNotifyListOfObservers() Fail(%d)", ret);
258                 return IOTCON_ERROR_IOTIVITY;
259         }
260
261         return IOTCON_ERROR_NONE;
262 }
263
264
265 int icd_ioty_notify_all(void *handle)
266 {
267         OCStackResult ret;
268
269         icd_ioty_csdk_lock();
270         /* TODO : QoS is come from lib. And user can set QoS to client structure.  */
271         ret = OCNotifyAllObservers(handle, OC_HIGH_QOS);
272         icd_ioty_csdk_unlock();
273
274         if (OC_STACK_NO_OBSERVERS == ret) {
275                 WARN("No Observers. Stop Notifying");
276                 return IOTCON_ERROR_NONE;
277         } else if (OC_STACK_OK != ret) {
278                 ERR("OCNotifyAllObservers() Fail(%d)", ret);
279                 return IOTCON_ERROR_IOTIVITY;
280         }
281
282         return IOTCON_ERROR_NONE;
283 }
284
285
286 static int _ioty_get_header_options(GVariantIter *src, int src_size,
287                 OCHeaderOption dest[], int dest_size)
288 {
289         int i = 0;
290         char *option_data;
291         unsigned short option_id;
292
293         RETV_IF(NULL == dest, IOTCON_ERROR_INVALID_PARAMETER);
294
295         if (dest_size < src_size) {
296                 ERR("Exceed Size(%d)", src_size);
297                 return IOTCON_ERROR_INVALID_PARAMETER;
298         }
299
300         while (g_variant_iter_loop(src, "(q&s)", &option_id, &option_data)) {
301                 dest[i].protocolID = OC_COAP_ID;
302                 dest[i].optionID = option_id;
303                 dest[i].optionLength = strlen(option_data) + 1;
304                 memcpy(dest[i].optionData, option_data, dest[i].optionLength);
305                 i++;
306         }
307
308         return IOTCON_ERROR_NONE;
309 }
310
311
312 int icd_ioty_send_response(GVariant *resp)
313 {
314         int result, error_code, options_size;
315         int request_handle, resource_handle;
316         char *new_uri_path, *repr_json;
317         GVariantIter *options;
318         OCStackResult ret;
319         OCEntityHandlerResponse response = {0};
320
321         g_variant_get(resp, "(&sia(qs)i&sii)",
322                         &new_uri_path,
323                         &error_code,
324                         &options,
325                         &result,
326                         &repr_json,
327                         &request_handle,
328                         &resource_handle);
329
330         response.requestHandle = GINT_TO_POINTER(request_handle);
331         response.resourceHandle = GINT_TO_POINTER(resource_handle);
332         response.ehResult = (OCEntityHandlerResult)result;
333
334         if (OC_EH_RESOURCE_CREATED == response.ehResult)
335                 snprintf(response.resourceUri, sizeof(response.resourceUri), "%s", new_uri_path);
336
337         options_size = g_variant_iter_n_children(options);
338         response.numSendVendorSpecificHeaderOptions = options_size;
339
340         if (0 != options_size) {
341                 int ret= _ioty_get_header_options(options,
342                                 response.numSendVendorSpecificHeaderOptions,
343                                 response.sendVendorSpecificHeaderOptions,
344                                 sizeof(response.sendVendorSpecificHeaderOptions)
345                                 / sizeof(response.sendVendorSpecificHeaderOptions[0]));
346
347                 if (IOTCON_ERROR_NONE != ret)
348                         ERR("_ioty_get_header_options() Fail(%d)", ret);
349         }
350         g_variant_iter_free(options);
351
352         response.payload = repr_json;
353         response.payloadSize = strlen(response.payload) + 1;
354
355         /* related to block transfer */
356         response.persistentBufferFlag = 0;
357
358         icd_ioty_csdk_lock();
359         ret = OCDoResponse(&response);
360         icd_ioty_csdk_unlock();
361
362         if (OC_STACK_OK != ret) {
363                 ERR("OCDoResponse() Fail(%d)", ret);
364                 return IOTCON_ERROR_IOTIVITY;
365         }
366
367         return IOTCON_ERROR_NONE;
368 }
369
370
371 static void _ioty_free_signal_context(void *data)
372 {
373         icd_sig_ctx_s *context = data;
374         free(context->bus_name);
375         free(context);
376 }
377
378 int icd_ioty_find_resource(const char *host_address, const char *resource_type,
379                 unsigned int signum, const char *bus_name)
380 {
381         int len;
382         OCStackResult result;
383         OCCallbackData cbdata = {0};
384         icd_sig_ctx_s *context;
385         char uri[PATH_MAX] = {0};
386         iotcon_connectivity_type_e conn_type = IOTCON_CONNECTIVITY_IPV4;
387
388         if (IC_STR_EQUAL == strcmp(IOTCON_MULTICAST_ADDRESS, host_address)) {
389                 len = snprintf(uri, sizeof(uri), "%s", OC_MULTICAST_DISCOVERY_URI);
390                 conn_type = IOTCON_CONNECTIVITY_ALL;
391         } else {
392                 len = snprintf(uri, sizeof(uri), ICD_IOTY_COAP"%s%s", host_address,
393                                 OC_MULTICAST_DISCOVERY_URI);
394         }
395         if (len <= 0 || sizeof(uri) <= len) {
396                 ERR("snprintf() Fail(%d)", len);
397                 return IOTCON_ERROR_UNKNOWN;
398         }
399
400         if (IC_STR_EQUAL != strcmp(IC_STR_NULL, resource_type))
401                 snprintf(uri + len, sizeof(uri), "?rt=%s", resource_type);
402
403         context = calloc(1, sizeof(icd_sig_ctx_s));
404         if (NULL == context) {
405                 ERR("calloc() Fail(%d)", errno);
406                 return IOTCON_ERROR_OUT_OF_MEMORY;
407         }
408
409         context->bus_name = ic_utils_strdup(bus_name);
410         context->signum = signum;
411
412         cbdata.context = context;
413         cbdata.cb = icd_ioty_ocprocess_find_cb;
414         cbdata.cd = _ioty_free_signal_context;
415
416         icd_ioty_csdk_lock();
417         result = OCDoResource(NULL, OC_REST_GET, uri, NULL, NULL, conn_type, OC_LOW_QOS,
418                         &cbdata, NULL, 0);
419         icd_ioty_csdk_unlock();
420
421         if (OC_STACK_OK != result) {
422                 ERR("OCDoResource() Fail(%d)", result);
423                 free(context->bus_name);
424                 free(context);
425                 return IOTCON_ERROR_IOTIVITY;
426         }
427
428         return IOTCON_ERROR_NONE;
429 }
430
431
432 /*
433  * returned string SHOULD be released by you
434  */
435 static char* _icd_ioty_resource_generate_uri(char *host, char *uri_path, GVariant *query)
436 {
437         int len;
438         bool loop_first = true;
439         char *key, *value;
440         GVariantIter query_iter;
441         char uri_buf[PATH_MAX] = {0};
442
443         len = snprintf(uri_buf, sizeof(uri_buf), "%s%s", host, uri_path);
444
445         /* remove suffix '/' */
446         if ('/' == uri_buf[strlen(uri_buf) - 1]) {
447                 uri_buf[strlen(uri_buf) - 1] = '\0';
448                 len--;
449         }
450
451         g_variant_iter_init(&query_iter, query);
452
453         while (g_variant_iter_loop(&query_iter, "(&s&s)", &key, &value)) {
454                 int query_len;
455
456                 DBG("query exist. key(%s), value(%s)", key, value);
457
458                 if (true == loop_first) {
459                         query_len = snprintf(uri_buf + len, sizeof(uri_buf), "?%s=%s", key, value);
460                         loop_first = false;
461                 } else {
462                         query_len = snprintf(uri_buf + len, sizeof(uri_buf), "&%s=%s", key, value);
463                 }
464
465                 len += query_len;
466         }
467
468         return strdup(uri_buf);
469 }
470
471
472 void icd_ioty_get_complete(GDBusMethodInvocation *invocation, GVariant *value)
473 {
474         ic_dbus_complete_get(icd_dbus_get_object(), invocation, value);
475 }
476
477
478 void icd_ioty_get_complete_error(GDBusMethodInvocation *invocation, int ret_val)
479 {
480         GVariant *value;
481
482         value = g_variant_new("(a(qs)si)", NULL, IC_STR_NULL, ret_val);
483
484         ic_dbus_complete_get(icd_dbus_get_object(), invocation, value);
485 }
486
487
488 gboolean icd_ioty_get(icDbus *object, GDBusMethodInvocation *invocation,
489                 GVariant *resource, GVariant *query)
490 {
491         FN_CALL;
492         OCStackResult result;
493         GVariantIter *options;
494         OCCallbackData cbdata = {0};
495         int conn_type, options_size;
496         char *uri_path, *host, *uri;
497         int is_observable, ifaces, observe_handle;
498         OCHeaderOption oic_options[MAX_HEADER_OPTIONS];
499
500         g_variant_get(resource, "(&s&sba(qs)iii)", &uri_path, &host, &is_observable, &options,
501                         &ifaces, &observe_handle, &conn_type);
502
503         uri = _icd_ioty_resource_generate_uri(host, uri_path, query);
504         if (NULL == uri) {
505                 ERR("_icd_ioty_resource_generate_uri() Fail");
506                 g_variant_iter_free(options);
507                 icd_ioty_get_complete_error(invocation, IOTCON_ERROR_INVALID_PARAMETER);
508                 return TRUE;
509         }
510
511         cbdata.context = invocation;
512         cbdata.cb = icd_ioty_ocprocess_get_cb;
513
514         options_size = g_variant_iter_n_children(options);
515         if (0 != options_size) {
516                 int ret = _ioty_get_header_options(options, options_size, oic_options,
517                                 sizeof(oic_options) / sizeof(oic_options[0]));
518                 if (IOTCON_ERROR_NONE != ret) {
519                         ERR("_ioty_get_header_options() Fail(%d)", ret);
520                         free(uri);
521                         g_variant_iter_free(options);
522                         icd_ioty_get_complete_error(invocation, ret);
523                         return TRUE;
524                 }
525         }
526         g_variant_iter_free(options);
527
528         icd_ioty_csdk_lock();
529         /* TODO : QoS is come from lib. And user can set QoS to client structure.  */
530         result = OCDoResource(NULL, OC_REST_GET, uri, NULL, NULL, conn_type, OC_HIGH_QOS,
531                         &cbdata, options_size?oic_options:NULL, options_size);
532         icd_ioty_csdk_unlock();
533
534         free(uri);
535
536         if (OC_STACK_OK != result) {
537                 ERR("OCDoResource() Fail(%d)", result);
538                 icd_ioty_get_complete_error(invocation, IOTCON_ERROR_IOTIVITY);
539                 return TRUE;
540         }
541
542         return TRUE;
543 }
544
545
546 void icd_ioty_put_complete(GDBusMethodInvocation *invocation, GVariant *value)
547 {
548         ic_dbus_complete_put(icd_dbus_get_object(), invocation, value);
549 }
550
551
552 void icd_ioty_put_complete_error(GDBusMethodInvocation *invocation, int ret_val)
553 {
554         GVariant *value;
555
556         value = g_variant_new("(a(qs)si)", NULL, IC_STR_NULL, ret_val);
557
558         ic_dbus_complete_put(icd_dbus_get_object(), invocation, value);
559 }
560
561
562 gboolean icd_ioty_put(icDbus *object, GDBusMethodInvocation *invocation,
563                 GVariant *resource, const char *repr, GVariant *query)
564 {
565         // TODO : To be implemented
566         return IOTCON_ERROR_NONE;
567 }
568
569
570 void icd_ioty_post_complete(GDBusMethodInvocation *invocation, GVariant *value)
571 {
572         ic_dbus_complete_post(icd_dbus_get_object(), invocation, value);
573 }
574
575
576 void icd_ioty_post_complete_error(GDBusMethodInvocation *invocation, int ret_val)
577 {
578         GVariant *value;
579
580         value = g_variant_new("(a(qs)si)", NULL, IC_STR_NULL, ret_val);
581
582         ic_dbus_complete_post(icd_dbus_get_object(), invocation, value);
583 }
584
585
586 gboolean icd_ioty_post(icDbus *object, GDBusMethodInvocation *invocation,
587                 GVariant *resource, const char *repr, GVariant *query)
588 {
589         // TODO : To be implemented
590         return IOTCON_ERROR_NONE;
591 }
592
593
594 void icd_ioty_delete_complete(GDBusMethodInvocation *invocation, GVariant *value)
595 {
596         ic_dbus_complete_delete(icd_dbus_get_object(), invocation, value);
597 }
598
599
600 void icd_ioty_delete_complete_error(GDBusMethodInvocation *invocation, int ret_val)
601 {
602         GVariant *value;
603
604         value = g_variant_new("(a(qs)i)", NULL, IC_STR_NULL, ret_val);
605
606         ic_dbus_complete_delete(icd_dbus_get_object(), invocation, value);
607 }
608
609
610 gboolean icd_ioty_delete(icDbus *object, GDBusMethodInvocation *invocation,
611                 GVariant *resource)
612 {
613         // TODO : To be implemented
614         return IOTCON_ERROR_NONE;
615 }
616
617
618 int icd_ioty_observer_start(GVariant *resource, int observe_type,
619                 GVariant *query, unsigned int signal_number, const char *bus_name, int *observe_h)
620 {
621         // TODO : To be implemented
622         return IOTCON_ERROR_NONE;
623 }
624
625
626 int icd_ioty_observer_stop(void *observe_h)
627 {
628         // TODO : To be implemented
629         return IOTCON_ERROR_NONE;
630 }
631
632
633 #ifdef DEVICE_INFO_IMPL /* not implemented in iotivity 0.9.1 */
634 int icd_ioty_register_device_info(GVariant *value)
635 {
636         // TODO : To be implemented
637         return IOTCON_ERROR_NONE;
638 }
639
640
641 int icd_ioty_get_device_info(const char *host_address,
642                 unsigned int signal_number, const char *bus_name)
643 {
644         // TODO : To be implemented
645         return IOTCON_ERROR_NONE;
646 }
647 #endif
648
649
650 int icd_ioty_register_platform_info(GVariant *value)
651 {
652         // TODO : To be implemented
653         return IOTCON_ERROR_NONE;
654 }
655
656
657 int icd_ioty_get_platform_info(const char *host_address, unsigned int signal_number,
658                 const char *bus_name)
659 {
660         // TODO : To be implemented
661         return IOTCON_ERROR_NONE;
662 }
663
664
665 OCDoHandle icd_ioty_subscribe_presence(const char *host_address,
666                 const char *resource_type, unsigned int signal_number, const char *bus_name)
667 {
668         // TODO : To be implemented
669         return NULL;
670 }
671
672
673 int icd_ioty_unsubscribe_presence(OCDoHandle presence_handle)
674 {
675         // TODO : To be implemented
676         return IOTCON_ERROR_NONE;
677 }
678
679
680 int icd_ioty_start_presence(unsigned int time_to_live)
681 {
682         // TODO : To be implemented
683         return IOTCON_ERROR_NONE;
684 }
685
686
687 int icd_ioty_stop_presence()
688 {
689         // TODO : To be implemented
690         return IOTCON_ERROR_NONE;
691 }