diff --git a/api/cloud/unittest/cloud_store_test.cpp b/api/cloud/unittest/cloud_store_test.cpp index ee5df28c1..34161fd9d 100644 --- a/api/cloud/unittest/cloud_store_test.cpp +++ b/api/cloud/unittest/cloud_store_test.cpp @@ -22,6 +22,7 @@ #include "api/cloud/oc_cloud_resource_internal.h" #include "api/cloud/oc_cloud_store_internal.h" #include "api/oc_event_callback_internal.h" +#include "api/oc_rep_internal.h" #include "api/oc_storage_internal.h" #include "oc_api.h" #include "oc_config.h" diff --git a/api/oc_collection.c b/api/oc_collection.c index f4a91b631..5d1bb512c 100644 --- a/api/oc_collection.c +++ b/api/oc_collection.c @@ -25,6 +25,7 @@ #include "api/oc_link_internal.h" #include "api/oc_resource_internal.h" #include "api/oc_ri_internal.h" +#include "api/oc_ri_server_internal.h" #include "messaging/coap/observe_internal.h" #include "oc_api.h" #include "oc_core_res.h" diff --git a/api/oc_ri.c b/api/oc_ri.c index 309264785..7b3868da4 100644 --- a/api/oc_ri.c +++ b/api/oc_ri.c @@ -16,7 +16,6 @@ * ***************************************************************************/ -#include "api/oc_discovery_internal.h" #include "api/oc_endpoint_internal.h" #include "api/oc_event_callback_internal.h" #include "api/oc_events_internal.h" @@ -60,16 +59,10 @@ #ifdef OC_SERVER #include "api/oc_ri_server_internal.h" -#include "api/oc_server_api_internal.h" #include "messaging/coap/observe_internal.h" #include "messaging/coap/separate_internal.h" #ifdef OC_COLLECTIONS #include "api/oc_collection_internal.h" -#include "api/oc_link_internal.h" -#include "oc_collection.h" -#ifdef OC_COLLECTIONS_IF_CREATE -#include "oc_resource_factory_internal.h" -#endif /* OC_COLLECTIONS_IF_CREATE */ #endif /* OC_COLLECTIONS */ #endif /* OC_SERVER */ @@ -113,21 +106,6 @@ OC_PROCESS_NAME(oc_push_process); #endif /* OC_HAS_FEATURE_PUSH */ -#ifdef OC_SERVER - -typedef struct oc_resource_defaults_data_t -{ - oc_resource_t *resource; - oc_interface_mask_t iface_mask; -} oc_resource_defaults_data_t; - -OC_LIST(g_app_resources); -OC_LIST(g_app_resources_to_be_deleted); -OC_MEMB(g_app_resources_s, oc_resource_t, OC_MAX_APP_RESOURCES); -OC_MEMB(g_resource_default_s, oc_resource_defaults_data_t, - OC_MAX_APP_RESOURCES); -#endif /* OC_SERVER */ - static coap_status_t oc_coap_status_codes[__NUM_OC_STATUS_CODES__] = { 0 }; static const char *oc_status_strs[] = { @@ -203,111 +181,6 @@ ri_set_status_codes(void) PROXYING_NOT_SUPPORTED_5_05; } -#ifdef OC_SERVER - -oc_resource_t * -oc_ri_get_app_resources(void) -{ - return oc_list_head(g_app_resources); -} - -static bool -ri_app_resource_is_in_list(oc_list_t list, const oc_resource_t *resource) -{ - const oc_resource_t *res = oc_list_head(list); - for (; res != NULL; res = res->next) { - if (res == resource) { - return true; - } - } - return false; -} - -bool -oc_ri_is_app_resource_valid(const oc_resource_t *resource) -{ - return ri_app_resource_is_in_list(g_app_resources, resource); -} - -bool -oc_ri_is_app_resource_to_be_deleted(const oc_resource_t *resource) -{ - return ri_app_resource_is_in_list(g_app_resources_to_be_deleted, resource); -} - -static bool -ri_uri_is_in_list(oc_list_t list, const char *uri, size_t uri_len, - size_t device) -{ - while (uri[0] == '/') { - uri++; - uri_len--; - } - - const oc_resource_t *res = oc_list_head(list); - for (; res != NULL; res = res->next) { - if (res->device == device && oc_string_len(res->uri) == (uri_len + 1) && - strncmp(oc_string(res->uri) + 1, uri, uri_len) == 0) { - return true; - } - } - return false; -} - -bool -oc_ri_URI_is_in_use(size_t device, const char *uri, size_t uri_len) -{ - // check core resources - if (oc_core_get_resource_by_uri_v1(uri, uri_len, device) != NULL) { - return true; - } - // dynamic resources / dynamic resources scheduled to be deleted - if (ri_uri_is_in_list(g_app_resources, uri, uri_len, device) || - ri_uri_is_in_list(g_app_resources_to_be_deleted, uri, uri_len, device)) { - return true; - } - -#ifdef OC_COLLECTIONS - // collections - if (oc_get_collection_by_uri(uri, uri_len, device) != NULL) { - return true; - } -#endif /* OC_COLLECTIONS */ - return false; -} - -static void -ri_app_resource_to_be_deleted(oc_resource_t *resource) -{ - oc_list_remove2(g_app_resources, resource); - if (!oc_ri_is_app_resource_to_be_deleted(resource)) { - oc_list_add(g_app_resources_to_be_deleted, resource); - } -} - -static oc_event_callback_retval_t -oc_delayed_delete_resource_cb(void *data) -{ - oc_resource_t *resource = (oc_resource_t *)data; - OC_DBG("delayed delete resource(%p)", (void *)resource); - oc_ri_on_delete_resource_invoke(resource); - oc_delete_resource(resource); - return OC_EVENT_DONE; -} - -void -oc_delayed_delete_resource(oc_resource_t *resource) -{ - if (resource == NULL) { - return; - } - OC_DBG("(re)scheduling delayed delete resource(%p)", (void *)resource); - ri_app_resource_to_be_deleted(resource); - oc_reset_delayed_callback(resource, oc_delayed_delete_resource_cb, 0); -} - -#endif /* OC_SERVER */ - coap_status_t oc_status_code_unsafe(oc_status_t key) { @@ -445,46 +318,13 @@ stop_processes(void) oc_message_buffer_handler_stop(); } -#ifdef OC_SERVER -oc_resource_t * -oc_ri_get_app_resource_by_uri(const char *uri, size_t uri_len, size_t device) -{ - if (!uri || uri_len == 0) { - return NULL; - } - - int skip = 0; - if (uri[0] != '/') { - skip = 1; - } - oc_resource_t *res = oc_ri_get_app_resources(); - while (res != NULL) { - if (oc_string_len(res->uri) == (uri_len + skip) && - strncmp(uri, oc_string(res->uri) + skip, uri_len) == 0 && - res->device == device) { - return res; - } - res = res->next; - } - -#ifdef OC_COLLECTIONS - oc_collection_t *col = oc_get_collection_by_uri(uri, uri_len, device); - if (col != NULL) { - return &col->res; - } -#endif /* OC_COLLECTIONS */ - return NULL; -} -#endif /* OC_SERVER */ - void oc_ri_init(void) { ri_set_status_codes(); #ifdef OC_SERVER - oc_list_init(g_app_resources); - oc_list_init(g_app_resources_to_be_deleted); + oc_ri_server_init(); #endif /* OC_SERVER */ #ifdef OC_CLIENT @@ -519,177 +359,6 @@ oc_method_to_str(oc_method_t method) return method_strs[method]; } -#ifdef OC_SERVER - -oc_resource_t * -oc_ri_alloc_resource(void) -{ - return oc_memb_alloc(&g_app_resources_s); -} - -void -oc_ri_dealloc_resource(oc_resource_t *resource) -{ - oc_memb_free(&g_app_resources_s, resource); -} - -static oc_resource_defaults_data_t * -oc_ri_alloc_resource_defaults(void) -{ - return oc_memb_alloc(&g_resource_default_s); -} - -static void -oc_ri_dealloc_resource_defaults(oc_resource_defaults_data_t *data) -{ - oc_memb_free(&g_resource_default_s, data); -} - -static void -ri_delete_resource(oc_resource_t *resource, bool notify) -{ - OC_DBG("delete resource(%p)", (void *)resource); - -#ifdef OC_COLLECTIONS -#ifdef OC_COLLECTIONS_IF_CREATE - oc_rt_created_t *rtc = oc_rt_get_factory_create_for_resource(resource); - if (rtc != NULL) { - /* For dynamically created resources invoke the created instance destructor - * and return. The destructor invokes at the end oc_delete_resource again, - * but the resource will no longer be in the list of created resources so - * this if-branch will be skipped and normal resource deallocation will be - * executed. */ - oc_rt_factory_free_created_resource(rtc, rtc->rf); - return; - } -#endif /* OC_COLLECTIONS_IF_CREATE */ - -#if defined(OC_RES_BATCH_SUPPORT) && defined(OC_DISCOVERY_RESOURCE_OBSERVABLE) - bool needsBatchDispatch = false; -#endif /* OC_RES_BATCH_SUPPORT && OC_DISCOVERY_RESOURCE_OBSERVABLE */ - // remove the resource from the collections - oc_collection_t *collection = - oc_get_next_collection_with_link(resource, NULL); - while (collection != NULL) { - oc_link_t *link = oc_get_link_by_uri(collection, oc_string(resource->uri), - oc_string_len(resource->uri)); - if (link != NULL) { - if (oc_collection_remove_link_and_notify( - &collection->res, link, notify, - /*discoveryBatchDispatch*/ false)) { -#if defined(OC_RES_BATCH_SUPPORT) && defined(OC_DISCOVERY_RESOURCE_OBSERVABLE) - needsBatchDispatch = true; -#endif /* OC_RES_BATCH_SUPPORT && OC_DISCOVERY_RESOURCE_OBSERVABLE */ - } - oc_delete_link(link); - } - collection = oc_get_next_collection_with_link(resource, collection); - } -#endif /* OC_COLLECTIONS */ - - bool removed = oc_list_remove2(g_app_resources, resource) != NULL; - removed = - oc_list_remove2(g_app_resources_to_be_deleted, resource) != NULL || removed; - - oc_remove_delayed_callback(resource, oc_delayed_delete_resource_cb); - oc_notify_clear(resource); - - if (resource->num_observers > 0) { - int removed_num = coap_remove_observers_by_resource(resource); - OC_DBG("removing resource observers: removed(%d) vs expected(%d)", - removed_num, resource->num_observers); -#if !OC_DBG_IS_ENABLED - (void)removed_num; -#endif /* !OC_DBG_IS_ENABLED */ - } - - if (notify) { - if (removed) { - oc_notify_resource_removed(resource); - } else { -#if defined(OC_COLLECTIONS) && defined(OC_RES_BATCH_SUPPORT) && \ - defined(OC_DISCOVERY_RESOURCE_OBSERVABLE) - // if oc_notify_resource_removed is not called, then we need to dispatch - // manually if it is requested - if (needsBatchDispatch) { - coap_dispatch_process_batch_observers(); - } -#endif /* OC_COLLECTIONS && OC_RES_BATCH_SUPPORT && \ - OC_DISCOVERY_RESOURCE_OBSERVABLE */ - } - } - - oc_ri_free_resource_properties(resource); - oc_ri_dealloc_resource(resource); -} - -bool -oc_ri_delete_resource(oc_resource_t *resource) -{ - if (resource == NULL) { - return false; - } - ri_delete_resource(resource, true); - return true; -} - -bool -oc_ri_add_resource(oc_resource_t *resource) -{ - if (resource == NULL) { - return false; - } - - if (!resource->get_handler.cb && !resource->put_handler.cb && - !resource->post_handler.cb && !resource->delete_handler.cb) { - OC_ERR("resource(%s) has no handlers", oc_string(resource->uri)); - return false; - } - - if ((resource->properties & OC_PERIODIC) != 0 && - resource->observe_period_seconds == 0) { - OC_ERR("resource(%s) has invalid observe period", oc_string(resource->uri)); - return false; - } - - if (oc_ri_is_app_resource_valid(resource)) { - OC_ERR("resource(%s) already exists in IoTivity stack", - oc_string(resource->uri)); - return false; - } - if (oc_ri_is_app_resource_to_be_deleted(resource)) { - OC_ERR("resource(%s) is scheduled to be deleted", oc_string(resource->uri)); - return false; - } - if (oc_ri_URI_is_in_use(resource->device, oc_string(resource->uri), - oc_string_len(resource->uri))) { - OC_ERR("resource(%s) URI is already in use", oc_string(resource->uri)); - return false; - } - - oc_list_add(g_app_resources, resource); - oc_notify_resource_added(resource); - return true; -} - -static void -ri_delete_all_app_resources(void) -{ - oc_resource_t *res = oc_ri_get_app_resources(); - while (res) { - ri_delete_resource(res, false); - res = oc_ri_get_app_resources(); - } - - res = oc_list_head(g_app_resources_to_be_deleted); - while (res) { - ri_delete_resource(res, false); - res = oc_list_head(g_app_resources_to_be_deleted); - } -} - -#endif /* OC_SERVER */ - void oc_ri_free_resource_properties(oc_resource_t *resource) { @@ -853,225 +522,6 @@ ri_get_ocf_version_from_header(const coap_packet_t *request) return OCF_VER_1_0_0; } -#ifdef OC_SERVER - -#ifdef OC_COLLECTIONS -static bool -ri_add_collection_observation(oc_collection_t *collection, - const oc_endpoint_t *endpoint, bool is_batch) -{ - oc_link_t *links = (oc_link_t *)oc_list_head(collection->links); -#ifdef OC_SECURITY - for (; links != NULL; links = links->next) { - if (links->resource == NULL || - (links->resource->properties & OC_OBSERVABLE) == 0 || - oc_sec_check_acl(OC_GET, links->resource, endpoint)) { - continue; - } - return false; - } -#else /* !OC_SECURITY */ - (void)endpoint; -#endif /* OC_SECURITY */ - if (is_batch) { - links = (oc_link_t *)oc_list_head(collection->links); - for (; links != NULL; links = links->next) { - if (links->resource == NULL || - (links->resource->properties & OC_PERIODIC) == 0) { - continue; - } - if (!oc_periodic_observe_callback_add(links->resource)) { - // TODO: shouldn't we remove the periodic observe of links added by this - // call? - return false; - } - } - } - return true; -} - -#endif /* OC_COLLECTIONS */ - -static int -ri_observe_handler(const coap_packet_t *request, const coap_packet_t *response, - oc_resource_t *resource, uint16_t block2_size, - const oc_endpoint_t *endpoint, - oc_interface_mask_t iface_mask) -{ - if (request->code != COAP_GET || response->code >= 128 || - !IS_OPTION(request, COAP_OPTION_OBSERVE)) { - return -1; - } - if (request->observe == OC_COAP_OPTION_OBSERVE_REGISTER) { - if (NULL == coap_add_observer(resource, block2_size, endpoint, - request->token, request->token_len, - request->uri_path, request->uri_path_len, - iface_mask)) { - OC_ERR("failed to add observer"); - return -1; - } - return 0; - } - if (request->observe == OC_COAP_OPTION_OBSERVE_UNREGISTER) { - if (!coap_remove_observer_by_token(endpoint, request->token, - request->token_len)) { - return 0; - } - return 1; - } - return -1; -} - -static bool -ri_add_observation(const coap_packet_t *request, const coap_packet_t *response, - oc_resource_t *resource, bool resource_is_collection, - uint16_t block2_size, const oc_endpoint_t *endpoint, - oc_interface_mask_t iface_query) -{ - if (ri_observe_handler(request, response, resource, block2_size, endpoint, - iface_query) >= 0) { - /* If the resource is marked as periodic observable it means it must be - * polled internally for updates (which would lead to notifications being - * sent). If so, add the resource to a list of periodic GET callbacks to - * utilize the framework's internal polling mechanism. - */ - if ((resource->properties & OC_PERIODIC) != 0 && - !oc_periodic_observe_callback_add(resource)) { - return false; - } - } -#ifdef OC_COLLECTIONS - if (resource_is_collection) { - oc_collection_t *collection = (oc_collection_t *)resource; - if (!ri_add_collection_observation(collection, endpoint, - iface_query == OC_IF_B)) { - // TODO: shouldn't we remove the periodic observe callback here? - return false; - } - } -#else /* !OC_COLLECTIONS */ - (void)resource_is_collection; -#endif /* OC_COLLECTIONS */ - return true; -} - -static void -ri_remove_observation(const coap_packet_t *request, - const coap_packet_t *response, oc_resource_t *resource, - bool resource_is_collection, uint16_t block2_size, - const oc_endpoint_t *endpoint, - oc_interface_mask_t iface_query) -{ - if (ri_observe_handler(request, response, resource, block2_size, endpoint, - iface_query) <= 0) { - return; - } - if ((resource->properties & OC_PERIODIC) != 0) { - oc_periodic_observe_callback_remove(resource); - } -#if defined(OC_COLLECTIONS) - if (resource_is_collection) { - oc_collection_t *collection = (oc_collection_t *)resource; - oc_link_t *links = (oc_link_t *)oc_list_head(collection->links); - for (; links != NULL; links = links->next) { - if (links->resource != NULL && - (links->resource->properties & OC_PERIODIC) != 0) { - oc_periodic_observe_callback_remove(links->resource); - } - } - } -#else /* !OC_COLLECTIONS */ - (void)resource_is_collection; -#endif /* OC_COLLECTIONS */ -} - -static int -ri_handle_observation(const coap_packet_t *request, coap_packet_t *response, - oc_resource_t *resource, bool resource_is_collection, - uint16_t block2_size, const oc_endpoint_t *endpoint, - oc_interface_mask_t iface_query) -{ - - /* If a GET request was successfully processed, then check if the resource is - * OBSERVABLE and check its observe option. - */ - int32_t observe = OC_COAP_OPTION_OBSERVE_NOT_SET; - if ((resource->properties & OC_OBSERVABLE) == 0 || - !coap_options_get_observe(request, &observe)) { - return OC_COAP_OPTION_OBSERVE_NOT_SET; - } - - /* If the observe option is set to 0, make an attempt to add the requesting - * client as an observer. - */ - if (observe == OC_COAP_OPTION_OBSERVE_REGISTER) { - if (!ri_add_observation(request, response, resource, resource_is_collection, - block2_size, endpoint, iface_query)) { - coap_remove_observer_by_token(endpoint, request->token, - request->token_len); - return OC_COAP_OPTION_OBSERVE_NOT_SET; - } - coap_options_set_observe(response, OC_COAP_OPTION_OBSERVE_REGISTER); - return OC_COAP_OPTION_OBSERVE_REGISTER; - } - - /* If the observe option is set to 1, make an attempt to remove the - * requesting client from the list of observers. In addition, remove the - * resource from the list periodic GET callbacks if it is periodic observable. - */ - if (observe == OC_COAP_OPTION_OBSERVE_UNREGISTER) { - ri_remove_observation(request, response, resource, resource_is_collection, - block2_size, endpoint, iface_query); - return OC_COAP_OPTION_OBSERVE_UNREGISTER; - } - - // if the observe option is >= 2 then we a have a notification - return observe; -} - -static oc_event_callback_retval_t -ri_observe_notification_resource_defaults_delayed(void *data) -{ - oc_resource_defaults_data_t *resource_defaults_data = - (oc_resource_defaults_data_t *)data; - notify_resource_defaults_observer(resource_defaults_data->resource, - resource_defaults_data->iface_mask); - oc_ri_dealloc_resource_defaults(resource_defaults_data); - return OC_EVENT_DONE; -} - -#ifdef OC_HAS_FEATURE_ETAG - -uint64_t -oc_ri_get_etag(const oc_resource_t *resource) -{ - return resource->etag; -} - -uint64_t -oc_ri_get_batch_etag(const oc_resource_t *resource, - const oc_endpoint_t *endpoint, size_t device) -{ -#ifdef OC_RES_BATCH_SUPPORT - if (oc_core_get_resource_by_index(OCF_RES, device) == resource) { - return oc_discovery_get_batch_etag(endpoint, device); - } -#endif /* OC_RES_BATCH_SUPPORT */ -#ifdef OC_COLLECTIONS - if (oc_check_if_collection(resource)) { - return oc_collection_get_batch_etag((const oc_collection_t *)resource); - } -#endif /* OC_COLLECTIONS */ - (void)resource; - (void)endpoint; - (void)device; - OC_DBG("custom batch etag"); - return OC_ETAG_UNINITIALIZED; -} - -#endif /* OC_HAS_FEATURE_ETAG */ -#endif /* OC_SERVER */ - typedef struct { oc_response_t *response_obj; @@ -1171,28 +621,16 @@ ri_invoke_coap_entity_set_response(coap_packet_t *response, } #endif /* OC_HAS_FEATURE_ETAG */ - /* If the recently handled request was a PUT/POST, it conceivably - * altered the resource state, so attempt to notify all observers - * of that resource with the change. - */ if ( #ifdef OC_COLLECTIONS !ctx.resource_is_collection && #endif /* OC_COLLECTIONS */ (ctx.method == OC_PUT || ctx.method == OC_POST) && response_buffer->code < oc_status_code_unsafe(OC_STATUS_BAD_REQUEST)) { - if ((ctx.iface_mask == OC_IF_STARTUP) || - (ctx.iface_mask == OC_IF_STARTUP_REVERT)) { - oc_resource_defaults_data_t *resource_defaults_data = - oc_ri_alloc_resource_defaults(); - resource_defaults_data->resource = ctx.resource; - resource_defaults_data->iface_mask = ctx.iface_mask; - oc_ri_add_timed_event_callback_ticks( - resource_defaults_data, - &ri_observe_notification_resource_defaults_delayed, 0); - } else { - oc_notify_resource_changed_delayed_ms(ctx.resource, 0); - } + /* If the recently handled request was a PUT/POST, it conceivably + * altered the resource state, so attempt to notify all observers + * of that resource with the change. */ + oc_ri_notify_resource_observers(ctx.resource, ctx.iface_mask); } } #endif /* OC_SERVER */ @@ -1863,7 +1301,7 @@ ri_invoke_coap_entity_handler(coap_make_response_ctx_t *ctx, if (success && method == OC_GET && request_obj.response->response_buffer->code < oc_status_code_unsafe(OC_STATUS_BAD_REQUEST)) { - observe = ri_handle_observation( + observe = oc_ri_handle_observation( ctx->request, ctx->response, cur_resource, get_resource_is_collection(ctx->preparsed_request_obj), block2_size, endpoint, ctx->preparsed_request_obj->iface_query); @@ -1919,10 +1357,7 @@ void oc_ri_reset(void) { #ifdef OC_SERVER -#if defined(OC_RES_BATCH_SUPPORT) && defined(OC_DISCOVERY_RESOURCE_OBSERVABLE) - coap_free_all_discovery_batch_observers(); -#endif /* OC_RES_BATCH_SUPPORT && OC_DISCOVERY_RESOURCE_OBSERVABLE */ - coap_free_all_observers(); + oc_ri_server_reset(); #endif /* OC_SERVER */ coap_free_all_transactions(); oc_event_callbacks_shutdown(); @@ -1948,12 +1383,6 @@ oc_ri_shutdown(void) oc_process_shutdown(); #ifdef OC_SERVER - oc_ri_on_delete_resource_remove_all(); - -#ifdef OC_COLLECTIONS - oc_collections_free_all(); -#endif /* OC_COLLECTIONS */ - - ri_delete_all_app_resources(); + oc_ri_server_shutdown(); #endif /* OC_SERVER */ } diff --git a/api/oc_ri_internal.h b/api/oc_ri_internal.h index e3c1d0781..4109b5d7d 100644 --- a/api/oc_ri_internal.h +++ b/api/oc_ri_internal.h @@ -129,19 +129,6 @@ uint64_t oc_ri_get_batch_etag(const oc_resource_t *resource, */ void oc_ri_free_resource_properties(oc_resource_t *resource) OC_NONNULL(); -/** - * @brief Check if given URI is in use by given device - * - * @param device index of device - * @param uri URI to check (cannot be NULL) - * @param uri_len length of URI - * - * @return true if URI is in use - * @return false otherwise - */ -bool oc_ri_URI_is_in_use(size_t device, const char *uri, size_t uri_len) - OC_NONNULL(); - /** @brief Handle a coap request. */ bool oc_ri_invoke_coap_entity_handler(coap_make_response_ctx_t *ctx, const oc_endpoint_t *endpoint, diff --git a/api/oc_ri_server.c b/api/oc_ri_server.c index 7ed34f67f..cb2752c96 100644 --- a/api/oc_ri_server.c +++ b/api/oc_ri_server.c @@ -17,19 +17,249 @@ * ***************************************************************************/ -#include "oc_config.h" +#include "util/oc_features.h" #ifdef OC_SERVER +#include "api/oc_discovery_internal.h" +#include "api/oc_event_callback_internal.h" +#include "api/oc_server_api_internal.h" +#include "messaging/coap/options_internal.h" +#include "messaging/coap/observe_internal.h" +#include "oc_api.h" +#include "oc_core_res.h" #include "oc_ri_server_internal.h" #include "port/oc_log_internal.h" #include "util/oc_list.h" #include "util/oc_memb.h" +#ifdef OC_COLLECTIONS +#include "api/oc_collection_internal.h" +#include "api/oc_link_internal.h" +#ifdef OC_COLLECTIONS_IF_CREATE +#include "oc_resource_factory_internal.h" +#endif /* OC_COLLECTIONS_IF_CREATE */ +#endif /* OC_COLLECTIONS */ + +#ifdef OC_SECURITY +#include "security/oc_acl_util_internal.h" +#endif /* OC_SECURITY */ + +#ifdef OC_HAS_FEATURE_ETAG +#include "api/oc_etag_internal.h" +#endif /* OC_HAS_FEATURE_ETAG */ + +typedef struct oc_resource_defaults_data_t +{ + oc_resource_t *resource; + oc_interface_mask_t iface_mask; +} oc_resource_defaults_data_t; + +OC_LIST(g_app_resources); +OC_LIST(g_app_resources_to_be_deleted); +OC_MEMB(g_resource_default_s, oc_resource_defaults_data_t, + OC_MAX_APP_RESOURCES); + +OC_MEMB(g_app_resources_s, oc_resource_t, OC_MAX_APP_RESOURCES); + OC_LIST(g_on_delete_resource_cb_list); OC_MEMB(g_on_delete_resource_cb_s, oc_ri_on_delete_resource_t, OC_MAX_ON_DELETE_RESOURCE_CBS); +oc_resource_t * +oc_ri_alloc_resource(void) +{ + return oc_memb_alloc(&g_app_resources_s); +} + +void +oc_ri_dealloc_resource(oc_resource_t *resource) +{ + oc_memb_free(&g_app_resources_s, resource); +} + +static oc_resource_defaults_data_t * +oc_ri_alloc_resource_defaults(void) +{ + return oc_memb_alloc(&g_resource_default_s); +} + +static void +oc_ri_dealloc_resource_defaults(oc_resource_defaults_data_t *data) +{ + oc_memb_free(&g_resource_default_s, data); +} + +bool +oc_ri_add_resource(oc_resource_t *resource) +{ + if (resource == NULL) { + return false; + } + + if (!resource->get_handler.cb && !resource->put_handler.cb && + !resource->post_handler.cb && !resource->delete_handler.cb) { + OC_ERR("resource(%s) has no handlers", oc_string(resource->uri)); + return false; + } + + if ((resource->properties & OC_PERIODIC) != 0 && + resource->observe_period_seconds == 0) { + OC_ERR("resource(%s) has invalid observe period", oc_string(resource->uri)); + return false; + } + + if (oc_ri_is_app_resource_valid(resource)) { + OC_ERR("resource(%s) already exists in IoTivity stack", + oc_string(resource->uri)); + return false; + } + if (oc_ri_is_app_resource_to_be_deleted(resource)) { + OC_ERR("resource(%s) is scheduled to be deleted", oc_string(resource->uri)); + return false; + } + if (oc_ri_URI_is_in_use(resource->device, oc_string(resource->uri), + oc_string_len(resource->uri))) { + OC_ERR("resource(%s) URI is already in use", oc_string(resource->uri)); + return false; + } + + oc_list_add(g_app_resources, resource); + oc_notify_resource_added(resource); + return true; +} + +oc_resource_t * +oc_ri_get_app_resources(void) +{ + return oc_list_head(g_app_resources); +} + +oc_resource_t * +oc_ri_get_app_resource_by_uri(const char *uri, size_t uri_len, size_t device) +{ + if (!uri || uri_len == 0) { + return NULL; + } + + int skip = 0; + if (uri[0] != '/') { + skip = 1; + } + oc_resource_t *res = oc_ri_get_app_resources(); + while (res != NULL) { + if (oc_string_len(res->uri) == (uri_len + skip) && + strncmp(uri, oc_string(res->uri) + skip, uri_len) == 0 && + res->device == device) { + return res; + } + res = res->next; + } + +#ifdef OC_COLLECTIONS + oc_collection_t *col = oc_get_collection_by_uri(uri, uri_len, device); + if (col != NULL) { + return &col->res; + } +#endif /* OC_COLLECTIONS */ + return NULL; +} + +static bool +ri_app_resource_is_in_list(oc_list_t list, const oc_resource_t *resource) +{ + const oc_resource_t *res = oc_list_head(list); + for (; res != NULL; res = res->next) { + if (res == resource) { + return true; + } + } + return false; +} + +bool +oc_ri_is_app_resource_valid(const oc_resource_t *resource) +{ + return ri_app_resource_is_in_list(g_app_resources, resource); +} + +bool +oc_ri_is_app_resource_to_be_deleted(const oc_resource_t *resource) +{ + return ri_app_resource_is_in_list(g_app_resources_to_be_deleted, resource); +} + +static bool +ri_uri_is_in_list(oc_list_t list, const char *uri, size_t uri_len, + size_t device) +{ + while (uri[0] == '/') { + uri++; + uri_len--; + } + + const oc_resource_t *res = oc_list_head(list); + for (; res != NULL; res = res->next) { + if (res->device == device && oc_string_len(res->uri) == (uri_len + 1) && + strncmp(oc_string(res->uri) + 1, uri, uri_len) == 0) { + return true; + } + } + return false; +} + +bool +oc_ri_URI_is_in_use(size_t device, const char *uri, size_t uri_len) +{ + // check core resources + if (oc_core_get_resource_by_uri_v1(uri, uri_len, device) != NULL) { + return true; + } + // dynamic resources / dynamic resources scheduled to be deleted + if (ri_uri_is_in_list(g_app_resources, uri, uri_len, device) || + ri_uri_is_in_list(g_app_resources_to_be_deleted, uri, uri_len, device)) { + return true; + } + +#ifdef OC_COLLECTIONS + // collections + if (oc_get_collection_by_uri(uri, uri_len, device) != NULL) { + return true; + } +#endif /* OC_COLLECTIONS */ + return false; +} + +static void +ri_app_resource_to_be_deleted(oc_resource_t *resource) +{ + oc_list_remove2(g_app_resources, resource); + if (!oc_ri_is_app_resource_to_be_deleted(resource)) { + oc_list_add(g_app_resources_to_be_deleted, resource); + } +} + +static oc_event_callback_retval_t +oc_delayed_delete_resource_cb(void *data) +{ + oc_resource_t *resource = (oc_resource_t *)data; + OC_DBG("delayed delete resource(%p)", (void *)resource); + oc_ri_on_delete_resource_invoke(resource); + oc_delete_resource(resource); + return OC_EVENT_DONE; +} + +void +oc_delayed_delete_resource(oc_resource_t *resource) +{ + if (resource == NULL) { + return; + } + OC_DBG("(re)scheduling delayed delete resource(%p)", (void *)resource); + ri_app_resource_to_be_deleted(resource); + oc_reset_delayed_callback(resource, oc_delayed_delete_resource_cb, 0); +} + bool oc_ri_on_delete_resource_add_callback(oc_ri_delete_resource_cb_t cb) { @@ -96,4 +326,370 @@ oc_ri_on_delete_resource_invoke(oc_resource_t *resource) } } +static void +ri_delete_resource(oc_resource_t *resource, bool notify) +{ + OC_DBG("delete resource(%p)", (void *)resource); + +#ifdef OC_COLLECTIONS +#ifdef OC_COLLECTIONS_IF_CREATE + oc_rt_created_t *rtc = oc_rt_get_factory_create_for_resource(resource); + if (rtc != NULL) { + /* For dynamically created resources invoke the created instance destructor + * and return. The destructor invokes at the end oc_delete_resource again, + * but the resource will no longer be in the list of created resources so + * this if-branch will be skipped and normal resource deallocation will be + * executed. */ + oc_rt_factory_free_created_resource(rtc, rtc->rf); + return; + } +#endif /* OC_COLLECTIONS_IF_CREATE */ + +#if defined(OC_RES_BATCH_SUPPORT) && defined(OC_DISCOVERY_RESOURCE_OBSERVABLE) + bool needsBatchDispatch = false; +#endif /* OC_RES_BATCH_SUPPORT && OC_DISCOVERY_RESOURCE_OBSERVABLE */ + // remove the resource from the collections + oc_collection_t *collection = + oc_get_next_collection_with_link(resource, NULL); + while (collection != NULL) { + oc_link_t *link = oc_get_link_by_uri(collection, oc_string(resource->uri), + oc_string_len(resource->uri)); + if (link != NULL) { + if (oc_collection_remove_link_and_notify( + &collection->res, link, notify, + /*discoveryBatchDispatch*/ false)) { +#if defined(OC_RES_BATCH_SUPPORT) && defined(OC_DISCOVERY_RESOURCE_OBSERVABLE) + needsBatchDispatch = true; +#endif /* OC_RES_BATCH_SUPPORT && OC_DISCOVERY_RESOURCE_OBSERVABLE */ + } + oc_delete_link(link); + } + collection = oc_get_next_collection_with_link(resource, collection); + } +#endif /* OC_COLLECTIONS */ + + bool removed = oc_list_remove2(g_app_resources, resource) != NULL; + removed = + oc_list_remove2(g_app_resources_to_be_deleted, resource) != NULL || removed; + + oc_remove_delayed_callback(resource, oc_delayed_delete_resource_cb); + oc_notify_clear(resource); + + if (resource->num_observers > 0) { + int removed_num = coap_remove_observers_by_resource(resource); + OC_DBG("removing resource observers: removed(%d) vs expected(%d)", + removed_num, resource->num_observers); +#if !OC_DBG_IS_ENABLED + (void)removed_num; +#endif /* !OC_DBG_IS_ENABLED */ + } + + if (notify) { + if (removed) { + oc_notify_resource_removed(resource); + } else { +#if defined(OC_COLLECTIONS) && defined(OC_RES_BATCH_SUPPORT) && \ + defined(OC_DISCOVERY_RESOURCE_OBSERVABLE) + // if oc_notify_resource_removed is not called, then we need to dispatch + // manually if it is requested + if (needsBatchDispatch) { + coap_dispatch_process_batch_observers(); + } +#endif /* OC_COLLECTIONS && OC_RES_BATCH_SUPPORT && \ + OC_DISCOVERY_RESOURCE_OBSERVABLE */ + } + } + + oc_ri_free_resource_properties(resource); + oc_ri_dealloc_resource(resource); +} + +bool +oc_ri_delete_resource(oc_resource_t *resource) +{ + if (resource == NULL) { + return false; + } + ri_delete_resource(resource, true); + return true; +} + +static void +ri_delete_all_app_resources(void) +{ + oc_resource_t *res = oc_ri_get_app_resources(); + while (res) { + ri_delete_resource(res, false); + res = oc_ri_get_app_resources(); + } + + res = oc_list_head(g_app_resources_to_be_deleted); + while (res) { + ri_delete_resource(res, false); + res = oc_list_head(g_app_resources_to_be_deleted); + } +} + +static oc_event_callback_retval_t +ri_observe_notification_resource_defaults_delayed(void *data) +{ + oc_resource_defaults_data_t *resource_defaults_data = + (oc_resource_defaults_data_t *)data; + notify_resource_defaults_observer(resource_defaults_data->resource, + resource_defaults_data->iface_mask); + oc_ri_dealloc_resource_defaults(resource_defaults_data); + return OC_EVENT_DONE; +} + +void +oc_ri_notify_resource_observers(oc_resource_t *resource, + oc_interface_mask_t iface_mask) +{ + if ((iface_mask == OC_IF_STARTUP) || (iface_mask == OC_IF_STARTUP_REVERT)) { + oc_resource_defaults_data_t *resource_defaults_data = + oc_ri_alloc_resource_defaults(); + resource_defaults_data->resource = resource; + resource_defaults_data->iface_mask = iface_mask; + oc_ri_add_timed_event_callback_ticks( + resource_defaults_data, + &ri_observe_notification_resource_defaults_delayed, 0); + } else { + oc_notify_resource_changed_delayed_ms(resource, 0); + } +} + +void +oc_ri_server_init(void) +{ + + oc_list_init(g_app_resources); + oc_list_init(g_app_resources_to_be_deleted); +} + +void +oc_ri_server_reset(void) +{ +#if defined(OC_RES_BATCH_SUPPORT) && defined(OC_DISCOVERY_RESOURCE_OBSERVABLE) + coap_free_all_discovery_batch_observers(); +#endif /* OC_RES_BATCH_SUPPORT && OC_DISCOVERY_RESOURCE_OBSERVABLE */ + coap_free_all_observers(); +} + +void +oc_ri_server_shutdown(void) +{ + oc_ri_on_delete_resource_remove_all(); + +#ifdef OC_COLLECTIONS + oc_collections_free_all(); +#endif /* OC_COLLECTIONS */ + + ri_delete_all_app_resources(); +} + +#ifdef OC_COLLECTIONS +static bool +ri_add_collection_observation(oc_collection_t *collection, + const oc_endpoint_t *endpoint, bool is_batch) +{ + oc_link_t *links = (oc_link_t *)oc_list_head(collection->links); +#ifdef OC_SECURITY + for (; links != NULL; links = links->next) { + if (links->resource == NULL || + (links->resource->properties & OC_OBSERVABLE) == 0 || + oc_sec_check_acl(OC_GET, links->resource, endpoint)) { + continue; + } + return false; + } +#else /* !OC_SECURITY */ + (void)endpoint; +#endif /* OC_SECURITY */ + if (is_batch) { + links = (oc_link_t *)oc_list_head(collection->links); + for (; links != NULL; links = links->next) { + if (links->resource == NULL || + (links->resource->properties & OC_PERIODIC) == 0) { + continue; + } + if (!oc_periodic_observe_callback_add(links->resource)) { + // TODO: shouldn't we remove the periodic observe of links added by this + // call? + return false; + } + } + } + return true; +} + +#endif /* OC_COLLECTIONS */ + +static int +ri_observe_handler(const coap_packet_t *request, const coap_packet_t *response, + oc_resource_t *resource, uint16_t block2_size, + const oc_endpoint_t *endpoint, + oc_interface_mask_t iface_mask) +{ + if (request->code != COAP_GET || response->code >= 128 || + !IS_OPTION(request, COAP_OPTION_OBSERVE)) { + return -1; + } + if (request->observe == OC_COAP_OPTION_OBSERVE_REGISTER) { + if (NULL == coap_add_observer(resource, block2_size, endpoint, + request->token, request->token_len, + request->uri_path, request->uri_path_len, + iface_mask)) { + OC_ERR("failed to add observer"); + return -1; + } + return 0; + } + if (request->observe == OC_COAP_OPTION_OBSERVE_UNREGISTER) { + if (!coap_remove_observer_by_token(endpoint, request->token, + request->token_len)) { + return 0; + } + return 1; + } + return -1; +} + +static bool +ri_add_observation(const coap_packet_t *request, const coap_packet_t *response, + oc_resource_t *resource, bool resource_is_collection, + uint16_t block2_size, const oc_endpoint_t *endpoint, + oc_interface_mask_t iface_query) +{ + if (ri_observe_handler(request, response, resource, block2_size, endpoint, + iface_query) >= 0) { + /* If the resource is marked as periodic observable it means it must be + * polled internally for updates (which would lead to notifications being + * sent). If so, add the resource to a list of periodic GET callbacks to + * utilize the framework's internal polling mechanism. + */ + if ((resource->properties & OC_PERIODIC) != 0 && + !oc_periodic_observe_callback_add(resource)) { + return false; + } + } +#ifdef OC_COLLECTIONS + if (resource_is_collection) { + oc_collection_t *collection = (oc_collection_t *)resource; + if (!ri_add_collection_observation(collection, endpoint, + iface_query == OC_IF_B)) { + // TODO: shouldn't we remove the periodic observe callback here? + return false; + } + } +#else /* !OC_COLLECTIONS */ + (void)resource_is_collection; +#endif /* OC_COLLECTIONS */ + return true; +} + +static void +ri_remove_observation(const coap_packet_t *request, + const coap_packet_t *response, oc_resource_t *resource, + bool resource_is_collection, uint16_t block2_size, + const oc_endpoint_t *endpoint, + oc_interface_mask_t iface_query) +{ + if (ri_observe_handler(request, response, resource, block2_size, endpoint, + iface_query) <= 0) { + return; + } + if ((resource->properties & OC_PERIODIC) != 0) { + oc_periodic_observe_callback_remove(resource); + } +#if defined(OC_COLLECTIONS) + if (resource_is_collection) { + oc_collection_t *collection = (oc_collection_t *)resource; + oc_link_t *links = (oc_link_t *)oc_list_head(collection->links); + for (; links != NULL; links = links->next) { + if (links->resource != NULL && + (links->resource->properties & OC_PERIODIC) != 0) { + oc_periodic_observe_callback_remove(links->resource); + } + } + } +#else /* !OC_COLLECTIONS */ + (void)resource_is_collection; +#endif /* OC_COLLECTIONS */ +} + +int +oc_ri_handle_observation(const coap_packet_t *request, coap_packet_t *response, + oc_resource_t *resource, bool resource_is_collection, + uint16_t block2_size, const oc_endpoint_t *endpoint, + oc_interface_mask_t iface_query) +{ + + /* If a GET request was successfully processed, then check if the resource is + * OBSERVABLE and check its observe option. + */ + int32_t observe = OC_COAP_OPTION_OBSERVE_NOT_SET; + if ((resource->properties & OC_OBSERVABLE) == 0 || + !coap_options_get_observe(request, &observe)) { + return OC_COAP_OPTION_OBSERVE_NOT_SET; + } + + /* If the observe option is set to 0, make an attempt to add the requesting + * client as an observer. + */ + if (observe == OC_COAP_OPTION_OBSERVE_REGISTER) { + if (!ri_add_observation(request, response, resource, resource_is_collection, + block2_size, endpoint, iface_query)) { + coap_remove_observer_by_token(endpoint, request->token, + request->token_len); + return OC_COAP_OPTION_OBSERVE_NOT_SET; + } + coap_options_set_observe(response, OC_COAP_OPTION_OBSERVE_REGISTER); + return OC_COAP_OPTION_OBSERVE_REGISTER; + } + + /* If the observe option is set to 1, make an attempt to remove the + * requesting client from the list of observers. In addition, remove the + * resource from the list periodic GET callbacks if it is periodic observable. + */ + if (observe == OC_COAP_OPTION_OBSERVE_UNREGISTER) { + ri_remove_observation(request, response, resource, resource_is_collection, + block2_size, endpoint, iface_query); + return OC_COAP_OPTION_OBSERVE_UNREGISTER; + } + + // if the observe option is >= 2 then we a have a notification + return observe; +} + +#ifdef OC_HAS_FEATURE_ETAG + +uint64_t +oc_ri_get_etag(const oc_resource_t *resource) +{ + return resource->etag; +} + +uint64_t +oc_ri_get_batch_etag(const oc_resource_t *resource, + const oc_endpoint_t *endpoint, size_t device) +{ +#ifdef OC_RES_BATCH_SUPPORT + if (oc_core_get_resource_by_index(OCF_RES, device) == resource) { + return oc_discovery_get_batch_etag(endpoint, device); + } +#endif /* OC_RES_BATCH_SUPPORT */ +#ifdef OC_COLLECTIONS + if (oc_check_if_collection(resource)) { + return oc_collection_get_batch_etag((const oc_collection_t *)resource); + } +#endif /* OC_COLLECTIONS */ + (void)resource; + (void)endpoint; + (void)device; + OC_DBG("custom batch etag"); + return OC_ETAG_UNINITIALIZED; +} + +#endif /* OC_HAS_FEATURE_ETAG */ + #endif /* OC_SERVER */ diff --git a/api/oc_ri_server_internal.h b/api/oc_ri_server_internal.h index 6db24da9f..ad170c469 100644 --- a/api/oc_ri_server_internal.h +++ b/api/oc_ri_server_internal.h @@ -31,6 +31,19 @@ extern "C" { #ifdef OC_SERVER +/** + * @brief Check if given URI is in use by given device + * + * @param device index of device + * @param uri URI to check (cannot be NULL) + * @param uri_len length of URI + * + * @return true if URI is in use + * @return false otherwise + */ +bool oc_ri_URI_is_in_use(size_t device, const char *uri, size_t uri_len) + OC_NONNULL(); + /** * @brief allocate a resource structure * @@ -74,6 +87,26 @@ void oc_ri_on_delete_resource_remove_all(void); */ void oc_ri_on_delete_resource_invoke(oc_resource_t *resource) OC_NONNULL(); +int oc_ri_handle_observation(const coap_packet_t *request, + coap_packet_t *response, oc_resource_t *resource, + bool resource_is_collection, uint16_t block2_size, + const oc_endpoint_t *endpoint, + oc_interface_mask_t iface_query) OC_NONNULL(); + +/** Attempt to notify all observers of the resource */ +void oc_ri_notify_resource_observers(oc_resource_t *resource, + oc_interface_mask_t iface_mask) + OC_NONNULL(); + +/** Initialize resource interface server variables */ +void oc_ri_server_init(void); + +/** Reset observations */ +void oc_ri_server_reset(void); + +/** Deinitialize resource interface server variables */ +void oc_ri_server_shutdown(void); + #endif /* OC_SERVER */ #ifdef __cplusplus diff --git a/include/oc_ri.h b/include/oc_ri.h index 7a6d44373..7f80c9251 100644 --- a/include/oc_ri.h +++ b/include/oc_ri.h @@ -37,10 +37,7 @@ extern "C" { #endif -/** - * @brief CoAP methods - * - */ +/** @brief CoAP methods */ typedef enum { OC_GET = 1, ///< GET OC_POST, ///< POST @@ -49,10 +46,7 @@ typedef enum { OC_FETCH ///< FETCH } oc_method_t; -/** - * @brief resource properties (bit mask) - * - */ +/** @brief resource properties (bit mask) */ typedef enum { OC_DISCOVERABLE = (1 << 0), ///< discoverable OC_OBSERVABLE = (1 << 1), ///< observable @@ -70,7 +64,8 @@ typedef enum { /** * @brief response status - * can be translated to HTTP or CoAP. + * + * @note can be translated to HTTP or CoAP. */ typedef enum { OC_STATUS_OK = 0, ///< OK @@ -162,10 +157,7 @@ typedef enum { APPLICATION_NOT_DEFINED = 0xFFFF, ///< not defined } oc_content_format_t; -/** - * @brief interface masks - * - */ +/** @brief interface masks */ typedef enum { OC_IF_BASELINE = 1 << 1, ///< oic.if.baseline OC_IF_LL = 1 << 2, ///< oic.if.ll @@ -232,19 +224,13 @@ typedef enum { typedef struct oc_resource_s oc_resource_t; -/** - * @brief seperate response type - */ +/** @brief seperate response type */ typedef struct oc_separate_response_s oc_separate_response_t; -/** - * @brief response type - */ +/** @brief response type */ typedef struct oc_response_s oc_response_t; -/** - * @brief request information structure - */ +/** @brief request information structure */ typedef struct oc_request_t { const oc_endpoint_t *origin; ///< origin of the request @@ -267,32 +253,22 @@ typedef struct oc_request_t #endif /* OC_HAS_FEATURE_ETAG */ } oc_request_t; -/** - * @brief request callback - * - */ +/** @brief request callback */ typedef void (*oc_request_callback_t)(oc_request_t *, oc_interface_mask_t, void *); -/** - * @brief request handler type - * - */ +/** @brief request handler type */ typedef struct oc_request_handler_s { oc_request_callback_t cb; void *user_data; } oc_request_handler_t; -/** - * @brief set properties callback - */ +/** @brief set properties callback */ typedef bool (*oc_set_properties_cb_t)(const oc_resource_t *, const oc_rep_t *, void *); -/** - * @brief get properties callback - */ +/** @brief get properties callback */ typedef void (*oc_get_properties_cb_t)(const oc_resource_t *, oc_interface_mask_t, void *); @@ -305,10 +281,7 @@ typedef void (*oc_payload_callback_t)(void); #endif /* OC_HAS_FEATURE_PUSH */ -/** - * @brief properties callback structure - * - */ +/** @brief properties callback structure */ typedef struct oc_properties_cb_t { union { @@ -318,10 +291,7 @@ typedef struct oc_properties_cb_t void *user_data; } oc_properties_cb_t; -/** - * @brief resource structure - * - */ +/** @brief resource structure */ struct oc_resource_s { struct oc_resource_s *next; ///< next resource @@ -534,6 +504,7 @@ oc_interface_mask_t oc_ri_get_interface_mask(const char *iface, * @param device the device index * @return oc_resource_t* the resource structure */ +OC_API oc_resource_t *oc_ri_get_app_resource_by_uri(const char *uri, size_t uri_len, size_t device); @@ -542,6 +513,7 @@ oc_resource_t *oc_ri_get_app_resource_by_uri(const char *uri, size_t uri_len, * * @return oc_resource_t* the resource list */ +OC_API oc_resource_t *oc_ri_get_app_resources(void); /** @@ -635,6 +607,7 @@ bool oc_ri_on_delete_resource_remove_callback(oc_ri_delete_resource_cb_t cb) * @return int the position of the next key value pair in the query * @return int -1 on failure */ +OC_API int oc_ri_get_query_nth_key_value(const char *query, size_t query_len, const char **key, size_t *key_len, const char **value, size_t *value_len, @@ -651,6 +624,7 @@ int oc_ri_get_query_nth_key_value(const char *query, size_t query_len, * @return -1 if the key is not found * @return the length of the value */ +OC_API int oc_ri_get_query_value_v1(const char *query, size_t query_len, const char *key, size_t key_len, const char **value) OC_NONNULL(3); @@ -660,6 +634,7 @@ int oc_ri_get_query_value_v1(const char *query, size_t query_len, * * @deprecated replaced by oc_ri_get_query_value_v1 in v2.2.5.9 */ +OC_API int oc_ri_get_query_value(const char *query, size_t query_len, const char *key, const char **value) OC_NONNULL(3) OC_DEPRECATED("replaced by oc_ri_get_query_value_v1 in v2.2.5.9"); @@ -674,6 +649,7 @@ int oc_ri_get_query_value(const char *query, size_t query_len, const char *key, * @param key_len the key length * @return true if key exists */ +OC_API bool oc_ri_query_exists_v1(const char *query, size_t query_len, const char *key, size_t key_len) OC_NONNULL(3); @@ -684,6 +660,7 @@ bool oc_ri_query_exists_v1(const char *query, size_t query_len, const char *key, * * @deprecated replaced by oc_ri_query_exists_v1 in v2.2.5.9 */ +OC_API int oc_ri_query_exists(const char *query, size_t query_len, const char *key) OC_NONNULL(3) OC_DEPRECATED("replaced by oc_ri_query_exists_v1 in v2.2.5.9"); @@ -700,6 +677,7 @@ int oc_ri_query_exists(const char *query, size_t query_len, const char *key) * @return >= 0 if key exists and the value is the position of the next key in * the query or query_len if it is the last key */ +OC_API int oc_ri_query_nth_key_exists(const char *query, size_t query_len, const char **key, size_t *key_len, size_t n) OC_NONNULL(3, 4);