Resource Encapsulation
[platform/core/iot/iotcon.git] / lib / icl-resource.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 #include <stdbool.h>
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <glib.h>
23
24 #include "iotcon.h"
25 #include "ic-utils.h"
26 #include "icl.h"
27 #include "icl-repr.h"
28 #include "icl-dbus.h"
29 #include "icl-request.h"
30 #include "icl-dbus-type.h"
31 #include "icl-resource-types.h"
32 #include "icl-resource.h"
33 #include "icl-payload.h"
34
35 static void _icl_request_handler(GDBusConnection *connection,
36                 const gchar *sender_name,
37                 const gchar *object_path,
38                 const gchar *interface_name,
39                 const gchar *signal_name,
40                 GVariant *parameters,
41                 gpointer user_data)
42 {
43         FN_CALL;
44         int ret;
45         char *key = NULL;
46         char *option_data;
47         char *value = NULL;
48         GVariant *repr_gvar;
49         GVariantIter *query;
50         GVariantIter *options;
51         GVariantIter *repr_iter;
52         unsigned short option_id;
53         struct icl_resource_request request = {0};
54         iotcon_resource_h resource = user_data;
55         iotcon_request_handler_cb cb = resource->cb;
56
57         g_variant_get(parameters, "(ia(qs)a(ss)iiavxx)",
58                         &request.types,
59                         &options,
60                         &query,
61                         &request.observation_info.action,
62                         &request.observation_info.observer_id,
63                         &repr_iter,
64                         &request.oic_request_h,
65                         &request.oic_resource_h);
66
67         if (g_variant_iter_n_children(options)) {
68                 ret = iotcon_options_create(&request.header_options);
69                 if (IOTCON_ERROR_NONE != ret) {
70                         ERR("iotcon_options_create() Fail(%d)", ret);
71                         g_variant_iter_free(options);
72                         g_variant_iter_free(query);
73                         g_variant_iter_free(repr_iter);
74                         return;
75                 }
76
77                 while (g_variant_iter_loop(options, "(q&s)", &option_id, &option_data))
78                         iotcon_options_add(request.header_options, option_id, option_data);
79         }
80         g_variant_iter_free(options);
81
82         if (g_variant_iter_n_children(query)) {
83                 ret = iotcon_query_create(&request.query);
84                 if (IOTCON_ERROR_NONE != ret) {
85                         ERR("iotcon_query_create() Fail(%d)", ret);
86                         g_variant_iter_free(query);
87                         g_variant_iter_free(repr_iter);
88                         if (request.header_options)
89                                 iotcon_options_destroy(request.header_options);
90                         return;
91                 }
92
93                 while (g_variant_iter_loop(query, "(&s&s)", &key, &value))
94                         iotcon_query_add(request.query, key, value);
95         }
96         g_variant_iter_free(query);
97
98         if (g_variant_iter_loop(repr_iter, "v", &repr_gvar)) {
99                 request.repr = icl_representation_from_gvariant(repr_gvar);
100                 if (NULL == request.repr) {
101                         ERR("icl_representation_from_gvariant() Fail");
102                         if (request.query)
103                                 iotcon_query_destroy(request.query);
104                         if (request.header_options)
105                                 iotcon_options_destroy(request.header_options);
106                         return;
107                 }
108         }
109         g_variant_iter_free(repr_iter);
110
111         /* for iotcon_resource_notify */
112         if (IOTCON_REQUEST_OBSERVE & request.types) {
113                 int observer_id = request.observation_info.observer_id;
114                 if (IOTCON_OBSERVE_REGISTER == request.observation_info.action) {
115                         if (NULL == resource->observers)
116                                 iotcon_observers_create(&resource->observers);
117                         iotcon_observers_add(resource->observers, observer_id);
118                 } else if (IOTCON_OBSERVE_DEREGISTER == request.observation_info.action) {
119                         iotcon_observers_remove(resource->observers, observer_id);
120                 }
121         }
122
123         if (cb)
124                 cb(resource, &request, resource->user_data);
125
126         /* To avoid unnecessary ERR log (representation could be NULL) */
127         if (request.repr)
128                 iotcon_representation_destroy(request.repr);
129         if (request.query)
130                 iotcon_query_destroy(request.query);
131         if (request.header_options)
132                 iotcon_options_destroy(request.header_options);
133 }
134
135
136 static void _icl_resource_conn_cleanup(iotcon_resource_h resource)
137 {
138         resource->sub_id = 0;
139
140         if (resource->handle) {
141                 resource->handle = 0;
142                 return;
143         }
144
145         iotcon_resource_types_destroy(resource->types);
146         if (resource->observers)
147                 iotcon_observers_destroy(resource->observers);
148         free(resource->uri_path);
149         free(resource);
150 }
151
152
153 /* The length of uri_path should be less than or equal to 36. */
154 API int iotcon_resource_create(const char *uri_path,
155                 iotcon_resource_types_h res_types,
156                 int ifaces,
157                 int properties,
158                 iotcon_request_handler_cb cb,
159                 void *user_data,
160                 iotcon_resource_h *resource_handle)
161 {
162         unsigned int sub_id;
163         GError *error = NULL;
164         const gchar **types;
165         iotcon_resource_h resource;
166         int signal_number, ret;
167         char sig_name[IC_DBUS_SIGNAL_LENGTH];
168
169         RETV_IF(NULL == icl_dbus_get_object(), IOTCON_ERROR_DBUS);
170         RETV_IF(NULL == uri_path, IOTCON_ERROR_INVALID_PARAMETER);
171         RETVM_IF(ICL_URI_PATH_LENGTH_MAX < strlen(uri_path),
172                         IOTCON_ERROR_INVALID_PARAMETER, "Invalid uri_path(%s)", uri_path);
173         RETV_IF(NULL == res_types, IOTCON_ERROR_INVALID_PARAMETER);
174         RETV_IF(NULL == cb, IOTCON_ERROR_INVALID_PARAMETER);
175
176         resource = calloc(1, sizeof(struct icl_resource));
177         if (NULL == resource) {
178                 ERR("calloc() Fail(%d)", errno);
179                 return IOTCON_ERROR_OUT_OF_MEMORY;
180         }
181
182         types = icl_dbus_resource_types_to_array(res_types);
183         if (NULL == types) {
184                 ERR("icl_dbus_resource_types_to_array() Fail");
185                 free(resource);
186                 return IOTCON_ERROR_OUT_OF_MEMORY;
187         }
188
189         signal_number = icl_dbus_generate_signal_number();
190
191         ic_dbus_call_register_resource_sync(icl_dbus_get_object(), uri_path, types, ifaces,
192                         properties, signal_number, &(resource->handle), NULL, &error);
193         if (error) {
194                 ERR("ic_dbus_call_register_resource_sync() Fail(%s)", error->message);
195                 ret = icl_dbus_convert_dbus_error(error->code);
196                 g_error_free(error);
197                 free(types);
198                 free(resource);
199                 return ret;
200         }
201         free(types);
202
203         if (0 == resource->handle) {
204                 ERR("iotcon-daemon Fail");
205                 free(resource);
206                 return IOTCON_ERROR_IOTIVITY;
207         }
208
209         resource->cb = cb;
210         resource->user_data = user_data;
211
212         resource->types = icl_resource_types_ref(res_types);
213         resource->uri_path = ic_utils_strdup(uri_path);
214         resource->ifaces = ifaces;
215         resource->is_observable = properties & IOTCON_OBSERVABLE;
216
217         snprintf(sig_name, sizeof(sig_name), "%s_%u", IC_DBUS_SIGNAL_REQUEST_HANDLER,
218                         signal_number);
219
220         sub_id = icl_dbus_subscribe_signal(sig_name, resource, _icl_resource_conn_cleanup,
221                         _icl_request_handler);
222         if (0 == sub_id) {
223                 ERR("icl_dbus_subscribe_signal() Fail");
224                 iotcon_resource_types_destroy(res_types);
225                 free(resource->uri_path);
226                 free(resource);
227                 return IOTCON_ERROR_DBUS;
228         }
229
230         resource->sub_id = sub_id;
231
232         *resource_handle = resource;
233
234         return IOTCON_ERROR_NONE;
235 }
236
237
238 API int iotcon_resource_destroy(iotcon_resource_h resource)
239 {
240         FN_CALL;
241         int ret;
242         GError *error = NULL;
243
244         RETV_IF(NULL == icl_dbus_get_object(), IOTCON_ERROR_DBUS);
245         RETV_IF(NULL == resource, IOTCON_ERROR_INVALID_PARAMETER);
246
247         if (0 == resource->sub_id) {
248                 WARN("Invalid Resource handle");
249                 iotcon_resource_types_destroy(resource->types);
250                 if (resource->observers)
251                         iotcon_observers_destroy(resource->observers);
252                 free(resource->uri_path);
253                 free(resource);
254                 return IOTCON_ERROR_NONE;
255         }
256
257         ic_dbus_call_unregister_resource_sync(icl_dbus_get_object(), resource->handle, NULL,
258                         &error);
259         if (error) {
260                 ERR("ic_dbus_call_unregister_resource_sync() Fail(%s)", error->message);
261                 ret = icl_dbus_convert_dbus_error(error->code);
262                 g_error_free(error);
263                 return ret;
264         }
265
266         resource->handle = 0;
267
268         icl_dbus_unsubscribe_signal(resource->sub_id);
269
270         return IOTCON_ERROR_NONE;
271 }
272
273
274 API int iotcon_resource_bind_interface(iotcon_resource_h resource,
275                 iotcon_interface_e iface)
276 {
277         FN_CALL;
278         int ret;
279         GError *error = NULL;
280
281         RETV_IF(NULL == icl_dbus_get_object(), IOTCON_ERROR_DBUS);
282         RETV_IF(NULL == resource, IOTCON_ERROR_INVALID_PARAMETER);
283         if (0 == resource->sub_id) {
284                 ERR("Invalid Resource handle");
285                 return IOTCON_ERROR_INVALID_PARAMETER;
286         }
287
288         ic_dbus_call_bind_interface_sync(icl_dbus_get_object(), resource->handle,
289                         iface, &ret, NULL, &error);
290         if (error) {
291                 ERR("ic_dbus_call_bind_interface_sync() Fail(%s)", error->message);
292                 ret = icl_dbus_convert_dbus_error(error->code);
293                 g_error_free(error);
294                 return ret;
295         }
296
297         if (IOTCON_ERROR_NONE != ret) {
298                 ERR("iotcon-daemon Fail(%d)", ret);
299                 return icl_dbus_convert_daemon_error(ret);
300         }
301
302         return ret;
303 }
304
305
306 API int iotcon_resource_bind_type(iotcon_resource_h resource, const char *resource_type)
307 {
308         FN_CALL;
309         int ret;
310         GError *error = NULL;
311
312         RETV_IF(NULL == icl_dbus_get_object(), IOTCON_ERROR_DBUS);
313         RETV_IF(NULL == resource, IOTCON_ERROR_INVALID_PARAMETER);
314         RETV_IF(NULL == resource_type, IOTCON_ERROR_INVALID_PARAMETER);
315         if (ICL_RESOURCE_TYPE_LENGTH_MAX < strlen(resource_type)) {
316                 ERR("Invalid resource_type(%s)", resource_type);
317                 return IOTCON_ERROR_INVALID_PARAMETER;
318         }
319
320         if (0 == resource->sub_id) {
321                 ERR("Invalid Resource handle");
322                 return IOTCON_ERROR_INVALID_PARAMETER;
323         }
324
325         ic_dbus_call_bind_type_sync(icl_dbus_get_object(), resource->handle, resource_type,
326                         &ret, NULL, &error);
327         if (error) {
328                 ERR("ic_dbus_call_bind_type_sync() Fail(%s)", error->message);
329                 ret = icl_dbus_convert_dbus_error(error->code);
330                 g_error_free(error);
331                 return ret;
332         }
333
334         if (IOTCON_ERROR_NONE != ret) {
335                 ERR("iotcon-daemon Fail(%d)", ret);
336                 return icl_dbus_convert_daemon_error(ret);
337         }
338
339         return ret;
340 }
341
342
343 API int iotcon_resource_set_request_handler(iotcon_resource_h resource,
344                 iotcon_request_handler_cb cb)
345 {
346         RETV_IF(NULL == resource, IOTCON_ERROR_INVALID_PARAMETER);
347         RETV_IF(NULL == cb, IOTCON_ERROR_INVALID_PARAMETER);
348
349         WARN("Request handler is changed");
350         resource->cb = cb;
351
352         return IOTCON_ERROR_NONE;
353 }
354
355
356 API int iotcon_resource_bind_child_resource(iotcon_resource_h parent,
357                 iotcon_resource_h child)
358 {
359         GError *error = NULL;
360         int i, ret;
361
362         RETV_IF(NULL == icl_dbus_get_object(), IOTCON_ERROR_DBUS);
363         RETV_IF(NULL == parent, IOTCON_ERROR_INVALID_PARAMETER);
364         RETV_IF(NULL == child, IOTCON_ERROR_INVALID_PARAMETER);
365         RETV_IF(parent == child, IOTCON_ERROR_INVALID_PARAMETER);
366
367         if (0 == parent->sub_id) {
368                 ERR("Invalid Resource handle(parent)");
369                 return IOTCON_ERROR_INVALID_PARAMETER;
370         }
371         if (0 == child->sub_id) {
372                 ERR("Invalid Resource handle(child)");
373                 return IOTCON_ERROR_INVALID_PARAMETER;
374         }
375
376         for (i = 0; i < ICL_CONTAINED_RESOURCES_MAX; i++) {
377                 if (child == parent->children[i]) {
378                         ERR("Child resource was already bound to parent resource.");
379                         return IOTCON_ERROR_ALREADY;
380                 }
381         }
382
383         for (i = 0; i < ICL_CONTAINED_RESOURCES_MAX; i++) {
384                 if (NULL == parent->children[i]) {
385                         ic_dbus_call_bind_resource_sync(icl_dbus_get_object(), parent->handle,
386                                         child->handle, &ret, NULL, &error);
387                         if (error) {
388                                 ERR("ic_dbus_call_bind_resource_sync() Fail(%s)", error->message);
389                                 ret = icl_dbus_convert_dbus_error(error->code);
390                                 g_error_free(error);
391                                 return ret;
392                         }
393
394                         if (IOTCON_ERROR_NONE != ret) {
395                                 ERR("iotcon-daemon Fail(%d)", ret);
396                                 return icl_dbus_convert_daemon_error(ret);
397                         }
398
399                         parent->children[i] = child;
400
401                         return IOTCON_ERROR_NONE;
402                 }
403         }
404
405         ERR("There is no slot to bind a child resource");
406         return IOTCON_ERROR_OUT_OF_MEMORY;
407 }
408
409
410 API int iotcon_resource_unbind_child_resource(iotcon_resource_h parent,
411                 iotcon_resource_h child)
412 {
413         GError *error = NULL;
414         int i, ret;
415
416         RETV_IF(NULL == icl_dbus_get_object(), IOTCON_ERROR_DBUS);
417         RETV_IF(NULL == parent, IOTCON_ERROR_INVALID_PARAMETER);
418         RETV_IF(NULL == child, IOTCON_ERROR_INVALID_PARAMETER);
419
420         if (0 == parent->sub_id) {
421                 ERR("Invalid Resource handle(parent)");
422                 return IOTCON_ERROR_INVALID_PARAMETER;
423         }
424         if (0 == child->sub_id) {
425                 ERR("Invalid Resource handle(child)");
426                 return IOTCON_ERROR_INVALID_PARAMETER;
427         }
428
429         ic_dbus_call_unbind_resource_sync(icl_dbus_get_object(), parent->handle,
430                         child->handle, &ret, NULL, &error);
431         if (error) {
432                 ERR("ic_dbus_call_unbind_resource_sync() Fail(%s)", error->message);
433                 ret = icl_dbus_convert_dbus_error(error->code);
434                 g_error_free(error);
435                 return ret;
436         }
437
438         if (IOTCON_ERROR_NONE != ret) {
439                 ERR("iotcon-daemon Fail(%d)", ret);
440                 return icl_dbus_convert_daemon_error(ret);
441         }
442
443         for (i = 0; i < ICL_CONTAINED_RESOURCES_MAX; i++) {
444                 if (child == parent->children[i])
445                         parent->children[i] = NULL;
446         }
447
448         return IOTCON_ERROR_NONE;
449 }
450
451
452 API int iotcon_resource_get_number_of_children(iotcon_resource_h resource, int *number)
453 {
454         int i;
455
456         RETV_IF(NULL == resource, IOTCON_ERROR_INVALID_PARAMETER);
457         RETV_IF(NULL == number, IOTCON_ERROR_INVALID_PARAMETER);
458
459         *number = 0;
460         for (i = 0; i < ICL_CONTAINED_RESOURCES_MAX; i++) {
461                 if (resource->children[i])
462                         *number += 1;
463         }
464
465         return IOTCON_ERROR_NONE;
466 }
467
468
469 API int iotcon_resource_get_nth_child(iotcon_resource_h parent, int index,
470                 iotcon_resource_h *child)
471 {
472         RETV_IF(NULL == parent, IOTCON_ERROR_INVALID_PARAMETER);
473         RETV_IF(NULL == child, IOTCON_ERROR_INVALID_PARAMETER);
474         if ((index < 0) || (ICL_CONTAINED_RESOURCES_MAX <= index)) {
475                 ERR("Invalid index(%d)", index);
476                 return IOTCON_ERROR_INVALID_PARAMETER;
477         }
478
479         *child = parent->children[index];
480
481         return IOTCON_ERROR_NONE;
482 }
483
484
485 /* The content of the resource should not be freed by user. */
486 API int iotcon_resource_get_uri_path(iotcon_resource_h resource, char **uri_path)
487 {
488         RETV_IF(NULL == resource, IOTCON_ERROR_INVALID_PARAMETER);
489         RETV_IF(NULL == uri_path, IOTCON_ERROR_INVALID_PARAMETER);
490
491         *uri_path = resource->uri_path;
492
493         return IOTCON_ERROR_NONE;
494 }
495
496
497 /* The content of the resource should not be freed by user. */
498 API int iotcon_resource_get_types(iotcon_resource_h resource,
499                 iotcon_resource_types_h *types)
500 {
501         RETV_IF(NULL == resource, IOTCON_ERROR_INVALID_PARAMETER);
502         RETV_IF(NULL == types, IOTCON_ERROR_INVALID_PARAMETER);
503
504         *types = resource->types;
505
506         return IOTCON_ERROR_NONE;
507 }
508
509
510 API int iotcon_resource_get_interfaces(iotcon_resource_h resource, int *ifaces)
511 {
512         RETV_IF(NULL == resource, IOTCON_ERROR_INVALID_PARAMETER);
513         RETV_IF(NULL == ifaces, IOTCON_ERROR_INVALID_PARAMETER);
514
515         *ifaces = resource->ifaces;
516
517         return IOTCON_ERROR_NONE;
518 }
519
520
521 API int iotcon_resource_is_observable(iotcon_resource_h resource, bool *observable)
522 {
523         RETV_IF(NULL == resource, IOTCON_ERROR_INVALID_PARAMETER);
524         RETV_IF(NULL == observable, IOTCON_ERROR_INVALID_PARAMETER);
525
526         *observable = resource->is_observable;
527
528         return IOTCON_ERROR_NONE;
529 }
530
531
532 API int iotcon_notimsg_create(iotcon_representation_h repr, iotcon_interface_e iface,
533                 iotcon_notimsg_h *notimsg_handle)
534 {
535         iotcon_notimsg_h msg;
536
537         RETV_IF(NULL == repr, IOTCON_ERROR_INVALID_PARAMETER);
538         RETV_IF(NULL == notimsg_handle, IOTCON_ERROR_INVALID_PARAMETER);
539
540         msg = calloc(1, sizeof(struct icl_notify_msg));
541         if (NULL == msg) {
542                 ERR("calloc() Fail(%d)", errno);
543                 return IOTCON_ERROR_OUT_OF_MEMORY;
544         }
545
546         msg->repr = repr;
547         icl_representation_inc_ref_count(msg->repr);
548         msg->iface = iface;
549         msg->error_code = 200;
550
551         *notimsg_handle = msg;
552
553         return IOTCON_ERROR_NONE;
554 }
555
556
557 API void iotcon_notimsg_destroy(iotcon_notimsg_h msg)
558 {
559         RET_IF(NULL == msg);
560
561         iotcon_representation_destroy(msg->repr);
562         free(msg);
563 }
564
565 API int iotcon_resource_notify(iotcon_resource_h resource, iotcon_notimsg_h msg,
566                 iotcon_observers_h observers)
567 {
568         int ret;
569         GError *error = NULL;
570         GVariant *noti_msg;
571         GVariant *obs;
572
573         RETV_IF(NULL == icl_dbus_get_object(), IOTCON_ERROR_DBUS);
574         RETV_IF(NULL == resource, IOTCON_ERROR_INVALID_PARAMETER);
575
576         if (0 == resource->sub_id) {
577                 ERR("Invalid Resource handle");
578                 return IOTCON_ERROR_INVALID_PARAMETER;
579         }
580
581         /* TODO: Get default message if msg parameter is NULL */
582         noti_msg = icl_dbus_notimsg_to_gvariant(msg);
583         if (NULL == noti_msg) {
584                 ERR("icl_dbus_notimsg_to_gvariant() Fail");
585                 return IOTCON_ERROR_REPRESENTATION;
586         }
587
588         if (observers)
589                 obs = icl_dbus_observers_to_gvariant(observers);
590         else
591                 obs = icl_dbus_observers_to_gvariant(resource->observers);
592
593         ic_dbus_call_notify_sync(icl_dbus_get_object(), resource->handle, noti_msg, obs, &ret,
594                         NULL, &error);
595         if (error) {
596                 ERR("ic_dbus_call_notify_sync() Fail(%s)", error->message);
597                 ret = icl_dbus_convert_dbus_error(error->code);
598                 g_error_free(error);
599                 g_variant_unref(obs);
600                 g_variant_unref(noti_msg);
601                 return ret;
602         }
603
604         if (IOTCON_ERROR_NONE != ret) {
605                 ERR("iotcon-daemon Fail(%d)", ret);
606                 return icl_dbus_convert_daemon_error(ret);
607         }
608
609         return IOTCON_ERROR_NONE;
610 }
611