Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: simple-mbed-cloud-client/mbed-cloud-client/mbed-client/source/m2mnsdlinterface.cpp
- Revision:
- 0:8f0bb79ddd48
diff -r 000000000000 -r 8f0bb79ddd48 simple-mbed-cloud-client/mbed-cloud-client/mbed-client/source/m2mnsdlinterface.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simple-mbed-cloud-client/mbed-cloud-client/mbed-client/source/m2mnsdlinterface.cpp Tue May 04 08:55:12 2021 +0000 @@ -0,0 +1,3723 @@ +/* + * Copyright (c) 2015-2019 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Needed for PRIu64 on FreeRTOS +#include <stdio.h> +// Note: this macro is needed on armcc to get the the limit macros like UINT16_MAX +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + +// Note: this macro is needed on armcc to get the the PRI*32 macros +// from inttypes.h in a C++ code. +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include <assert.h> +#include <inttypes.h> +#include <stdlib.h> +#include "include/nsdlaccesshelper.h" +#include "include/m2mnsdlobserver.h" +#include "include/m2mtlvdeserializer.h" +#include "include/m2mtlvserializer.h" +#include "include/m2mnsdlinterface.h" +#include "include/m2mreporthandler.h" +#include "mbed-client/m2mstring.h" +#include "mbed-client/m2msecurity.h" +#include "mbed-client/m2mserver.h" +#include "mbed-client/m2mobject.h" +#include "mbed-client/m2mendpoint.h" +#include "mbed-client/m2mobjectinstance.h" +#include "mbed-client/m2mresource.h" +#include "mbed-client/m2mblockmessage.h" +#include "mbed-client/m2mconstants.h" +#include "mbed-client/uriqueryparser.h" +#include "mbed-trace/mbed_trace.h" +#include "sn_grs.h" +#include "mbed-client/m2minterfacefactory.h" +#include "mbed-client/m2mdevice.h" +#include "randLIB.h" +#include "common_functions.h" +#include "sn_nsdl_lib.h" +#include "sn_coap_protocol.h" +#include "m2mnotificationhandler.h" +#include "eventOS_event_timer.h" +#include "eventOS_scheduler.h" +#include "ns_hal_init.h" + +#define MBED_CLIENT_NSDLINTERFACE_TASKLET_INIT_EVENT 0 // Tasklet init occurs always when generating a tasklet +#define MBED_CLIENT_NSDLINTERFACE_EVENT 30 +#define MBED_CLIENT_NSDLINTERFACE_BS_EVENT 31 +#define MBED_CLIENT_NSDLINTERFACE_BS_PUT_EVENT 32 +#define MBED_CLIENT_NSDLINTERFACE_BS_FINISH_EVENT 33 + +#ifdef MBED_CONF_MBED_CLIENT_EVENT_LOOP_SIZE +#define MBED_CLIENT_EVENT_LOOP_SIZE MBED_CONF_MBED_CLIENT_EVENT_LOOP_SIZE +#else +#define MBED_CLIENT_EVENT_LOOP_SIZE 1024 +#endif + +#define BUFFER_SIZE 21 +#define TRACE_GROUP "mClt" +#define MAX_QUERY_COUNT 10 + +const char *MCC_VERSION = "mccv=2.2.1"; + +int8_t M2MNsdlInterface::_tasklet_id = -1; + +extern "C" void nsdlinterface_tasklet_func(arm_event_s *event) +{ + // skip the init event as there will be a timer event after + if (event->event_type == MBED_CLIENT_NSDLINTERFACE_EVENT) { + eventOS_scheduler_mutex_wait(); + M2MNsdlInterface::nsdl_coap_data_s *coap_data = (M2MNsdlInterface::nsdl_coap_data_s*)event->data_ptr; + M2MNsdlInterface *interface = (M2MNsdlInterface*)sn_nsdl_get_context(coap_data->nsdl_handle); + if (interface) { + interface->resource_callback_handle_event(coap_data->received_coap_header, &coap_data->address); + if (coap_data->received_coap_header->coap_status == COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVED && + coap_data->received_coap_header->payload_ptr) { + coap_data->nsdl_handle->grs->sn_grs_free(coap_data->received_coap_header->payload_ptr); + coap_data->received_coap_header->payload_ptr = 0; + } + } + + M2MNsdlInterface::memory_free(coap_data->received_coap_header->payload_ptr); + sn_coap_parser_release_allocated_coap_msg_mem(coap_data->nsdl_handle->grs->coap, coap_data->received_coap_header); + M2MNsdlInterface::memory_free(coap_data->address.addr_ptr); + M2MNsdlInterface::memory_free(coap_data); + eventOS_scheduler_mutex_release(); + + } else if (event->event_type == MBED_CLIENT_NSDLINTERFACE_BS_EVENT) { + M2MNsdlInterface::nsdl_coap_data_s *coap_data = (M2MNsdlInterface::nsdl_coap_data_s*)event->data_ptr; + M2MNsdlInterface *interface = (M2MNsdlInterface*)sn_nsdl_get_context(coap_data->nsdl_handle); + + sn_coap_hdr_s *coap_response = sn_nsdl_build_response(coap_data->nsdl_handle, + coap_data->received_coap_header, + coap_data->received_coap_header->msg_code); + if (coap_response) { + coap_response->msg_type = coap_data->received_coap_header->msg_type; + + if (sn_nsdl_send_coap_message(coap_data->nsdl_handle, &coap_data->address, coap_response) == 0) { + interface->store_bs_finished_response_id(coap_response->msg_id); + } else { + tr_error("Failed to send final response for BS finished"); + } + + sn_coap_parser_release_allocated_coap_msg_mem(coap_data->nsdl_handle->grs->coap, coap_response); + + } else { + tr_error("Failed to create final response message for BS finished"); + } + + // Release the memory + M2MNsdlInterface::memory_free(coap_data->received_coap_header->payload_ptr); + sn_coap_parser_release_allocated_coap_msg_mem(coap_data->nsdl_handle->grs->coap, coap_data->received_coap_header); + M2MNsdlInterface::memory_free(coap_data->address.addr_ptr); + M2MNsdlInterface::memory_free(coap_data); + } else if (event->event_type == MBED_CLIENT_NSDLINTERFACE_BS_PUT_EVENT) { + M2MNsdlInterface::nsdl_coap_data_s *coap_data = (M2MNsdlInterface::nsdl_coap_data_s*)event->data_ptr; + M2MNsdlInterface *interface = (M2MNsdlInterface*)sn_nsdl_get_context(coap_data->nsdl_handle); + interface->handle_bootstrap_put_message(coap_data->received_coap_header, &coap_data->address); + + M2MNsdlInterface::memory_free(coap_data->received_coap_header->payload_ptr); + sn_coap_parser_release_allocated_coap_msg_mem(coap_data->nsdl_handle->grs->coap, coap_data->received_coap_header); + M2MNsdlInterface::memory_free(coap_data->address.addr_ptr); + M2MNsdlInterface::memory_free(coap_data); + } else if (event->event_type == MBED_CLIENT_NSDLINTERFACE_BS_FINISH_EVENT) { + nsdl_s *nsdl_handle = (nsdl_s*)event->data_ptr; + M2MNsdlInterface *interface = (M2MNsdlInterface*)sn_nsdl_get_context(nsdl_handle); + interface->handle_bootstrap_finish_ack(event->event_data); + } +} + +M2MNsdlInterface::M2MNsdlInterface(M2MNsdlObserver &observer, M2MConnectionHandler &connection_handler) +: _observer(observer), + _endpoint(NULL), + _nsdl_handle(NULL), + _security(NULL), + _server(NULL), + _nsdl_execution_timer(*this), + _registration_timer(*this), + _connection_handler(connection_handler), + _counter_for_nsdl(0), + _next_coap_ping_send_time(0), + _server_address(NULL), + _custom_uri_query_params(NULL), + _notification_handler(new M2MNotificationHandler()), + _bootstrap_id(0), + _binding_mode(M2MInterface::NOT_SET), + _identity_accepted(false), + _nsdl_execution_timer_running(false), + _notification_send_ongoing(false), + _registered(false), + _bootstrap_finish_ack_received(false), + _download_retry_timer(*this), + _download_retry_time(0) +{ + tr_debug("M2MNsdlInterface::M2MNsdlInterface()"); + + _event.data.data_ptr = NULL; + _event.data.event_data = 0; + _event.data.event_id = 0; + _event.data.sender = 0; + _event.data.event_type = 0; + _event.data.priority = ARM_LIB_MED_PRIORITY_EVENT; + + _server = new M2MServer(); + + // This initializes libCoap and libNsdl + // Parameters are function pointers to used memory allocation + // and free functions in structure and used functions for sending + // and receiving purposes. + _nsdl_handle = sn_nsdl_init(&(__nsdl_c_send_to_server), &(__nsdl_c_received_from_server), + &(__nsdl_c_memory_alloc), &(__nsdl_c_memory_free), &(__nsdl_c_auto_obs_token)); + + sn_nsdl_set_context(_nsdl_handle, this); + + ns_hal_init(NULL, MBED_CLIENT_EVENT_LOOP_SIZE, NULL, NULL); + eventOS_scheduler_mutex_wait(); + if (M2MNsdlInterface::_tasklet_id < 0) { + M2MNsdlInterface::_tasklet_id = eventOS_event_handler_create(nsdlinterface_tasklet_func, MBED_CLIENT_NSDLINTERFACE_TASKLET_INIT_EVENT); + assert(M2MNsdlInterface::_tasklet_id >= 0); + } + eventOS_scheduler_mutex_release(); + + _event.data.receiver = M2MNsdlInterface::_tasklet_id; + + // Randomize the initial auto obs token. Range is in 1 - 1023 + _auto_obs_token = randLIB_get_random_in_range(AUTO_OBS_TOKEN_MIN, AUTO_OBS_TOKEN_MAX); + + initialize(); +} + +M2MNsdlInterface::~M2MNsdlInterface() +{ + tr_debug("M2MNsdlInterface::~M2MNsdlInterface() - IN"); + if (_endpoint) { + memory_free(_endpoint->endpoint_name_ptr); + memory_free(_endpoint->domain_name_ptr); + memory_free(_endpoint->type_ptr); + memory_free(_endpoint->lifetime_ptr); + memory_free(_endpoint); + } + + delete _notification_handler; + _base_list.clear(); + _security = NULL; + delete _server; + sn_nsdl_destroy(_nsdl_handle); + _nsdl_handle = NULL; + memory_free(_server_address); + free_request_context_list(NULL, false); + free_response_list(); + memory_free(_custom_uri_query_params); + tr_debug("M2MNsdlInterface::~M2MNsdlInterface() - OUT"); +} + +bool M2MNsdlInterface::initialize() +{ + tr_debug("M2MNsdlInterface::initialize()"); + bool success = false; + + // Sets the packet retransmission attempts and time interval + sn_nsdl_set_retransmission_parameters(_nsdl_handle, + MBED_CLIENT_RECONNECTION_COUNT, + MBED_CLIENT_RECONNECTION_INTERVAL); + + sn_nsdl_set_retransmission_buffer(_nsdl_handle, MBED_CLIENT_SN_COAP_RESENDING_QUEUE_SIZE_MSGS, 0); + + sn_nsdl_handle_block2_response_internally(_nsdl_handle, false); + + // Allocate the memory for endpoint + _endpoint = (sn_nsdl_ep_parameters_s*)memory_alloc(sizeof(sn_nsdl_ep_parameters_s)); + if (_endpoint) { + memset(_endpoint, 0, sizeof(sn_nsdl_ep_parameters_s)); + success = true; + } + + M2MResource* update_trigger = _server->get_resource(M2MServer::RegistrationUpdate); + if (update_trigger) { + update_trigger->set_execute_function(execute_callback(this, + &M2MNsdlInterface::update_trigger_callback)); + } + + add_object_to_list(_server); + create_nsdl_object_structure(_server); + ns_list_init(&_request_context_list); + ns_list_init(&_response_list); + + return success; +} + +void M2MNsdlInterface::create_endpoint(const String &name, + const String &type, + const int32_t life_time, + const String &domain, + const uint8_t mode, + const String &/*context_address*/) +{ + tr_info("M2MNsdlInterface::create_endpoint( name %s type %s lifetime %" PRId32 ", domain %s, mode %d)", + name.c_str(), type.c_str(), life_time, domain.c_str(), mode); + _endpoint_name = name; + _binding_mode = mode; + + if (_endpoint){ + memset(_endpoint, 0, sizeof(sn_nsdl_ep_parameters_s)); + if (!_endpoint_name.empty()) { + memory_free(_endpoint->endpoint_name_ptr); + _endpoint->endpoint_name_ptr = alloc_string_copy((uint8_t*)_endpoint_name.c_str(), _endpoint_name.length()); + _endpoint->endpoint_name_len = _endpoint_name.length(); + } + if (!type.empty()) { + _endpoint->type_ptr = alloc_string_copy((uint8_t*)type.c_str(), type.length()); + _endpoint->type_len = type.length(); + } + if (!domain.empty()) { + _endpoint->domain_name_ptr = alloc_string_copy((uint8_t*)domain.c_str(), domain.length()); + _endpoint->domain_name_len = domain.length(); + } + + // nsdl binding mode is only 3 least significant bits + _endpoint->binding_and_mode = (sn_nsdl_oma_binding_and_mode_t)((uint8_t)mode & 0x07); + + // If lifetime is less than zero then leave the field empty + if (life_time > 0) { + set_endpoint_lifetime_buffer(life_time); + } + } +} + +void M2MNsdlInterface::update_endpoint(const String &name) +{ + _endpoint_name = name; + if (_endpoint){ + if (!_endpoint_name.empty()) { + memory_free(_endpoint->endpoint_name_ptr); + _endpoint->endpoint_name_ptr = alloc_string_copy((uint8_t*)_endpoint_name.c_str(), _endpoint_name.length()); + _endpoint->endpoint_name_len = _endpoint_name.length(); + } + } +} + +void M2MNsdlInterface::update_domain(const String &domain) +{ + if (_endpoint){ + memory_free(_endpoint->domain_name_ptr); + if (!domain.empty()) { + _endpoint->domain_name_ptr = alloc_string_copy((uint8_t*)domain.c_str(), domain.length()); + _endpoint->domain_name_len = domain.length(); + } else { + _endpoint->domain_name_ptr = NULL; + _endpoint->domain_name_len = 0; + } + } +} + +void M2MNsdlInterface::set_endpoint_lifetime_buffer(int lifetime) +{ + tr_info("M2MNsdlInterface::set_endpoint_lifetime_buffer - %d", lifetime); + if (lifetime < MINIMUM_REGISTRATION_TIME) { + return; + } + + _server->set_resource_value(M2MServer::Lifetime, lifetime); + + if (_endpoint && _endpoint->lifetime_ptr) { + memory_free(_endpoint->lifetime_ptr); + _endpoint->lifetime_ptr = NULL; + _endpoint->lifetime_len = 0; + } + + char buffer[20+1]; + uint32_t size = m2m::itoa_c(lifetime, buffer); + if (_endpoint && size <= sizeof(buffer)) { + _endpoint->lifetime_len = 0; + _endpoint->lifetime_ptr = alloc_string_copy((uint8_t*)buffer, size); + if (_endpoint->lifetime_ptr) { + _endpoint->lifetime_len = size; + } + } + + set_retransmission_parameters(); +} + + +void M2MNsdlInterface::delete_endpoint() +{ + tr_debug("M2MNsdlInterface::delete_endpoint()"); + if (_endpoint) { + free(_endpoint->lifetime_ptr); + memory_free(_endpoint); + _endpoint = NULL; + } +} + +bool M2MNsdlInterface::create_nsdl_list_structure(const M2MBaseList &list) +{ + tr_debug("M2MNsdlInterface::create_nsdl_list_structure()"); + bool success = false; + if (!list.empty()) { + tr_debug("M2MNsdlInterface::create_nsdl_list_structure - Object count is %d", list.size()); + M2MBaseList::const_iterator it; + it = list.begin(); + for ( ; it != list.end(); it++ ) { + // Create NSDL structure for all Objects inside + success = create_nsdl_structure(*it); + if (!success) { + tr_debug("M2MNsdlInterface::create_nsdl_list_structure - fail to create resource"); + break; + } + + tr_debug("M2MNsdlInterface::create_nsdl_list_structure - create %d", success); + add_object_to_list(*it); + } + } + + return success; +} + +bool M2MNsdlInterface::remove_nsdl_resource(M2MBase *base) +{ + sn_nsdl_dynamic_resource_parameters_s* resource = base->get_nsdl_resource(); + return sn_nsdl_pop_resource(_nsdl_handle, resource); +} + +bool M2MNsdlInterface::create_bootstrap_resource(sn_nsdl_addr_s *address) +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_debug("M2MNsdlInterface::create_bootstrap_resource()"); + _identity_accepted = false; + _bootstrap_finish_ack_received = false; + bool success = false; + tr_debug("M2MNsdlInterface::create_bootstrap_resource() - endpoint name: %.*s", _endpoint->endpoint_name_len, + _endpoint->endpoint_name_ptr); + + if (_bootstrap_id == 0) { + // Take copy of the address, uri_query_parameters() will modify the source buffer + bool msg_sent = false; + if (_server_address) { + char *address_copy = M2MBase::alloc_string_copy(_server_address); + if (address_copy) { + char* query = parse_uri_query_parameters(_server_address); + if (query != NULL) { + size_t query_len = 1 + strlen(query) + 1 + strlen(MCC_VERSION) + 1; + if (query_len <= MAX_URI_QUERY_LEN) { + char query_params[MAX_URI_QUERY_LEN]; + strcpy(query_params, "&"); + strcat(query_params, query); + strcat(query_params, "&"); + strcat(query_params, MCC_VERSION); + msg_sent = true; + sn_nsdl_clear_coap_resending_queue(_nsdl_handle); + _bootstrap_id = sn_nsdl_oma_bootstrap(_nsdl_handle, + address, + _endpoint, + query_params); + free(_server_address); + _server_address = M2MBase::alloc_string_copy(address_copy); + } else { + tr_error("M2MNsdlInterface::create_bootstrap_resource() - max uri param length reached (%lu)", + (unsigned long)query_len); + } + } + free(address_copy); + } + } + if (!msg_sent) { + sn_nsdl_clear_coap_resending_queue(_nsdl_handle); + _bootstrap_id = sn_nsdl_oma_bootstrap(_nsdl_handle, + address, + _endpoint, + NULL); + } + success = _bootstrap_id != 0; + tr_debug("M2MNsdlInterface::create_bootstrap_resource - _bootstrap_id %d", _bootstrap_id); + } + return success; +#else + (void)address; + return false; +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +void M2MNsdlInterface::set_server_address(uint8_t* address, + uint8_t address_length, + const uint16_t port, + sn_nsdl_addr_type_e address_type) +{ + tr_debug("M2MNsdlInterface::set_server_address()"); + set_NSP_address(_nsdl_handle, address, address_length, port, address_type); +} + +bool M2MNsdlInterface::send_register_message() +{ + tr_info("M2MNsdlInterface::send_register_message()"); + bool success = false; + if (_server_address) { + success = parse_and_send_uri_query_parameters(); + } + // If URI parsing fails or there is no parameters, try again without parameters + if (!success) { + // Clear the observation tokens + send_next_notification(true); + + success = sn_nsdl_register_endpoint(_nsdl_handle,_endpoint, NULL) != 0; + } + return success; +} + +void M2MNsdlInterface::send_request(DownloadType type, + const char *uri, + const sn_coap_msg_code_e msg_code, + const size_t offset, + const bool async, + uint32_t token, + const uint16_t payload_len, + uint8_t *payload_ptr, + request_data_cb data_cb, + request_error_cb error_cb, + void *context) +{ + assert(uri != NULL); + int32_t message_id = 0; + request_context_s *data_request = NULL; + + if (msg_code == COAP_MSG_CODE_REQUEST_GET && !_registered) { + tr_error("M2MNsdlInterface::send_request - client not registered!"); + error_cb(ERROR_NOT_REGISTERED, context); + return; + } + + // Check the duplicate items + request_context_s *data = (request_context_s *)ns_list_get_first(&_request_context_list); + while (data) { + if ((strcmp(uri, data->uri_path) == 0) && (offset == data->received_size)) { + tr_debug("M2MNsdlInterface::send_request - item already exists"); + // Remove queued message from the resend queue before resuming file download. + // Otherwise there will be duplicate block transfer with a just different message id's. + sn_nsdl_remove_msg_from_retransmission(_nsdl_handle, + (uint8_t*)&data->msg_token, + sizeof(data->msg_token)); + data_request = data; + break; + } + data = (request_context_s *)ns_list_get_next(&_request_context_list, data); + } + + if (data_request == NULL) { + data_request = (struct request_context_s*)memory_alloc(sizeof(struct request_context_s)); + if (data_request == NULL) { + error_cb(FAILED_TO_ALLOCATE_MEMORY, context); + return; + } + + data_request->resend = false; + data_request->context = context; + data_request->async_req = async; + data_request->received_size = offset; + data_request->download_type = type; + data_request->uri_path = (char*)alloc_string_copy((uint8_t*)uri, strlen(uri)); + if (data_request->uri_path == NULL) { + memory_free(data_request); + error_cb(FAILED_TO_ALLOCATE_MEMORY, context); + return; + } + + data_request->on_request_data_cb = data_cb; + data_request->on_request_error_cb = error_cb; + + if (!token) { + randLIB_get_n_bytes_random(&token, sizeof(token)); + + if (!token) { + token++; + } + } + + data_request->msg_token = token; + data_request->msg_code = msg_code; + + ns_list_add_to_end(&_request_context_list, data_request); + + } + + message_id = sn_nsdl_send_request(_nsdl_handle, + data_request->msg_code, + data_request->uri_path, + data_request->msg_token, + data_request->received_size, + payload_len, + payload_ptr, + data_request->download_type); + + if (message_id == -4) { + data_request->resend = true; + } else if (message_id <= 0) { + ns_list_remove(&_request_context_list, data_request); + memory_free(data_request->uri_path); + memory_free(data_request); + error_cb(FAILED_TO_ALLOCATE_MEMORY, context); + } +} + +bool M2MNsdlInterface::send_update_registration(const uint32_t lifetime) +{ + tr_info("M2MNsdlInterface::send_update_registration( lifetime %" PRIu32 ")", lifetime); + bool success = false; + int32_t ret = 0; + + _registration_timer.stop_timer(); + bool lifetime_changed = true; + + // If new resources have been created after registration those must be created and published to the server. + create_nsdl_list_structure(_base_list); + + // Check if resource(1/0/1) value has been updated and update it into _endpoint struct + if (lifetime == 0) { + lifetime_changed = lifetime_value_changed(); + if (lifetime_changed) { + set_endpoint_lifetime_buffer(_server->resource_value_int(M2MServer::Lifetime));; + } + } else { + set_endpoint_lifetime_buffer(lifetime); + } + + if (_nsdl_handle) { + if (!lifetime_changed) { + tr_debug("M2MNsdlInterface::send_update_registration - regular update"); + ret = sn_nsdl_update_registration(_nsdl_handle, NULL, 0); + } else { + if (_endpoint && _endpoint->lifetime_ptr) { + tr_debug("M2MNsdlInterface::send_update_registration - new lifetime value"); + ret = sn_nsdl_update_registration(_nsdl_handle, + _endpoint->lifetime_ptr, + _endpoint->lifetime_len); + } + } + } + + if (ret >= 0) { + success = true; + } + + _registration_timer.start_timer(registration_time() * 1000, + M2MTimerObserver::Registration, + false); + + return success; +} + +bool M2MNsdlInterface::send_unregister_message() +{ + tr_info("M2MNsdlInterface::send_unregister_message"); + if (is_unregister_ongoing()) { + tr_debug("M2MNsdlInterface::send_unregister_message - unregistration already in progress"); + return true; + } + + bool success = false; + int32_t ret = 0; + + ret = sn_nsdl_unregister_endpoint(_nsdl_handle); + if (ret == -4) { + tr_warn("Failed to send registration update. Clearing queue and retrying."); + sn_nsdl_clear_coap_resending_queue(_nsdl_handle); + ret = sn_nsdl_unregister_endpoint(_nsdl_handle); + } + if (ret >= 0) { + success = true; + } + return success; +} + +// XXX: move these to common place, no need to copy these wrappers to multiple places: +void *M2MNsdlInterface::memory_alloc(uint32_t size) +{ + if(size) + return malloc(size); + else + return 0; +} + +void M2MNsdlInterface::memory_free(void *ptr) +{ + free(ptr); +} + +uint8_t* M2MNsdlInterface::alloc_string_copy(const uint8_t* source, uint16_t size) +{ + assert(source != NULL); + + uint8_t* result = (uint8_t*)memory_alloc(size + 1); + if (result) { + memcpy(result, source, size); + result[size] = '\0'; + } + return result; +} + +uint8_t M2MNsdlInterface::send_to_server_callback(struct nsdl_s * /*nsdl_handle*/, + sn_nsdl_capab_e /*protocol*/, + uint8_t *data_ptr, + uint16_t data_len, + sn_nsdl_addr_s *address) +{ + tr_debug("M2MNsdlInterface::send_to_server_callback(data size %d)", data_len); + _observer.coap_message_ready(data_ptr,data_len,address); + return 1; +} + +uint8_t M2MNsdlInterface::received_from_server_callback(struct nsdl_s *nsdl_handle, + sn_coap_hdr_s *coap_header, + sn_nsdl_addr_s *address) +{ + tr_debug("M2MNsdlInterface::received_from_server_callback"); + _observer.coap_data_processed(); + uint8_t value = 0; + request_context_s request_context; + if(nsdl_handle && coap_header) { + bool is_bootstrap_msg = nsdl_handle->is_bs_server; + + if (coap_header->token_ptr && + coap_header->token_len == sizeof(nsdl_handle->register_token) && + memcmp(coap_header->token_ptr, &nsdl_handle->register_token, sizeof(nsdl_handle->register_token)) == 0) { + + handle_register_response(coap_header); + + } else if (coap_header->token_ptr && + coap_header->token_len == sizeof(nsdl_handle->unregister_token) && + memcmp(coap_header->token_ptr, + &nsdl_handle->unregister_token, + sizeof(nsdl_handle->unregister_token)) == 0) { + + handle_unregister_response(coap_header); + + } else if (coap_header->token_ptr && + coap_header->token_len == sizeof(nsdl_handle->update_register_token) && + memcmp(coap_header->token_ptr, + &nsdl_handle->update_register_token, + sizeof(nsdl_handle->update_register_token)) == 0) { + + handle_register_update_response(coap_header); + + } else if (coap_header->token_ptr && is_response_to_request(coap_header, request_context)) { + + handle_request_response(coap_header, &request_context); + + } +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + else if (coap_header->token_ptr && + coap_header->token_len == sizeof(nsdl_handle->bootstrap_token) && + memcmp(coap_header->token_ptr, &nsdl_handle->bootstrap_token, sizeof(nsdl_handle->bootstrap_token)) == 0) { + + handle_bootstrap_response(coap_header); + + } +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + else { + + sn_coap_hdr_s *coap_response = NULL; + bool execute_value_updated = false; + M2MObjectInstance *obj_instance = NULL; + + if (COAP_MSG_CODE_REQUEST_PUT == coap_header->msg_code) { + if (is_bootstrap_msg) { + send_empty_ack(coap_header, address); + nsdl_coap_data_s *nsdl_coap_data = create_coap_event_data(coap_header, + address, + nsdl_handle, + coap_header->msg_code); + if (nsdl_coap_data) { + _event.data.event_type = MBED_CLIENT_NSDLINTERFACE_BS_PUT_EVENT; + _event.data.data_ptr = (void*)nsdl_coap_data; + eventOS_event_send_user_allocated(&_event); + return 2; // freeing will be performed in MBED_CLIENT_NSDLINTERFACE_BS_PUT_EVENT event + } else { + tr_error("M2MNsdlInterface::received_from_server_callback() - BS PUT failed to allocate nsdl_coap_data_s!"); + coap_response = sn_nsdl_build_response(_nsdl_handle, + coap_header, + COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_TOO_LARGE); + } + + } else { + tr_debug("M2MNsdlInterface::received_from_server_callback - Method not allowed (PUT)."); + coap_response = sn_nsdl_build_response(_nsdl_handle, + coap_header, + COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED); + } + } + else if (COAP_MSG_CODE_REQUEST_DELETE == coap_header->msg_code) { + if (is_bootstrap_msg) { + handle_bootstrap_delete(coap_header, address); + } else { + tr_debug("M2MNsdlInterface::received_from_server_callback - Method not allowed (DELETE)."); + coap_response = sn_nsdl_build_response(_nsdl_handle, + coap_header, + COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED); + } + } else if (COAP_MSG_CODE_REQUEST_POST == coap_header->msg_code) { + + execute_value_updated = handle_post_response(coap_header, + address, + coap_response, + obj_instance, + is_bootstrap_msg); + + } else if (COAP_STATUS_BUILDER_BLOCK_SENDING_DONE == coap_header->coap_status && + (coap_header->msg_code == COAP_MSG_CODE_RESPONSE_CONTENT || + coap_header->msg_code == COAP_MSG_CODE_RESPONSE_CHANGED)) { + + coap_response_s *resp = find_response(coap_header->msg_id); + if (resp) { + M2MBase *base = find_resource(resp->uri_path); + if (base) { + if (resp->type == M2MBase::BLOCK_SUBSCRIBE) { + sn_coap_msg_code_e code; + // This case coap response is not needed. + // coap_header have the payload length and the observation number which is needed in following call. + base->handle_observation(nsdl_handle, *coap_header, *coap_header, this, code); + base->start_observation(*coap_header, this); + } else { + handle_message_delivered(base, resp->type); + } + + remove_item_from_response_list(NULL, coap_header->msg_id); + } + } + + // Retransmission done + } else if (COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED == coap_header->coap_status || + COAP_STATUS_BUILDER_BLOCK_SENDING_FAILED == coap_header->coap_status) { + + tr_info("M2MNsdlInterface::received_from_server_callback - message sending failed, id %d", coap_header->msg_id); + _observer.registration_error(M2MInterface::NetworkError, true); + + // Handle Server-side expections during registration flow + // Client might receive error from server due to temporary connection/operability reasons, + // server might not recover the flow in this case, so it is better for Client to restart registration. + } else if (COAP_MSG_CODE_EMPTY == coap_header->msg_code) { + + handle_empty_ack(coap_header, is_bootstrap_msg); + + } else if (nsdl_handle->register_token && + ((coap_header->msg_code == COAP_MSG_CODE_RESPONSE_INTERNAL_SERVER_ERROR) || + (coap_header->msg_code == COAP_MSG_CODE_RESPONSE_BAD_GATEWAY) || + (coap_header->msg_code == COAP_MSG_CODE_RESPONSE_SERVICE_UNAVAILABLE) || + (coap_header->msg_code == COAP_MSG_CODE_RESPONSE_GATEWAY_TIMEOUT))) { + + tr_error("M2MNsdlInterface::received_from_server_callback - registration error %d", coap_header->msg_code); + tr_error("M2MNsdlInterface::received_from_server_callback - unexpected error received from server"); + // Try to do clean register again + _observer.registration_error(M2MInterface::NetworkError, true); + + } else { + // Add warn for any message that gets this far. We might be missing some handling in above. + tr_warn("M2MNsdlInterface::received_from_server_callback - msg was ignored %d", coap_header->msg_code); + } + + // Send response to server + if (coap_response) { + tr_debug("M2MNsdlInterface::received_from_server_callback - send CoAP response"); + (sn_nsdl_send_coap_message(_nsdl_handle, address, coap_response) == 0) ? value = 0 : value = 1; + sn_nsdl_release_allocated_coap_msg_mem(_nsdl_handle, coap_response); + } + + // Tell to application that value has been updated + if (execute_value_updated) { + value_updated(obj_instance); + } + } + } + return value; +} +#ifdef ENABLE_ASYNC_REST_RESPONSE +M2MBase::Operation M2MNsdlInterface::operation_for_message_code(sn_coap_msg_code_e code) +{ + M2MBase::Operation ret_val; + switch (code) { + case COAP_MSG_CODE_REQUEST_POST: { + ret_val = M2MBase::POST_ALLOWED; + break; + } + case COAP_MSG_CODE_REQUEST_GET: { + ret_val = M2MBase::GET_ALLOWED; + break; + } + case COAP_MSG_CODE_REQUEST_PUT: { + ret_val = M2MBase::PUT_ALLOWED; + break; + } + default: + ret_val = M2MBase::NOT_ALLOWED; + break; + } + return ret_val; +} +#endif // ENABLE_ASYNC_REST_RESPONSE + +uint8_t M2MNsdlInterface::resource_callback(struct nsdl_s *nsdl_handle, + sn_coap_hdr_s *received_coap_header, + sn_nsdl_addr_s *address, + sn_nsdl_capab_e /*nsdl_capab*/) +{ + bool async_response = false; + tr_debug("M2MNsdlInterface::resource_callback()"); + + assert(received_coap_header); + _observer.coap_data_processed(); + + String resource_name = coap_to_string(received_coap_header->uri_path_ptr, + received_coap_header->uri_path_len); + + M2MBase *base = find_resource(resource_name); + +#ifdef ENABLE_ASYNC_REST_RESPONSE + if (base) { + if (base->is_async_coap_request_callback_set()) { + async_response = true; + if (!handle_delayed_response_store(resource_name.c_str(), + received_coap_header, + address, + M2MBase::DELAYED_RESPONSE)) { + return 0; + } + } + } +#endif // ENABLE_ASYNC_REST_RESPONSE + + // Use piggypacked response for any other types than POST + if (received_coap_header->msg_code != COAP_MSG_CODE_REQUEST_POST) { + // If there is a async callback set for this resource than skip this and + // send empty ACK below, application will be responsible to send final response. + if (!async_response) { + uint8_t status = resource_callback_handle_event(received_coap_header, address); + if (received_coap_header->coap_status == COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVED) { + memory_free(received_coap_header->payload_ptr); + } + sn_nsdl_release_allocated_coap_msg_mem(_nsdl_handle, received_coap_header); + return status; + } + } + + // Only handle this in case of delayed response for POST and not for + // implementation behind ENABLE_ASYNC_REST_RESPONSE + if (base && !async_response) { + M2MResource *res = NULL; + if (M2MBase::Resource == base->base_type()) { + res = static_cast<M2MResource *> (base); + } + +#ifndef DISABLE_DELAYED_RESPONSE + if (res && res->delayed_response()) { + if (!handle_delayed_response_store(resource_name.c_str(), + received_coap_header, + address, + M2MBase::DELAYED_POST_RESPONSE)) { + return 0; + } + } +#endif // DISABLE_DELAYED_RESPONSE + + } + + send_empty_ack(received_coap_header, address); + nsdl_coap_data_s *nsdl_coap_data = create_coap_event_data(received_coap_header, + address, + nsdl_handle, + received_coap_header->msg_code); + if (nsdl_coap_data) { + _event.data.event_type = MBED_CLIENT_NSDLINTERFACE_EVENT; + _event.data.data_ptr = (void*)nsdl_coap_data; + eventOS_event_send_user_allocated(&_event); + } else { + tr_error("M2MNsdlInterface::resource_callback() - failed to allocate nsdl_coap_data_s!"); + } + return 0; +} + +uint8_t M2MNsdlInterface::resource_callback_handle_event(sn_coap_hdr_s *received_coap_header, + sn_nsdl_addr_s *address) +{ + tr_debug("M2MNsdlInterface::resource_callback_handle_event"); + uint8_t result = 1; + uint8_t *payload = NULL; + bool free_payload = true; + sn_coap_hdr_s *coap_response = NULL; + sn_coap_msg_code_e msg_code = COAP_MSG_CODE_RESPONSE_CHANGED; // 4.00 + String resource_name = coap_to_string(received_coap_header->uri_path_ptr, + received_coap_header->uri_path_len); + + bool execute_value_updated = false; + M2MBase* base = find_resource(resource_name); + bool subscribed = false; + if (base) { + if (COAP_MSG_CODE_REQUEST_GET == received_coap_header->msg_code) { + coap_response = base->handle_get_request(_nsdl_handle, received_coap_header,this); + + if (coap_response && + coap_response->options_list_ptr && + coap_response->options_list_ptr->observe != STOP_OBSERVATION && + coap_response->options_list_ptr->observe != -1 && + coap_response->token_ptr) { + if (M2MBase::is_blockwise_needed(_nsdl_handle, coap_response->payload_len)) { + store_to_response_list(resource_name.c_str(), + received_coap_header->msg_id, + M2MBase::BLOCK_SUBSCRIBE); + } + subscribed = true; + } + } else if (COAP_MSG_CODE_REQUEST_PUT == received_coap_header->msg_code) { + coap_response = base->handle_put_request(_nsdl_handle, received_coap_header, this, execute_value_updated); + } else if (COAP_MSG_CODE_REQUEST_POST == received_coap_header->msg_code) { + if (base->base_type() == M2MBase::ResourceInstance) { + msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; + } else { + coap_response = base->handle_post_request(_nsdl_handle, + received_coap_header, + this, + execute_value_updated, + address); + +#ifndef DISABLE_DELAYED_RESPONSE + if (base->base_type() == M2MBase::Resource) { + M2MResource *res = (M2MResource*) base; + if (res->delayed_response()) { + tr_debug("M2MNsdlInterface::resource_callback_handle_event - final response sent by application"); + sn_nsdl_release_allocated_coap_msg_mem(_nsdl_handle, coap_response); + return 0; + } + } +#endif // DISABLE_DELAYED_RESPONSE + // Separate response used for POST + if (coap_response) { + coap_response->msg_type = COAP_MSG_TYPE_CONFIRMABLE; + } + } + } else if (COAP_MSG_CODE_REQUEST_DELETE == received_coap_header->msg_code) { + // Delete the object instance + M2MBase::BaseType type = base->base_type(); + if(M2MBase::ObjectInstance == type) { + M2MBase* base_object = find_resource(base->uri_path()); + if(base_object) { + M2MObject &object = ((M2MObjectInstance*)base_object)->get_parent_object(); + int slash_found = resource_name.find_last_of('/'); + // Object instance validty checks done in upper level, no need for error handling + if (slash_found != -1) { + const String object_name = resource_name.substr(slash_found + 1, resource_name.length()); + if (object.remove_object_instance(strtoul( + object_name.c_str(), + NULL, + 10))) { + msg_code = COAP_MSG_CODE_RESPONSE_DELETED; + } + } + } + } else { + msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; // 4.00 + } + } + } else { + tr_error("M2MNsdlInterface::resource_callback_handle_event() - Resource NOT FOUND"); + msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; // 4.00 + } + + if (!coap_response) { + coap_response = sn_nsdl_build_response(_nsdl_handle, received_coap_header, msg_code); + } + + // This copy will be passed to resource instance + if (received_coap_header->payload_len > 0 && received_coap_header->payload_ptr) { + payload = (uint8_t *) memory_alloc(received_coap_header->payload_len); + if (payload) { + assert(received_coap_header->payload_ptr); + memcpy(payload, received_coap_header->payload_ptr, received_coap_header->payload_len); + } else { + if (coap_response) { + coap_response->msg_code = COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_TOO_LARGE; + } + } + } + +#ifdef ENABLE_ASYNC_REST_RESPONSE + bool async_request_callback_called = false; +#endif + + if (coap_response && + coap_response->coap_status != COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVING && + coap_response->msg_code != COAP_MSG_CODE_EMPTY) { + bool send_response = true; + +#ifdef ENABLE_ASYNC_REST_RESPONSE + if (base) { + if (base->is_async_coap_request_callback_set()) { + // In case of error or callback not found go to default response flow + if (coap_response->msg_code < COAP_MSG_CODE_RESPONSE_BAD_REQUEST) { + M2MBase::Operation operation = operation_for_message_code(received_coap_header->msg_code); + tr_debug("M2MNsdlInterface::resource_callback_handle_event - final response sent by application for " + "operation 0x%x", operation); + base->call_async_coap_request_callback(received_coap_header, + operation, + async_request_callback_called); + + // Response sent by the application + if (async_request_callback_called) { + send_response = false; + result = 0; + } else { + tr_error("M2MNsdlInterface::resource_callback_handle_event - async callback not called!"); + } + } else { + remove_item_from_response_list(base->uri_path(), UNDEFINED_MSG_ID); + } + } + } +#endif //ENABLE_ASYNC_REST_RESPONSE + + // Send CoAP response only for methods which are not handled by application + if (send_response) { + (sn_nsdl_send_coap_message(_nsdl_handle, address, coap_response) == 0) ? result = 0 : result = 1; + } + + free(coap_response->payload_ptr); + coap_response->payload_ptr = NULL; + + // See if there any pending notification to be sent after resource is subscribed. + if (subscribed) { + _notification_handler->send_notification(this); + } + } + + // If the external blockwise storing is enabled call value updated only when all blocks have been received + if (execute_value_updated && + coap_response && + coap_response->coap_status != COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVING && + coap_response->msg_code < COAP_MSG_CODE_RESPONSE_BAD_REQUEST) { + if ((COAP_MSG_CODE_REQUEST_PUT == received_coap_header->msg_code) && + (base->base_type() == M2MBase::Resource || + base->base_type() == M2MBase::ResourceInstance)) { + M2MResourceBase* res = (M2MResourceBase*)base; + +#ifdef ENABLE_ASYNC_REST_RESPONSE + if (!async_request_callback_called) { +#endif + // Clear the old resource value since the data is now passed to application + if (res->block_message() && res->block_message()->is_block_message()) { + res->clear_value(); + } else { + // Ownership of payload moved to resource, skip the freeing. + free_payload = false; + res->set_value_raw(payload, received_coap_header->payload_len); + } +#ifdef ENABLE_ASYNC_REST_RESPONSE + } +#endif + } + if (coap_response->msg_code != COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_TOO_LARGE) { + value_updated(base); + } + } + + if (free_payload) { + free(payload); + } + + sn_nsdl_release_allocated_coap_msg_mem(_nsdl_handle, coap_response); + + return result; +} + +bool M2MNsdlInterface::process_received_data(uint8_t *data, + uint16_t data_size, + sn_nsdl_addr_s *address) +{ + tr_debug("M2MNsdlInterface::process_received_data(data size %d)", data_size); + return (0 == sn_nsdl_process_coap(_nsdl_handle, + data, + data_size, + address)) ? true : false; +} + +void M2MNsdlInterface::stop_timers() +{ + tr_debug("M2MNsdlInterface::stop_timers()"); + _registration_timer.stop_timer(); + _nsdl_execution_timer.stop_timer(); + _nsdl_execution_timer_running = false; + _bootstrap_id = 0; + _nsdl_handle->update_register_token = 0; + _nsdl_handle->unregister_token = 0; + _download_retry_timer.stop_timer(); +} + +void M2MNsdlInterface::timer_expired(M2MTimerObserver::Type type) +{ + if(M2MTimerObserver::NsdlExecution == type) { + sn_nsdl_exec(_nsdl_handle, _counter_for_nsdl); + _counter_for_nsdl++; + send_coap_ping(); + } else if((M2MTimerObserver::Registration) == type && + (is_unregister_ongoing() == false) && + (is_update_register_ongoing() == false)) { + tr_debug("M2MNsdlInterface::timer_expired - Send update registration"); + if (!send_update_registration()) { + // Most likely case would be memory allocation failure + _observer.registration_error(M2MInterface::MemoryFail, false); + } + } else if (M2MTimerObserver::RetryTimer == type) { + send_pending_request(); + } +} + +bool M2MNsdlInterface::observation_to_be_sent(M2MBase *object, + uint16_t obs_number, + const m2m::Vector<uint16_t> &changed_instance_ids, + bool send_object) +{ + claim_mutex(); + + if (object && _nsdl_execution_timer_running && _registered) { + tr_debug("M2MNsdlInterface::observation_to_be_sent() uri %s", object->uri_path()); + + if (!_notification_send_ongoing) { + _notification_send_ongoing = true; + object->report_handler()->set_notification_in_queue(false); + M2MBase::BaseType type = object->base_type(); + + if (type == M2MBase::Object) { + send_object_observation(static_cast<M2MObject*> (object), + obs_number, + changed_instance_ids, + send_object); + } else if (type == M2MBase::ObjectInstance) { + send_object_instance_observation(static_cast<M2MObjectInstance*> (object), obs_number); + } else if (type == M2MBase::Resource) { + send_resource_observation(static_cast<M2MResource*> (object), obs_number); + } + + release_mutex(); + return true; + } else { + tr_info("M2MNsdlInterface::observation_to_be_sent() - send already in progress"); + } + } else { + tr_info("M2MNsdlInterface::observation_to_be_sent() - object NULL, in reconnection mode or not registered"); + } + + release_mutex(); + + return false; +} + +#ifndef DISABLE_DELAYED_RESPONSE +void M2MNsdlInterface::send_delayed_response(M2MBase *base) +{ + claim_mutex(); + tr_debug("M2MNsdlInterface::send_delayed_response()"); + M2MResource *resource = NULL; + if(base) { + if(M2MBase::Resource == base->base_type()) { + resource = static_cast<M2MResource *> (base); + } + if(resource) { + coap_response_s* resp = find_delayed_response(resource->uri_path(), M2MBase::DELAYED_POST_RESPONSE); + // If there is no response it means that this API is called + // before actual POST request has received the device + if (resp) { + sn_coap_hdr_s coap_response; + + memset(&coap_response,0,sizeof(sn_coap_hdr_s)); + + coap_response.msg_type = COAP_MSG_TYPE_CONFIRMABLE; + coap_response.msg_code = COAP_MSG_CODE_RESPONSE_CHANGED; + resource->get_delayed_token(coap_response.token_ptr,coap_response.token_len); + + uint32_t length = 0; + resource->get_value(coap_response.payload_ptr, length); + coap_response.payload_len = length; + + if (sn_nsdl_send_coap_message(_nsdl_handle, &_nsdl_handle->server_address, &coap_response) >= 0) { + // Update msgid, this will be used to track server response + resp->msg_id = coap_response.msg_id; + base->send_message_delivery_status(*base, M2MBase::MESSAGE_STATUS_SENT, M2MBase::DELAYED_POST_RESPONSE); + } else { + // Failed to create a message + base->send_message_delivery_status(*base, M2MBase::MESSAGE_STATUS_SEND_FAILED, M2MBase::DELAYED_POST_RESPONSE); + // Remove stored response from the list + remove_item_from_response_list(resource->uri_path(), UNDEFINED_MSG_ID); + } + + free(coap_response.payload_ptr); + free(coap_response.token_ptr); + } else { + tr_error("M2MNsdlInterface::send_delayed_response() - request not in list!"); + } + } + } + release_mutex(); +} +#endif //DISABLE_DELAYED_RESPONSE + +#ifdef ENABLE_ASYNC_REST_RESPONSE +void M2MNsdlInterface::send_asynchronous_response(M2MBase *base, + const uint8_t *payload, + size_t payload_len, + const uint8_t *token, + const uint8_t token_len, + coap_response_code_e code) +{ + claim_mutex(); + tr_debug("M2MNsdlInterface::send_asynchronous_response() %s", base->uri_path()); + if (base) { + coap_response_s* resp = find_delayed_response(base->uri_path(), M2MBase::DELAYED_RESPONSE); + // If there is no response it means that this API is called + // before actual GET/PUT/POST request has been received in the device. + if (resp) { + sn_coap_hdr_s *coap_response = (sn_coap_hdr_s *) memory_alloc(sizeof(sn_coap_hdr_s)); + bool msg_sent = false; + if (coap_response) { + memset(coap_response, 0, sizeof(sn_coap_hdr_s)); + + coap_response->msg_type = COAP_MSG_TYPE_CONFIRMABLE; + coap_response->msg_code = (sn_coap_msg_code_e) code; + coap_response->token_ptr = (uint8_t*)token; + coap_response->token_len = token_len; + coap_response->payload_ptr = (uint8_t*)payload; + coap_response->payload_len = payload_len; + + if (sn_nsdl_send_coap_message(_nsdl_handle, &_nsdl_handle->server_address, coap_response) >= 0) { + // Update msgid, this will be used to track server response + resp->msg_id = coap_response->msg_id; + base->send_message_delivery_status(*base, M2MBase::MESSAGE_STATUS_SENT, M2MBase::DELAYED_RESPONSE); + msg_sent = true; + if (M2MBase::is_blockwise_needed(_nsdl_handle, payload_len)) { + resp->blockwise_used = true; + } + } + + coap_response->token_ptr = NULL; + coap_response->payload_ptr = NULL; + sn_nsdl_release_allocated_coap_msg_mem(_nsdl_handle, coap_response); + } + + if (!msg_sent) { + // Failed to create a message + base->send_message_delivery_status(*base, M2MBase::MESSAGE_STATUS_SEND_FAILED, M2MBase::DELAYED_RESPONSE); + // Remove stored response from the list + remove_item_from_response_list(base->uri_path(), UNDEFINED_MSG_ID); + } + + } else { + tr_error("M2MNsdlInterface::send_delayed_response() - request not in list!"); + } + + } + release_mutex(); +} +#endif //ENABLE_ASYNC_REST_RESPONSE + +void M2MNsdlInterface::resource_to_be_deleted(M2MBase *base) +{ + tr_debug("M2MNsdlInterface::resource_to_be_deleted() %p", base); + claim_mutex(); + remove_nsdl_resource(base); +#if !defined(DISABLE_DELAYED_RESPONSE) || defined(ENABLE_ASYNC_REST_RESPONSE) + remove_items_from_response_list_for_uri(base->uri_path()); +#endif + // Since the M2MObject's are stored in _base_list, they need to be removed from there also. + if (base && base->base_type() == M2MBase::Object) { + remove_object(base); + } + + release_mutex(); +} + +void M2MNsdlInterface::value_updated(M2MBase *base) +{ + tr_debug("M2MNsdlInterface::value_updated()"); + String name; + if(base) { + switch(base->base_type()) { + case M2MBase::Object: + create_nsdl_object_structure(static_cast<M2MObject*> (base)); + name = base->name(); + break; + case M2MBase::ObjectInstance: + create_nsdl_object_instance_structure(static_cast<M2MObjectInstance*> (base)); + name = static_cast<M2MObjectInstance*> (base)->get_parent_object().name(); + break; + case M2MBase::Resource: { + M2MResource* resource = static_cast<M2MResource*> (base); + create_nsdl_resource_structure(resource, resource->supports_multiple_instances()); + name = base->name(); + break; + } + case M2MBase::ResourceInstance: { + M2MResourceInstance* instance = static_cast<M2MResourceInstance*> (base); + create_nsdl_resource(instance); + name = static_cast<M2MResourceInstance*> (base)->get_parent_resource().name(); + break; + } +#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION + case M2MBase::ObjectDirectory: + tr_error("M2MNsdlInterface::value_updated() - unsupported ObjectDirectory base type!"); + return; +#endif + } + } + + if (base && base->is_value_updated_function_set()) { + base->execute_value_updated(name); + } + else { + _observer.value_updated(base); + } +} + +void M2MNsdlInterface::remove_object(M2MBase *object) +{ + claim_mutex(); + tr_debug("M2MNsdlInterface::remove_object() %p", object); + M2MObject* rem_object = static_cast<M2MObject*> (object); + if(rem_object && !_base_list.empty()) { + M2MBaseList::const_iterator it; + it = _base_list.begin(); + int index = 0; + for ( ; it != _base_list.end(); it++, index++ ) { + if((*it)->base_type() == M2MBase::Object && (*it) == rem_object) { + _base_list.erase(index); + break; + } + } + } + release_mutex(); +} + +bool M2MNsdlInterface::create_nsdl_structure(M2MBase *base) +{ + tr_debug("M2MNsdlInterface::create_nsdl_structure()"); + bool success = false; + if(base) { + switch (base->base_type()) { +#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION + case M2MBase::ObjectDirectory: + success = create_nsdl_endpoint_structure((M2MEndpoint*)base); + break; +#endif + case M2MBase::Object: + success = create_nsdl_object_structure((M2MObject*)base); + break; + default: + break; + } + } + return success; +} + +#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION +bool M2MNsdlInterface::create_nsdl_endpoint_structure(M2MEndpoint *endpoint) +{ + tr_debug("M2MNsdlInterface::create_nsdl_endpoint_structure()"); + bool success = false; + if(endpoint) { + success = true; + if (endpoint->get_changed()) { + const M2MObjectList &object_list = endpoint->objects(); + tr_debug("M2MNsdlInterface::create_nsdl_endpoint_structure - Object count %d", object_list.size()); + if(!endpoint->is_deleted() && !object_list.empty()) { + M2MObjectList::const_iterator it; + it = object_list.begin(); + for ( ; it != object_list.end(); it++ ) { + // Create NSDL structure for all object instances inside + success = create_nsdl_object_structure(*it); + } + } + if (!create_nsdl_resource(endpoint)) { + success = false; + } + endpoint->clear_changed(); + } + } + return success; +} +#endif + +bool M2MNsdlInterface::create_nsdl_object_structure(M2MObject *object) +{ + bool success = false; + if (object) { + const M2MObjectInstanceList &instance_list = object->instances(); + if (!instance_list.empty()) { + M2MObjectInstanceList::const_iterator it; + it = instance_list.begin(); + for ( ; it != instance_list.end(); it++ ) { + // Create NSDL structure for all object instances inside + success = create_nsdl_object_instance_structure(*it); + if (!success) { + tr_error("M2MNsdlInterface::create_nsdl_object_structure - fail to create resource"); + return false; + } + } + } + } + + // If marked as NOT_ALLOWED then there is no need to + // create nsdl resource at all since it will not be published to mds + if (object && object->operation() != M2MBase::NOT_ALLOWED) { + success = create_nsdl_resource(object); + } else { + success = true; + } + + return success; +} + +bool M2MNsdlInterface::create_nsdl_object_instance_structure(M2MObjectInstance *object_instance) +{ + bool success = false; + + if (object_instance) { + const M2MResourceList &res_list = object_instance->resources(); + if (!res_list.empty()) { + M2MResourceList::const_iterator it; + it = res_list.begin(); + for ( ; it != res_list.end(); it++ ) { + // Create NSDL structure for all resources inside + success = create_nsdl_resource_structure(*it, (*it)->supports_multiple_instances()); + if (!success) { + tr_error("M2MNsdlInterface::create_nsdl_object_instance_structure - fail to create resource"); + return false; + } + } + } + + // If marked as NOT_ALLOWED then there is no need to + // create nsdl resource at all since it will not be published to mds + if (object_instance->operation() != M2MBase::NOT_ALLOWED) { + success = create_nsdl_resource(object_instance); + } else { + success = true; + } + } + + return success; +} + +bool M2MNsdlInterface::create_nsdl_resource_structure(M2MResource *res, + bool multiple_instances) +{ + bool success = false; + if (res) { + // if there are multiple instances supported + if (multiple_instances) { + const M2MResourceInstanceList &res_list = res->resource_instances(); + if (!res_list.empty()) { + M2MResourceInstanceList::const_iterator it; + it = res_list.begin(); + for ( ; it != res_list.end(); it++ ) { + success = create_nsdl_resource((*it)); + if (!success) { + tr_error("M2MNsdlInterface::create_nsdl_resource_structure - instance creation failed"); + return false; + } + } + // Register the main Resource as well along with ResourceInstances + success = create_nsdl_resource(res); + } + } else { + success = create_nsdl_resource(res); + } + } + return success; +} + +bool M2MNsdlInterface::create_nsdl_resource(M2MBase *base) +{ + claim_mutex(); + bool success = false; + + if (base) { + int8_t result = 0; + sn_nsdl_dynamic_resource_parameters_s* nsdl_resource = base->get_nsdl_resource(); + + // needed on deletion + if (base->observation_handler() == NULL) { + base->set_observation_handler(this); + } + + result = sn_nsdl_put_resource(_nsdl_handle, nsdl_resource); + + // Put under observation if auto-obs feature is set. + if (nsdl_resource && + nsdl_resource->auto_observable && + result != SN_GRS_RESOURCE_ALREADY_EXISTS) { + base->set_under_observation(true, base->observation_handler()); + + // Increment auto-obs token to be unique in every object + _auto_obs_token++; + if (_auto_obs_token > AUTO_OBS_TOKEN_MAX) { + _auto_obs_token = 1; + } + + // Store token in big-endian byte order + uint8_t token[sizeof(uint16_t)]; + common_write_16_bit(_auto_obs_token, token); + base->set_observation_token(token, sizeof(uint16_t)); + + switch (base->base_type()) { + case M2MBase::Object: + base->add_observation_level(M2MBase::O_Attribute); + break; + + case M2MBase::ObjectInstance: + base->add_observation_level(M2MBase::OI_Attribute); + break; + + case M2MBase::Resource: + case M2MBase::ResourceInstance: + base->add_observation_level(M2MBase::R_Attribute); + break; +#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION + case M2MBase::ObjectDirectory: + break; +#endif + } + } +#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION + else if (base->base_type() == M2MBase::ObjectDirectory) { + M2MEndpoint *endpoint = (M2MEndpoint*) base; + if (endpoint->is_deleted()) { + sn_nsdl_dynamic_resource_parameters_s *nsdl_resource = endpoint->get_nsdl_resource(); + nsdl_resource->registered = SN_NDSL_RESOURCE_DELETE; + } + } +#endif + + // Either the resource is created or it already + // exists , then result is success. + if (result == 0 || + result == SN_GRS_RESOURCE_ALREADY_EXISTS){ + success = true; + } + } + + release_mutex(); + return success; +} + +// convenience method to get the URI from its buffer field... +String M2MNsdlInterface::coap_to_string(const uint8_t *coap_data, int coap_data_length) +{ + String value; + if (coap_data != NULL && coap_data_length > 0) { + value.append_raw((char *)coap_data,coap_data_length); + } + return value; +} + +uint64_t M2MNsdlInterface::registration_time() const +{ + uint64_t value = 0; + if(_endpoint) { + value = _server->resource_value_int(M2MServer::Lifetime); + } + if(value < MINIMUM_REGISTRATION_TIME) { + tr_warn("M2MNsdlInterface::registration_time - stored value in resource (in seconds) %" PRIu64, value); + value = MINIMUM_REGISTRATION_TIME; + } + + if(value >= OPTIMUM_LIFETIME) { + value = value - REDUCE_LIFETIME; + } else { + value = REDUCTION_FACTOR * value; + } + tr_debug("M2MNsdlInterface::registration_time - value (in seconds) %" PRIu64, value); + return value; +} + +M2MBase* M2MNsdlInterface::find_resource(const String &object_name) const +{ + tr_debug("M2MNsdlInterface::find_resource(object level) - from %p name (%s) ", this, object_name.c_str()); + M2MObject *current = NULL; + M2MBase *found = NULL; + if(!_base_list.empty()) { + M2MBaseList::const_iterator it; + it = _base_list.begin(); + for ( ; it != _base_list.end(); it++ ) { + if ((*it)->base_type() == M2MBase::Object) { + current = (M2MObject*)*it; + tr_debug("M2MNsdlInterface::find_resource(object level) - path (%s)", + (char*)current->uri_path()); + if (strcmp((char*)current->uri_path(), object_name.c_str()) == 0) { + found = current; + tr_debug("M2MNsdlInterface::find_resource(%s) found", object_name.c_str()); + break; + } + + found = find_resource(current, object_name); + if(found != NULL) { + break; + } + } +#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION + else if ((*it)->base_type() == M2MBase::ObjectDirectory) { + M2MEndpoint *ep = (M2MEndpoint*)*it; + if(!strcmp((char*)(*it)->uri_path(), object_name.c_str())) { + found = NULL; + break; + } else { + found = find_resource(ep, object_name); + } + if(found != NULL) { + break; + } + } +#endif + } + } + return found; +} + +#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION +M2MBase* M2MNsdlInterface::find_resource(const M2MEndpoint *endpoint, + const String &object_name) const +{ + tr_debug("M2MNsdlInterface::find_resource(endpoint level) - name (%s)", object_name.c_str()); + M2MBase *object = NULL; + if(endpoint) { + const M2MObjectList &list = endpoint->objects(); + if(!list.empty()) { + M2MObjectList::const_iterator it; + it = list.begin(); + for ( ; it != list.end(); it++ ) { + if (!strcmp((char*)(*it)->uri_path(), object_name.c_str())) { + tr_debug("M2MNsdlInterface::find_resource(endpoint level) - object %p object name (%s)", + object, object_name.c_str()); + object = (*it); + break; + } + + object = find_resource((*it),object_name); + if(object != NULL){ + break; + } + } + } + } + return object; +} +#endif + +M2MBase* M2MNsdlInterface::find_resource(const M2MObject *object, + const String &object_instance) const +{ + M2MBase *instance = NULL; + if(object) { + const M2MObjectInstanceList &list = object->instances(); + if(!list.empty()) { + M2MObjectInstanceList::const_iterator it; + it = list.begin(); + for ( ; it != list.end(); it++ ) { + if(!strcmp((char*)(*it)->uri_path(), object_instance.c_str())){ + instance = (*it); + tr_debug("M2MNsdlInterface::find_resource(object instance level) - found (%s)", + (char*)(*it)->uri_path()); + break; + } + + instance = find_resource((*it),object_instance); + if(instance != NULL){ + break; + } + } + } + } + return instance; +} + +M2MBase* M2MNsdlInterface::find_resource(const M2MObjectInstance *object_instance, + const String &resource_instance) const +{ + M2MBase *instance = NULL; + if(object_instance) { + const M2MResourceList &list = object_instance->resources(); + if(!list.empty()) { + M2MResourceList::const_iterator it; + it = list.begin(); + for ( ; it != list.end(); it++ ) { + if(!strcmp((char*)(*it)->uri_path(), resource_instance.c_str())) { + instance = *it; + break; + } + else if((*it)->supports_multiple_instances()) { + instance = find_resource((*it), (*it)->uri_path(), + resource_instance); + if(instance != NULL){ + break; + } + } + } + } + } + return instance; +} + +M2MBase* M2MNsdlInterface::find_resource(const M2MResource *resource, + const String &object_name, + const String &resource_instance) const +{ + M2MBase *res = NULL; + if(resource) { + if(resource->supports_multiple_instances()) { + const M2MResourceInstanceList &list = resource->resource_instances(); + if(!list.empty()) { + M2MResourceInstanceList::const_iterator it; + it = list.begin(); + for ( ; it != list.end(); it++ ) { + if(!strcmp((char*)(*it)->uri_path(), resource_instance.c_str())){ + res = (*it); + break; + } + } + } + } + } + return res; +} + +bool M2MNsdlInterface::object_present(M2MBase* base) const +{ + bool success = false; + if(base && !_base_list.empty()) { + M2MBaseList::const_iterator it; + it = _base_list.begin(); + for ( ; it != _base_list.end(); it++ ) { + if((*it) == base) { + success = true; + break; + } + } + } + return success; +} + +int M2MNsdlInterface::object_index(M2MBase* base) const +{ + int found_index = -1; + int index; + if(base && !_base_list.empty()) { + M2MBaseList::const_iterator it; + + for (it = _base_list.begin(), index = 0; it != _base_list.end(); it++, index++) { + if((*it) == base) { + found_index = index; + break; + } + } + } + return found_index; +} + + +bool M2MNsdlInterface::add_object_to_list(M2MBase* object) +{ + tr_debug("M2MNsdlInterface::add_object_to_list this=%p object=%p", this, object); + bool success = false; + if(object && !object_present(object)) { + _base_list.push_back(object); + success = true; + } + return success; +} + +bool M2MNsdlInterface::remove_object_from_list(M2MBase* object) +{ + tr_debug("M2MNsdlInterface::remove_object_from_list this=%p object=%p", this, object); + bool success = false; + int index; + if(object && (-1 != (index = object_index(object)))) { + tr_debug(" object found at index %d", index); + _base_list.erase(index); + success = true; + } + return success; +} + +M2MInterface::Error M2MNsdlInterface::interface_error(const sn_coap_hdr_s &coap_header) +{ + M2MInterface::Error error; + switch(coap_header.msg_code) { + case COAP_MSG_CODE_RESPONSE_BAD_REQUEST: + case COAP_MSG_CODE_RESPONSE_BAD_OPTION: + case COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_INCOMPLETE: + case COAP_MSG_CODE_RESPONSE_PRECONDITION_FAILED: + case COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_TOO_LARGE: + case COAP_MSG_CODE_RESPONSE_UNSUPPORTED_CONTENT_FORMAT: + error = M2MInterface::InvalidParameters; + break; + case COAP_MSG_CODE_RESPONSE_UNAUTHORIZED: + case COAP_MSG_CODE_RESPONSE_FORBIDDEN: + case COAP_MSG_CODE_RESPONSE_NOT_ACCEPTABLE: + case COAP_MSG_CODE_RESPONSE_NOT_FOUND: + case COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED: + error = M2MInterface::NotAllowed; + break; + case COAP_MSG_CODE_RESPONSE_CREATED: + case COAP_MSG_CODE_RESPONSE_DELETED: + case COAP_MSG_CODE_RESPONSE_VALID: + case COAP_MSG_CODE_RESPONSE_CHANGED: + case COAP_MSG_CODE_RESPONSE_CONTENT: + error = M2MInterface::ErrorNone; + break; + default: + error = M2MInterface::UnknownError; + break; + } + if(coap_header.coap_status == COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED || + coap_header.coap_status == COAP_STATUS_BUILDER_BLOCK_SENDING_FAILED) { + error = M2MInterface::NetworkError; + } + return error; +} + +const char *M2MNsdlInterface::coap_error(const sn_coap_hdr_s &coap_header) +{ + if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_BAD_REQUEST) { + return COAP_ERROR_REASON_1; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_BAD_OPTION) { + return COAP_ERROR_REASON_2; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_INCOMPLETE) { + return COAP_ERROR_REASON_3; + }else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_PRECONDITION_FAILED) { + return COAP_ERROR_REASON_4; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_REQUEST_ENTITY_TOO_LARGE) { + return COAP_ERROR_REASON_5; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_UNSUPPORTED_CONTENT_FORMAT) { + return COAP_ERROR_REASON_6; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_UNAUTHORIZED) { + return COAP_ERROR_REASON_7; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_FORBIDDEN) { + return COAP_ERROR_REASON_8; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_NOT_ACCEPTABLE) { + return COAP_ERROR_REASON_9; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_NOT_FOUND) { + return COAP_ERROR_REASON_10; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_METHOD_NOT_ALLOWED) { + return COAP_ERROR_REASON_11; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_SERVICE_UNAVAILABLE) { + return COAP_ERROR_REASON_13; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_INTERNAL_SERVER_ERROR) { + return COAP_ERROR_REASON_14; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_BAD_GATEWAY) { + return COAP_ERROR_REASON_15; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_GATEWAY_TIMEOUT) { + return COAP_ERROR_REASON_16; + } else if (coap_header.msg_code == COAP_MSG_CODE_RESPONSE_PROXYING_NOT_SUPPORTED) { + return COAP_ERROR_REASON_17; + } else if (coap_header.coap_status == COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED || + coap_header.coap_status == COAP_STATUS_BUILDER_BLOCK_SENDING_FAILED) { + return COAP_ERROR_REASON_12; + } + return COAP_NO_ERROR; +} + +void M2MNsdlInterface::send_object_observation(M2MObject *object, + uint16_t obs_number, + const m2m::Vector<uint16_t> &changed_instance_ids, + bool send_object) +{ + tr_info("M2MNsdlInterface::send_object_observation"); + if(object) { + uint8_t *value = 0; + uint32_t length = 0; + uint8_t token[MAX_TOKEN_SIZE]; + uint8_t token_length = 0; + + // Send whole object structure + if (send_object) { + value = M2MTLVSerializer::serialize(object->instances(), length); + } + // Send only changed object instances + else { + M2MObjectInstanceList list; + Vector<uint16_t>::const_iterator it; + it = changed_instance_ids.begin(); + for (; it != changed_instance_ids.end(); it++){ + M2MObjectInstance* obj_instance = object->object_instance(*it); + if (obj_instance){ + list.push_back(obj_instance); + } + } + if (!list.empty()) { + value = M2MTLVSerializer::serialize(list, length); + list.clear(); + } + } + + object->get_observation_token((uint8_t*)&token,token_length); + + object->report_handler()->set_blockwise_notify(M2MBase::is_blockwise_needed(_nsdl_handle, length)); + + int32_t msgid = sn_nsdl_send_observation_notification(_nsdl_handle, token, token_length, value, length, + sn_coap_observe_e(obs_number), COAP_MSG_TYPE_CONFIRMABLE, + sn_coap_content_format_e(object->coap_content_type()), -1); + execute_notification_delivery_status_cb(object, msgid); + + memory_free(value); + } +} + +void M2MNsdlInterface::send_object_instance_observation(M2MObjectInstance *object_instance, + uint16_t obs_number) +{ + tr_info("M2MNsdlInterface::send_object_instance_observation"); + if(object_instance) { + uint8_t *value = 0; + uint32_t length = 0; + uint8_t token[MAX_TOKEN_SIZE]; + uint8_t token_length = 0; + + value = M2MTLVSerializer::serialize(object_instance->resources(), length); + + object_instance->get_observation_token((uint8_t*)&token,token_length); + + object_instance->report_handler()->set_blockwise_notify(M2MBase::is_blockwise_needed(_nsdl_handle, length)); + + int32_t msgid = sn_nsdl_send_observation_notification(_nsdl_handle, token, token_length, value, length, + sn_coap_observe_e(obs_number), COAP_MSG_TYPE_CONFIRMABLE, + sn_coap_content_format_e(object_instance->coap_content_type()), -1); + + execute_notification_delivery_status_cb(object_instance, msgid); + + + memory_free(value); + } +} + +void M2MNsdlInterface::send_resource_observation(M2MResource *resource, + uint16_t obs_number) +{ + if(resource) { + tr_info("M2MNsdlInterface::send_resource_observation - uri %s", resource->uri_path()); + uint8_t *value = 0; + uint32_t length = 0; + uint8_t token[MAX_TOKEN_SIZE]; + uint8_t token_length = 0; + + resource->get_observation_token((uint8_t*)token,token_length); + uint16_t content_type = resource->coap_content_type(); + if (M2MResourceBase::OPAQUE == resource->resource_instance_type()) { + content_type = COAP_CONTENT_OMA_OPAQUE_TYPE; + } + + if (resource->resource_instance_count() > 0 || content_type == COAP_CONTENT_OMA_TLV_TYPE) { + value = M2MTLVSerializer::serialize(resource, length); + } else { + resource->get_value(value,length); + } + + resource->report_handler()->set_blockwise_notify(M2MBase::is_blockwise_needed(_nsdl_handle, length)); + + int32_t msgid = sn_nsdl_send_observation_notification(_nsdl_handle, token, token_length, value, length, + sn_coap_observe_e(obs_number), + COAP_MSG_TYPE_CONFIRMABLE, + sn_coap_content_format_e(content_type), -1); + execute_notification_delivery_status_cb(resource, msgid); + + memory_free(value); + } +} +nsdl_s * M2MNsdlInterface::get_nsdl_handle() const +{ + return _nsdl_handle; +} + +void M2MNsdlInterface::handle_bootstrap_put_message(sn_coap_hdr_s *coap_header, + sn_nsdl_addr_s *address) { +#ifndef M2M_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_info("M2MNsdlInterface::handle_bootstrap_put_message"); + uint8_t response_code = COAP_MSG_CODE_RESPONSE_CHANGED; + sn_coap_hdr_s *coap_response = NULL; + bool success = false; + uint16_t content_type = 0; + char buffer[MAX_ALLOWED_ERROR_STRING_LENGTH]; + buffer[0] = '\0'; + M2MNsdlInterface::ObjectType object_type = M2MNsdlInterface::SECURITY; + + if (!_security) { + _security = M2MSecurity::get_instance(); + } + + String resource_name = coap_to_string(coap_header->uri_path_ptr, + coap_header->uri_path_len); + tr_debug("M2MNsdlInterface::handle_bootstrap_put_message - object path %s", resource_name.c_str()); + + // Security object + if (resource_name.compare(0,1,"0") == 0) { + object_type = M2MNsdlInterface::SECURITY; + success = true; + } + // Server object + else if (resource_name.compare(0,1,"1") == 0) { + object_type = M2MNsdlInterface::SERVER; + success = true; + } + // Device object + else if (resource_name.compare(0,1,"3") == 0) { + M2MDevice* dev = M2MInterfaceFactory::create_device(); + // Not mandatory resource, that's why it must be created first + M2MResource *res = dev->create_resource(M2MDevice::CurrentTime, 0); + if (res) { + res->set_auto_observable(true); + } + object_type = M2MNsdlInterface::DEVICE; + success = true; + } + + if (success) { + if(coap_header->content_format != COAP_CT_NONE) { + content_type = coap_header->content_format; + } + + if (content_type != COAP_CONTENT_OMA_TLV_TYPE && + content_type != COAP_CONTENT_OMA_TLV_TYPE_OLD) { + tr_error("M2MNsdlInterface::handle_bootstrap_put_message - content_type %d", content_type); + success = false; + } + // Parse TLV message and check is the object valid + if (success) { + change_operation_mode(_security, M2MBase::PUT_ALLOWED); + success = parse_bootstrap_message(coap_header, object_type); + if (success && object_type == M2MNsdlInterface::SECURITY) { + success = validate_security_object(); + if (!success) { + const char *desc = "Invalid security object"; + if (strlen(ERROR_REASON_22) + strlen(desc) <= MAX_ALLOWED_ERROR_STRING_LENGTH) { + snprintf(buffer, sizeof(buffer), ERROR_REASON_22, desc); + } + response_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; + } + } + // Set operation back to default ones + if (_security) { + change_operation_mode(_security, M2MBase::NOT_ALLOWED); + } + } + } + + if (!success) { + response_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; + } + + coap_response = sn_nsdl_build_response(_nsdl_handle, + coap_header, + response_code); + + if (coap_response) { + sn_nsdl_send_coap_message(_nsdl_handle, address, coap_response); + sn_nsdl_release_allocated_coap_msg_mem(_nsdl_handle, coap_response); + } + + if (!success) { + // Do not overwrite ERROR_REASON_22 + if (strlen(buffer) == 0) { + if (strlen(ERROR_REASON_20) + resource_name.size() <= MAX_ALLOWED_ERROR_STRING_LENGTH) { + snprintf(buffer, sizeof(buffer), ERROR_REASON_20, resource_name.c_str()); + } + } + handle_bootstrap_error(buffer, true); + } +#else + (void) coap_header; + (void) address; +#endif +} + +bool M2MNsdlInterface::parse_bootstrap_message(sn_coap_hdr_s *coap_header, + M2MNsdlInterface::ObjectType lwm2m_object_type) +{ +#ifndef M2M_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_info("M2MNsdlInterface::parse_bootstrap_message"); + bool ret = false; + bool is_obj_instance = false; + uint16_t instance_id = 0; + if (_security) { + ret = is_obj_instance = M2MTLVDeserializer::is_object_instance(coap_header->payload_ptr); + if (!is_obj_instance) { + ret = M2MTLVDeserializer::is_resource(coap_header->payload_ptr); + } + if (ret) { + M2MTLVDeserializer::Error error = M2MTLVDeserializer::None; + if (is_obj_instance) { + M2MObject* dev_object = static_cast<M2MObject*> (M2MInterfaceFactory::create_device()); + + switch (lwm2m_object_type) { + case M2MNsdlInterface::SECURITY: + instance_id = M2MTLVDeserializer::instance_id(coap_header->payload_ptr); + if (_security->object_instance(instance_id) == NULL) { + tr_debug("M2MNsdlInterface::parse_bootstrap_message - create instance %d", instance_id); + _security->create_object_instance(M2MSecurity::M2MServer); + change_operation_mode(_security, M2MBase::PUT_ALLOWED); + } + + error = M2MTLVDeserializer::deserialise_object_instances(coap_header->payload_ptr, + coap_header->payload_len, + *_security, + M2MTLVDeserializer::Put); + break; + case M2MNsdlInterface::SERVER: + error = M2MTLVDeserializer::deserialise_object_instances(coap_header->payload_ptr, + coap_header->payload_len, + *_server, + M2MTLVDeserializer::Put); + break; + case M2MNsdlInterface::DEVICE: + error = M2MTLVDeserializer::deserialise_object_instances(coap_header->payload_ptr, + coap_header->payload_len, + *dev_object, + M2MTLVDeserializer::Put); + break; + default: + break; + } + } + else { + instance_id = M2MTLVDeserializer::instance_id(coap_header->payload_ptr); + M2MObjectInstance* instance = NULL; + switch (lwm2m_object_type) { + case M2MNsdlInterface::SECURITY: + instance = _security->object_instance(instance_id); + if (instance) { + error = M2MTLVDeserializer::deserialize_resources(coap_header->payload_ptr, + coap_header->payload_len, + *instance, + M2MTLVDeserializer::Put); + } else { + error = M2MTLVDeserializer::NotValid; + } + + break; + case M2MNsdlInterface::SERVER: + instance = _server->object_instance(instance_id); + if (instance) { + error = M2MTLVDeserializer::deserialize_resources(coap_header->payload_ptr, + coap_header->payload_len, + *instance, + M2MTLVDeserializer::Post); + } else { + error = M2MTLVDeserializer::NotValid; + } + + break; + case M2MNsdlInterface::DEVICE: + default: + break; + } + } + + if (error != M2MTLVDeserializer::None) { + tr_error("M2MNsdlInterface::parse_bootstrap_message - error %d", error); + ret = false; + } + } + } else { + tr_error("M2MNsdlInterface::parse_bootstrap_message -- no security object!"); + } + return ret; +#else + (void) coap_header; + (void) is_security_object; + return false; +#endif +} + +void M2MNsdlInterface::handle_bootstrap_finished(sn_coap_hdr_s *coap_header,sn_nsdl_addr_s *address) +{ +#ifndef M2M_CLIENT_DISABLE_BOOTSTRAP_FEATURE + char buffer[MAX_ALLOWED_ERROR_STRING_LENGTH]; + + String object_name = coap_to_string(coap_header->uri_path_ptr, + coap_header->uri_path_len); + + int32_t m2m_id = -1; + // Security object can be null in case messages are coming in wrong order, for example + // BS POST is received before BS PUT. + if (_security) { + m2m_id = _security->get_security_instance_id(M2MSecurity::M2MServer); + } + + tr_info("M2MNsdlInterface::handle_bootstrap_finished - path: %s, m2mid: %" PRId32, object_name.c_str(), m2m_id); + +#ifndef MBED_CLIENT_DISABLE_EST_FEATURE + // In EST mode we must receive iep in uri-query + bool est_iep_ok = false; + if (m2m_id >= 0 && + _security->resource_value_int(M2MSecurity::SecurityMode, m2m_id) == M2MSecurity::EST) { + if (coap_header->options_list_ptr && coap_header->options_list_ptr->uri_query_ptr) { + String uri_query = coap_to_string(coap_header->options_list_ptr->uri_query_ptr, + coap_header->options_list_ptr->uri_query_len); + tr_info("M2MNsdlInterface::handle_bootstrap_finished - query: %s", uri_query.c_str()); + const char *iep_ptr = NULL; + const int iep_len = parse_query_parameter_value_from_query(uri_query.c_str(), QUERY_PARAM_IEP, &iep_ptr); + if (iep_ptr && iep_len > 0) { + est_iep_ok = true; + _internal_endpoint_name.clear(); + _internal_endpoint_name.append_raw(iep_ptr, iep_len); + tr_info("M2MNsdlInterface::handle_bootstrap_finished - iep: %s", _internal_endpoint_name.c_str()); + } + } + } +#endif + + sn_coap_hdr_s *coap_response = NULL; + uint8_t msg_code = COAP_MSG_CODE_RESPONSE_CHANGED; + // Accept only '/bs' path and check that needed data is in security object + if (object_name.size() != 2 || + object_name.compare(0, 2, BOOTSTRAP_URI) != 0) { + if (strlen(ERROR_REASON_22) + object_name.size() <= MAX_ALLOWED_ERROR_STRING_LENGTH) { + snprintf(buffer, sizeof(buffer), ERROR_REASON_22, object_name.c_str()); + } + msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; + } +#ifndef MBED_CLIENT_DISABLE_EST_FEATURE + else if (!est_iep_ok && + m2m_id >= 0 && + _security->resource_value_int(M2MSecurity::SecurityMode, m2m_id) == M2MSecurity::EST) { + tr_error("M2MNsdlInterface::handle_bootstrap_finished - EST mode but missing iep parameter!"); + snprintf(buffer, sizeof(buffer), ERROR_REASON_26); + msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; + } +#endif + else { + // Add short server id to server object + if (m2m_id == -1) { + snprintf(buffer,sizeof(buffer), ERROR_REASON_4); + msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; + } + else { + _server->set_resource_value(M2MServer::ShortServerID, + _security->resource_value_int(M2MSecurity::ShortServerID, m2m_id)); + } + } + + // In ok case send response as a separate response + if (msg_code == COAP_MSG_CODE_RESPONSE_CHANGED) { + send_empty_ack(coap_header, address); + // In error case use piggybacked response + } else { + coap_response = sn_nsdl_build_response(_nsdl_handle, coap_header, msg_code); + if (coap_response) { + sn_nsdl_send_coap_message(_nsdl_handle, address, coap_response); + sn_nsdl_release_allocated_coap_msg_mem(_nsdl_handle, coap_response); + } + + handle_bootstrap_error(buffer, true); + } + + // Send a event which is responsible of sending the final response + if (COAP_MSG_CODE_RESPONSE_CHANGED == msg_code) { + bool success = false; + sn_coap_hdr_s *coap_message = sn_nsdl_build_response(_nsdl_handle, + coap_header, + (sn_coap_msg_code_e)msg_code); + if (coap_message) { + // Switch back to original ep name + memory_free(_endpoint->endpoint_name_ptr); + _endpoint->endpoint_name_ptr = alloc_string_copy((uint8_t*)_endpoint_name.c_str(), _endpoint_name.length()); + if (_endpoint->endpoint_name_ptr) { + _endpoint->endpoint_name_len = _endpoint_name.length(); + nsdl_coap_data_s *nsdl_coap_data = create_coap_event_data(coap_message, + address, + _nsdl_handle, + (sn_coap_msg_code_e)msg_code); + if (nsdl_coap_data) { + success = true; + _event.data.event_type = MBED_CLIENT_NSDLINTERFACE_BS_EVENT; + _event.data.data_ptr = (void*)nsdl_coap_data; + eventOS_event_send_user_allocated(&_event); + } + } + } + + if (!success) { + const char *desc = "memory allocation failed"; + if (strlen(ERROR_REASON_22) + strlen(desc) <= MAX_ALLOWED_ERROR_STRING_LENGTH) { + snprintf(buffer, sizeof(buffer), ERROR_REASON_22, desc); + } + + handle_bootstrap_error(buffer, true); + } + } +#else + (void) coap_header; + (void) address; +#endif +} + +void M2MNsdlInterface::handle_bootstrap_delete(sn_coap_hdr_s *coap_header,sn_nsdl_addr_s *address) +{ + +#ifndef M2M_CLIENT_DISABLE_BOOTSTRAP_FEATURE + char buffer[MAX_ALLOWED_ERROR_STRING_LENGTH]; + memset(buffer,0,sizeof(buffer)); + sn_coap_hdr_s *coap_response = NULL; + uint8_t msg_code = COAP_MSG_CODE_RESPONSE_DELETED; + String object_name = coap_to_string(coap_header->uri_path_ptr, + coap_header->uri_path_len); + tr_info("M2MNsdlInterface::handle_bootstrap_delete - obj %s", object_name.c_str()); + if(!_identity_accepted) { + tr_warn("M2MNsdlInterface::handle_bootstrap_delete - Message received out-of-order - IGNORE"); + return; + } + // Only following paths are accepted, 0, 0/0 + else if (object_name.size() == 2 || object_name.size() > 3) { + if (strlen(ERROR_REASON_21) + object_name.size() <= MAX_ALLOWED_ERROR_STRING_LENGTH) { + snprintf(buffer, sizeof(buffer), ERROR_REASON_21,object_name.c_str()); + } + msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; + } + else if ((object_name.size() == 1 && object_name.compare(0,1,"0") != 0) || + (object_name.size() == 3 && object_name.compare(0,3,"0/0") != 0)) { + if (strlen(ERROR_REASON_21) + object_name.size() <= MAX_ALLOWED_ERROR_STRING_LENGTH) { + snprintf(buffer, sizeof(buffer), ERROR_REASON_21, object_name.c_str()); + } + msg_code = COAP_MSG_CODE_RESPONSE_BAD_REQUEST; + } + + coap_response = sn_nsdl_build_response(_nsdl_handle, + coap_header, + msg_code); + + if(coap_response) { + sn_nsdl_send_coap_message(_nsdl_handle, address, coap_response); + sn_nsdl_release_allocated_coap_msg_mem(_nsdl_handle, coap_response); + if(_security) { + _security->clear_resources(); + } + } + if (!coap_response || COAP_MSG_CODE_RESPONSE_DELETED != msg_code) { + handle_bootstrap_error(buffer, true); + } +#else + (void) coap_header; + (void) address; +#endif +} + +bool M2MNsdlInterface::validate_security_object() +{ + bool valid = false; +#ifndef M2M_CLIENT_DISABLE_BOOTSTRAP_FEATURE + const M2MObjectInstanceList &instances = _security->instances(); + M2MObjectInstanceList::const_iterator it; + it = instances.begin(); + uint16_t instance_id = 0; + for ( ; it != instances.end(); it++ ) { + valid = true; + instance_id = (*it)->instance_id(); + tr_debug("M2MNsdlInterface::validate_security_object - instance %d", instance_id); + String address = _security->resource_value_string(M2MSecurity::M2MServerUri, instance_id); + uint32_t sec_mode = _security->resource_value_int(M2MSecurity::SecurityMode, instance_id); + uint32_t is_bs_server = _security->resource_value_int(M2MSecurity::BootstrapServer, instance_id); + + uint32_t chain_size = 0; + uint32_t server_key_size = 0; + uint32_t pkey_size = 0; + + size_t buffer_size = MAX_CERTIFICATE_SIZE; + uint8_t certificate[MAX_CERTIFICATE_SIZE]; + uint8_t *certificate_ptr = (uint8_t *)&certificate; + + // Read through callback if set + M2MResource *res = _security->get_resource(M2MSecurity::OpenCertificateChain, instance_id); + if (res) { + M2MBase::lwm2m_parameters_s *param = res->get_lwm2m_parameters(); + if (param->read_write_callback_set) { + // Read the chain size + if (_security->resource_value_buffer(M2MSecurity::OpenCertificateChain, certificate_ptr, instance_id, &buffer_size) == 0) { + // Only set size if no error when reading + chain_size = buffer_size; + } + _security->resource_value_buffer(M2MSecurity::CloseCertificateChain, certificate_ptr, instance_id, &buffer_size); + } else { + // Read directly from the resource + if (_security->resource_value_buffer(M2MSecurity::PublicKey, certificate_ptr, instance_id, &buffer_size) == 0) { + // Only set size if no error when reading + chain_size = buffer_size; + } + } + } + + buffer_size = MAX_CERTIFICATE_SIZE; + + if (_security->resource_value_buffer(M2MSecurity::ServerPublicKey, certificate_ptr, instance_id, &buffer_size) == 0) { + // Only set size if no error when reading + server_key_size = buffer_size; + } + + buffer_size = MAX_CERTIFICATE_SIZE; + if (_security->resource_value_buffer(M2MSecurity::Secretkey, certificate_ptr, instance_id, &buffer_size) == 0) { + // Only set size if no error when reading + pkey_size = buffer_size; + } + + tr_info("M2MNsdlInterface::validate_security_object - Server URI /0/0: %s", address.c_str()); + tr_info("M2MNsdlInterface::validate_security_object - is bs server /0/1: %" PRIu32, is_bs_server); + tr_info("M2MNsdlInterface::validate_security_object - Security Mode /0/2: %" PRIu32, sec_mode); + tr_info("M2MNsdlInterface::validate_security_object - Public chain size /0/3: %" PRIu32, chain_size); + tr_info("M2MNsdlInterface::validate_security_object - Server Public key size /0/4: %" PRIu32, server_key_size); + tr_info("M2MNsdlInterface::validate_security_object - Secret key size /0/5: %" PRIu32, pkey_size); + if (address.empty()) { + return false; + } + + switch (sec_mode) { + case M2MSecurity::Certificate: + // Server public key and client private and public keys should be populated + if (!chain_size || !server_key_size || !pkey_size) { + return false; + } + break; +#ifndef MBED_CLIENT_DISABLE_EST_FEATURE + case M2MSecurity::EST: + // Only server public key should be populated for lwm2m, client keys will be generated + if (!is_bs_server && (!server_key_size || chain_size || pkey_size)) { + return false; + } + break; +#endif + case M2MSecurity::NoSecurity: + // Nothing to check for no security + break; + default: + // Security mode not supported + return false; + } + } +#endif + return valid; +} + + +void M2MNsdlInterface::handle_bootstrap_error(const char *reason, bool wait) +{ + tr_error("M2MNsdlInterface::handle_bootstrap_error(%s)",reason); + _identity_accepted = false; + + if (wait) { + _observer.bootstrap_error_wait(reason); + } else { + _observer.bootstrap_error(reason); + } +} + +const String& M2MNsdlInterface::endpoint_name() const +{ + return _endpoint_name; +} + +const String M2MNsdlInterface::internal_endpoint_name() const +{ + String iep; + if (_internal_endpoint_name.length() > 0) { + iep = _internal_endpoint_name; + } + else if (_nsdl_handle->ep_information_ptr->location_ptr) { + // If internal_endpoint_name not set yet, parse it from location path + String temp((const char*)_nsdl_handle->ep_information_ptr->location_ptr, + _nsdl_handle->ep_information_ptr->location_len); + // Get last part of the location path. + // In mbed Cloud environment full path is /rd/accountid/internal_endpoint + int location = temp.find_last_of('/') + 1; + iep.append_raw((const char*)_nsdl_handle->ep_information_ptr->location_ptr + location, + _nsdl_handle->ep_information_ptr->location_len - location); + } + return iep; +} + +void M2MNsdlInterface::change_operation_mode(M2MObject *object, M2MBase::Operation operation) +{ + const M2MObjectInstanceList &instances = object->instances(); + M2MObjectInstanceList::const_iterator inst = instances.begin(); + for (; inst != instances.end(); inst++ ) { + (*inst)->set_operation(operation); + const M2MResourceList &list = (*inst)->resources(); + if(!list.empty()) { + M2MResourceList::const_iterator it; + it = list.begin(); + for ( ; it != list.end(); it++ ) { + (*it)->set_operation(operation); + } + } + } +} + +void M2MNsdlInterface::set_server_address(const char* server_address) +{ + free(_server_address); + _server_address = M2MBase::alloc_string_copy(server_address); +} + +M2MTimer &M2MNsdlInterface::get_nsdl_execution_timer() +{ + return _nsdl_execution_timer; +} + +bool M2MNsdlInterface::is_unregister_ongoing() const +{ + return _nsdl_handle->unregister_token == 0 ? false : true; +} + +bool M2MNsdlInterface::parse_and_send_uri_query_parameters() +{ + bool msg_sent = false; + char *address_copy = M2MBase::alloc_string_copy(_server_address); + if (address_copy) { + const char* query = parse_uri_query_parameters(_server_address); + if (query != NULL) { + size_t query_len = 1 + strlen(query) + 1 + strlen(MCC_VERSION) + 1; + if (_custom_uri_query_params) { + query_len += 1 + strlen(_custom_uri_query_params); + } + + if (query_len <= MAX_URI_QUERY_LEN) { + char query_params[MAX_URI_QUERY_LEN]; + strcpy(query_params, "&"); + strcat(query_params, query); + strcat(query_params, "&"); + strcat(query_params, MCC_VERSION); + if (_custom_uri_query_params) { + strcat(query_params, "&"); + strcat(query_params, _custom_uri_query_params); + } + + tr_debug("M2MNsdlInterface::parse_and_send_uri_query_parameters - uri params: %s", query_params); + msg_sent = sn_nsdl_register_endpoint(_nsdl_handle,_endpoint,query_params) != 0; + } else { + tr_error("M2MNsdlInterface::parse_and_send_uri_query_parameters - max uri param length reached (%lu)", + (unsigned long)query_len); + } + } + free(address_copy); + } + return msg_sent; +} + +void M2MNsdlInterface::claim_mutex() +{ + _connection_handler.claim_mutex(); +} + +void M2MNsdlInterface::release_mutex() +{ + _connection_handler.release_mutex(); +} + +void M2MNsdlInterface::start_nsdl_execution_timer() +{ + tr_debug("M2MNsdlInterface::start_nsdl_execution_timer"); + _nsdl_execution_timer_running = true; + _nsdl_execution_timer.stop_timer(); + _nsdl_execution_timer.start_timer(ONE_SECOND_TIMER * 1000, + M2MTimerObserver::NsdlExecution, + false); +} + +M2MSecurity* M2MNsdlInterface::get_security_object() +{ + return _security; +} + +void M2MNsdlInterface::update_trigger_callback(void */*argument*/) +{ + if (!send_update_registration()) { + // Most likely case would be memory allocation failure + _observer.registration_error(M2MInterface::MemoryFail, false); + } +} + +bool M2MNsdlInterface::lifetime_value_changed() const +{ + uint64_t value = 0; + if (_endpoint && _endpoint->lifetime_ptr) { + value = atol((const char*)_endpoint->lifetime_ptr); + } + if (_server->resource_value_int(M2MServer::Lifetime) != value) { + return true; + } + return false; +} + +void M2MNsdlInterface::execute_notification_delivery_status_cb(M2MBase* object, int32_t msgid) +{ + if (msgid > 0) { + object->send_notification_delivery_status(*object, NOTIFICATION_STATUS_SENT); + object->send_message_delivery_status(*object, + M2MBase::MESSAGE_STATUS_SENT, + M2MBase::NOTIFICATION); + store_to_response_list(object->uri_path(), msgid, M2MBase::NOTIFICATION); + } else { + object->send_notification_delivery_status(*object, NOTIFICATION_STATUS_BUILD_ERROR); + object->send_message_delivery_status(*object, + M2MBase::MESSAGE_STATUS_BUILD_ERROR, + M2MBase::NOTIFICATION); + _notification_send_ongoing = false; + } +} + +uint8_t M2MNsdlInterface::find_auto_obs_token(const char *path, uint8_t *token) const +{ + uint8_t token_len = 0; + const String name(path); + M2MBase *object = find_resource(name); + if (object) { + object->get_observation_token(token, token_len); + } + return token_len; +} + +bool M2MNsdlInterface::is_response_to_request(const sn_coap_hdr_s *coap_header, request_context_s &get_data) +{ + // ns_list_foreach() replacement since it does not compile with IAR 7.x versions. + request_context_s *data = (request_context_s *)ns_list_get_first(&_request_context_list); + while (data) { + if (memcmp(coap_header->token_ptr, &data->msg_token, sizeof(data->msg_token)) == 0) { + get_data = *data; + return true; + } + data = (request_context_s *)ns_list_get_next(&_request_context_list, data); + } + + return false; +} + +void M2MNsdlInterface::free_request_context_list(const sn_coap_hdr_s *coap_header, bool call_error_cb, request_error_t error_code) +{ + // Clean up whole list + if (coap_header == NULL) { + // ns_list_foreach() replacement since it does not compile with IAR 7.x versions. + while (!ns_list_is_empty(&_request_context_list)) { + request_context_s* data = (request_context_s*)ns_list_get_first(&_request_context_list); + if (call_error_cb) { + data->on_request_error_cb(error_code, data->context); + } + ns_list_remove(&_request_context_list, data); + memory_free(data->uri_path); + memory_free(data); + } + + // Clean just one item from the list + } else { + // ns_list_foreach() replacement since it does not compile with IAR 7.x versions. + request_context_s *data = (request_context_s *)ns_list_get_first(&_request_context_list); + while (data) { + if (memcmp(coap_header->token_ptr, &data->msg_token, sizeof(data->msg_token)) == 0) { + if (call_error_cb) { + data->on_request_error_cb(error_code, data->context); + } + ns_list_remove(&_request_context_list, data); + memory_free(data->uri_path); + memory_free(data); + return; + } + data = (request_context_s *)ns_list_get_next(&_request_context_list, data); + } + } +} + +void M2MNsdlInterface::set_request_context_to_be_resend(uint8_t *token, uint8_t token_len) +{ + // ns_list_foreach() replacement since it does not compile with IAR 7.x versions. + request_context_s *data = (request_context_s *)ns_list_get_first(&_request_context_list); + while (data) { + if (token && token_len) { + if (token_len == sizeof(data->msg_token) && + memcmp((uint8_t*)&data->msg_token, token, token_len) == 0) { + data->resend = true; + } + } else { + data->resend = true; + } + data = (request_context_s *)ns_list_get_next(&_request_context_list, data); + } +} + +char* M2MNsdlInterface::parse_uri_query_parameters(char* uri) +{ + char* query = strchr((char*)uri, '?'); + if (query != NULL) { + query++; + if (*query == '\0') { + return NULL; + } else { + return query; + } + } else { + return NULL; + } +} + +bool M2MNsdlInterface::set_uri_query_parameters(const char *uri_query_params) +{ + tr_debug("M2MNsdlInterface::set_uri_query_parameters"); + size_t query_len = uri_query_params == NULL ? 0:strlen(uri_query_params); + size_t current_len = _custom_uri_query_params == NULL ? 0:strlen(_custom_uri_query_params); + size_t new_size = query_len + current_len; + + if (query_len == 0 || + query_len > MAX_ALLOWED_STRING_LENGTH || + new_size > MAX_ALLOWED_STRING_LENGTH) { + tr_error("M2MNsdlInterface::set_uri_query_parameters - invalid params!"); + return false; + } + + // Append into existing string + if (_custom_uri_query_params) { + // Reserve space for "&" and null marks + _custom_uri_query_params = (char*)realloc(_custom_uri_query_params, 1 + new_size + 1); + if (_custom_uri_query_params == NULL) { + return false; + } + + memcpy(_custom_uri_query_params + current_len, "&", 1); + memcpy(_custom_uri_query_params + current_len + 1, uri_query_params, query_len); + _custom_uri_query_params[1 + new_size] = '\0'; + } else { + _custom_uri_query_params = (char*)alloc_string_copy((uint8_t*)uri_query_params, query_len + 1); + if (_custom_uri_query_params == NULL) { + return false; + } + } + + tr_info("M2MNsdlInterface::set_uri_query_parameters - query %s", _custom_uri_query_params); + return true; +} + +void M2MNsdlInterface::clear_sent_blockwise_messages() +{ + sn_nsdl_clear_coap_sent_blockwise_messages(_nsdl_handle); +} + +void M2MNsdlInterface::clear_received_blockwise_messages() +{ + sn_nsdl_clear_coap_received_blockwise_messages(_nsdl_handle); +} + +void M2MNsdlInterface::send_coap_ping() +{ + if (_binding_mode == M2MInterface::TCP && _registered && + _counter_for_nsdl == _next_coap_ping_send_time && + !coap_ping_in_process()) { + + tr_info("M2MNsdlInterface::send_coap_ping()"); + + // Build the CoAP here as the CoAP builder would add the message to re-sending queue. + // Store the id to prevent multiple simultanous ping messages, may happen if ping interval is shorter than total retransmission time. + int32_t message_id = sn_nsdl_send_coap_ping(_nsdl_handle); + if (message_id > 0) { + store_to_response_list(NULL, message_id, M2MBase::PING); + } else { + tr_error("M2MNsdlInterface::send_coap_ping() - failed to create ping message!"); + } + } +} + +void M2MNsdlInterface::calculate_new_coap_ping_send_time() +{ + if (_binding_mode != M2MInterface::TCP) { + return; + } + + _next_coap_ping_send_time = _counter_for_nsdl + MBED_CLIENT_TCP_KEEPALIVE_INTERVAL; +} + +void M2MNsdlInterface::send_next_notification(bool clear_token) +{ + tr_debug("M2MNsdlInterface::send_next_notification"); + claim_mutex(); + if (!_base_list.empty()) { + M2MBaseList::const_iterator base_iterator; + base_iterator = _base_list.begin(); + for ( ; base_iterator != _base_list.end(); base_iterator++ ) { + if ((*base_iterator)->base_type() == M2MBase::Object) { + if (send_next_notification_for_object(*(M2MObject*)*base_iterator, clear_token)) { + release_mutex(); + return; + } + } +#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION + else if ((*base_iterator)->base_type() == M2MBase::ObjectDirectory) { + M2MEndpoint* endpoint = static_cast<M2MEndpoint*> (*base_iterator); + const M2MObjectList& object_list = endpoint->objects(); + if (!object_list.empty()) { + M2MObjectList::const_iterator object_iterator; + object_iterator = object_list.begin(); + // Object level + for ( ; object_iterator != object_list.end(); object_iterator++ ) { + if (send_next_notification_for_object(**object_iterator, clear_token)) { + release_mutex(); + return; + } + } + } + } +#endif + } + } + + _notification_send_ongoing = false; + release_mutex(); + tr_debug("M2MNsdlInterface::send_next_notification - nothing to send"); +} + +bool M2MNsdlInterface::send_next_notification_for_object(M2MObject& object, bool clear_token) { + const M2MObjectInstanceList &object_instance_list = object.instances(); + M2MReportHandler* reporter = object.report_handler(); + if (reporter) { + if (clear_token && !object.get_nsdl_resource()->auto_observable) { + reporter->set_observation_token(NULL, 0); + } else if (reporter->is_under_observation() && + (reporter->notification_in_queue() || reporter->notification_send_in_progress())) { + reporter->schedule_report(true); + return true; + } + } + + // Object instance level + if (!object_instance_list.empty()) { + M2MObjectInstanceList::const_iterator object_instance_iterator; + object_instance_iterator = object_instance_list.begin(); + for ( ; object_instance_iterator != object_instance_list.end(); object_instance_iterator++ ) { + reporter = (*object_instance_iterator)->report_handler(); + if (reporter) { + if (clear_token && !(*object_instance_iterator)->get_nsdl_resource()->auto_observable) { + reporter->set_observation_token(NULL, 0); + } else if (reporter->is_under_observation() && + (reporter->notification_in_queue() || reporter->notification_send_in_progress())) { + reporter->schedule_report(true); + return true; + } + } + + // Resource level + const M2MResourceList &resource_list = (*object_instance_iterator)->resources(); + if (!resource_list.empty()) { + M2MResourceList::const_iterator resource_iterator; + resource_iterator = resource_list.begin(); + for ( ; resource_iterator != resource_list.end(); resource_iterator++) { + reporter = (*resource_iterator)->report_handler(); + if (reporter) { + // Auto obs token can't be cleared + if (clear_token && !(*resource_iterator)->get_nsdl_resource()->auto_observable) { + reporter->set_observation_token(NULL, 0); + } else if (reporter->is_under_observation() && + (reporter->notification_in_queue() || reporter->notification_send_in_progress())) { + reporter->schedule_report(true); + return true; + } + } + } + } + } + } + + return false; +} + +void M2MNsdlInterface::send_empty_ack(const sn_coap_hdr_s *header, sn_nsdl_addr_s *address) +{ + tr_debug("M2MNsdlInterface::send_empty_ack()"); + sn_coap_hdr_s *empty_coap_ack = (sn_coap_hdr_s *) memory_alloc(sizeof(sn_coap_hdr_s)); + if (empty_coap_ack) { + memset(empty_coap_ack, 0, sizeof(sn_coap_hdr_s)); + empty_coap_ack->msg_code = COAP_MSG_CODE_EMPTY; + empty_coap_ack->msg_type = COAP_MSG_TYPE_ACKNOWLEDGEMENT; + empty_coap_ack->msg_id = header->msg_id; + sn_nsdl_send_coap_message(_nsdl_handle, address, empty_coap_ack); + memory_free(empty_coap_ack); + } +} + +void M2MNsdlInterface::store_bs_finished_response_id(uint16_t msg_id) +{ + tr_debug("M2MNsdlInterface::store_bs_finished_response_id - id %d", msg_id); + _bootstrap_id = msg_id; +} + +struct M2MNsdlInterface::nsdl_coap_data_s* M2MNsdlInterface::create_coap_event_data( + sn_coap_hdr_s *received_coap_header, + sn_nsdl_addr_s *address, + struct nsdl_s *nsdl_handle, + uint8_t coap_msg_code) +{ + nsdl_coap_data_s *nsdl_coap_data = (nsdl_coap_data_s*)memory_alloc(sizeof(nsdl_coap_data_s)); + + if (nsdl_coap_data) { + nsdl_coap_data->nsdl_handle = nsdl_handle; + nsdl_coap_data->address.addr_len = address->addr_len; + nsdl_coap_data->address.type = address->type; + nsdl_coap_data->address.port = address->port; + + // Needs to copy all the dynamic data since it resides on stack and this wil turn into an event based call. + nsdl_coap_data->address.addr_ptr = (uint8_t*) memory_alloc(address->addr_len); + + if (nsdl_coap_data->address.addr_ptr) { + memcpy(nsdl_coap_data->address.addr_ptr, address->addr_ptr, address->addr_len); + nsdl_coap_data->received_coap_header = received_coap_header; + nsdl_coap_data->received_coap_header->msg_type = COAP_MSG_TYPE_CONFIRMABLE; + nsdl_coap_data->received_coap_header->msg_code = (sn_coap_msg_code_e)coap_msg_code; + + // Copy payload + if ((received_coap_header->payload_len > 0) && + (received_coap_header->coap_status != COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVED)) { + assert(received_coap_header->payload_ptr); + + uint8_t *temp_ptr = (uint8_t*) memory_alloc(received_coap_header->payload_len); + if (temp_ptr) { + memcpy(temp_ptr, received_coap_header->payload_ptr, received_coap_header->payload_len); + nsdl_coap_data->received_coap_header->payload_ptr = temp_ptr; + nsdl_coap_data->received_coap_header->payload_len = received_coap_header->payload_len; + } else { + memory_free(nsdl_coap_data->received_coap_header->payload_ptr); + sn_coap_parser_release_allocated_coap_msg_mem(nsdl_handle->grs->coap, nsdl_coap_data->received_coap_header); + memory_free(nsdl_coap_data->address.addr_ptr); + memory_free(nsdl_coap_data); + return NULL; + } + } + } else { + memory_free(nsdl_coap_data); + return NULL; + } + } else { + return NULL; + } + + return nsdl_coap_data; +} + +void M2MNsdlInterface::set_registration_status(bool registered) +{ + _registered = registered; + + // Unblock CoAP ping sending by removing ping request from the list. + if (!registered) { + remove_ping_from_response_list(); + } +} + +void M2MNsdlInterface::handle_register_response(const sn_coap_hdr_s *coap_header) +{ + if (coap_header->msg_code == COAP_MSG_CODE_RESPONSE_CREATED) { + tr_info("M2MNsdlInterface::handle_register_response - registered"); + // If lifetime is less than zero then leave the field empty + if (coap_header->options_list_ptr) { + uint32_t max_time = coap_header->options_list_ptr->max_age; + + // If a sufficiently-large Max-Age option is present, we interpret it as registration lifetime; + // mbed server (mDS) reports lifetime this way as a non-standard extension. Other servers + // would likely not include an explicit Max-Age option, in which case we'd see the default 60 seconds. + if (max_time >= MINIMUM_REGISTRATION_TIME) { + set_endpoint_lifetime_buffer(max_time); + } + if (coap_header->options_list_ptr->location_path_ptr) { + sn_nsdl_set_endpoint_location(_nsdl_handle, + coap_header->options_list_ptr->location_path_ptr, + coap_header->options_list_ptr->location_path_len); + } + + } + if (_endpoint->lifetime_ptr) { + _registration_timer.stop_timer(); + _registration_timer.start_timer(registration_time() * 1000, + M2MTimerObserver::Registration, + false); + } + + _observer.client_registered(_server); + + _notification_send_ongoing = false; + + // Check if there are any pending download requests + send_pending_request(); + + } else { + tr_error("M2MNsdlInterface::handle_register_response - registration error %d", coap_header->msg_code); + if (coap_header->coap_status == COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED || + coap_header->coap_status == COAP_STATUS_BUILDER_BLOCK_SENDING_FAILED) { + tr_error("M2MNsdlInterface::handle_register_response - message sending failed !!!!"); + } + + if (COAP_MSG_CODE_RESPONSE_BAD_REQUEST == coap_header->msg_code || + COAP_MSG_CODE_RESPONSE_FORBIDDEN == coap_header->msg_code) { + _observer.registration_error(M2MInterface::InvalidParameters, false); + } else { + // Try to do clean register again + _observer.registration_error(M2MInterface::NetworkError, true, true); + } + } +} + +void M2MNsdlInterface::handle_unregister_response(const sn_coap_hdr_s *coap_header) +{ + tr_info("M2MNsdlInterface::handle_unregister_response - unregistered"); + + // Clear out the ongoing requests and call error callback with status ERROR_NOT_REGISTERED + free_request_context_list(NULL, true, ERROR_NOT_REGISTERED); + + if(coap_header->msg_code == COAP_MSG_CODE_RESPONSE_DELETED) { + _registration_timer.stop_timer(); + _observer.client_unregistered(); + } else { + tr_error("M2MNsdlInterface::handle_unregister_response - unregistration error %d", coap_header->msg_code); + M2MInterface::Error error = M2MInterface::UnregistrationFailed; + if (coap_header->msg_code == COAP_MSG_CODE_RESPONSE_NOT_FOUND) { + _observer.registration_error(error, false); + } else { + _observer.registration_error(error, true); + } + } +} + +void M2MNsdlInterface::handle_register_update_response(const sn_coap_hdr_s *coap_header) +{ + if (coap_header->msg_code == COAP_MSG_CODE_RESPONSE_CHANGED) { + tr_info("M2MNsdlInterface::handle_register_update_response - registration_updated"); + _observer.registration_updated(*_server); + + _notification_send_ongoing = false; + // Check if there are any pending notifications in queue + _notification_handler->send_notification(this); + + // Check if there are any pending download requests + send_pending_request(); + + } else { + tr_error("M2MNsdlInterface::handle_register_update_response - registration_updated failed %d, %d", coap_header->msg_code, coap_header->coap_status); + _nsdl_handle->update_register_token = 0; + _registration_timer.stop_timer(); + + if (coap_header->coap_status == COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED || + coap_header->coap_status == COAP_STATUS_BUILDER_BLOCK_SENDING_FAILED) { + // Inform interfaceimpl to do a reconnection and registration update + // till we get CoAP level response for the request + _observer.registration_error(M2MInterface::NetworkError, true); + } else { + // Do a full registration + bool msg_sent = false; + if (_server_address) { + msg_sent = parse_and_send_uri_query_parameters(); + } + if (!msg_sent) { + sn_nsdl_register_endpoint(_nsdl_handle, _endpoint, NULL); + } + } + } +} + +void M2MNsdlInterface::handle_request_response(const sn_coap_hdr_s *coap_header, + request_context_s *request_context) +{ + tr_info("M2MNsdlInterface::handle_request_response"); + size_t total_size = 0; + + if (coap_header->options_list_ptr) { + if (coap_header->options_list_ptr->use_size2) { + total_size = coap_header->options_list_ptr->size2; + } + } else { + total_size = coap_header->payload_len; + } + + if (coap_header->msg_code >= COAP_MSG_CODE_RESPONSE_CREATED && + coap_header->msg_code <= COAP_MSG_CODE_RESPONSE_CONTENT) { + + // Reset retry timer for next GET request + _download_retry_time = 0; + + // Take copy of uri_path in case of sync mode + // Pointer is freed already by "free_request_context_list" and then used again in send_request() call + char *temp = NULL; + if (!request_context->async_req) { + temp = (char*)alloc_string_copy((uint8_t*)request_context->uri_path, strlen(request_context->uri_path)); + if (temp == NULL) { + free_request_context_list(coap_header, true, FAILED_TO_ALLOCATE_MEMORY); + return; + } + } + + // TODO: clean this up, could we keep request_context in the list a bit longer + // or pass the existing one to send_request rather than copying? + size_t rcv_size = request_context->received_size + coap_header->payload_len; + request_data_cb data_cb = request_context->on_request_data_cb; + request_error_cb error_cb = request_context->on_request_error_cb; + void *ctx = request_context->context; + bool async = request_context->async_req; + sn_coap_msg_code_e msg_code = request_context->msg_code; + uint32_t token = request_context->msg_token; + DownloadType download_type = request_context->download_type; + + // Remove the request before calling the "on_request_data_cb" callback + free_request_context_list(coap_header, false); + + bool last_block = true; + if (coap_header->options_list_ptr && + coap_header->options_list_ptr->block2 != -1 && + coap_header->options_list_ptr->block2 & 0x08) { + // Not last block if block2 is set (blockwised transfer) and more bit is set + last_block = false; + } + + data_cb(coap_header->payload_ptr, + coap_header->payload_len, + total_size, + last_block, + ctx); + + // In sync mode, call next request automatically until all blocks have been received + if (!async) { + if (!last_block) { + // Note that payload will be empty here as it should have already been sent + // when the initial request was sent! + send_request(download_type, temp, msg_code, rcv_size, async, token, 0, NULL, data_cb, error_cb, ctx); + } else { + tr_info("M2MNsdlInterface::handle_request_response - all blocks received"); + } + + memory_free(temp); + } + + } else { + // Retransmission completed + if (coap_header->coap_status == COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED || + coap_header->coap_status == COAP_STATUS_BUILDER_BLOCK_SENDING_FAILED) { + _observer.registration_error(M2MInterface::NetworkError, true); + + // Start retry logic, only for file download operation + } else if (coap_header->msg_code == COAP_MSG_CODE_RESPONSE_SERVICE_UNAVAILABLE && + request_context->msg_code == COAP_MSG_CODE_REQUEST_GET) { + bool retry = true; + + if (!_download_retry_time) { + // Range is from 1 sec to 10 sec + _download_retry_time = randLIB_get_random_in_range(1, 10); + } else { + _download_retry_time *= RECONNECT_INCREMENT_FACTOR; + if (_download_retry_time > MAX_RECONNECT_TIMEOUT) { + tr_error("M2MNsdlInterface::handle_request_response - file download failed, retry completed"); + retry = false; + failed_to_send_request(request_context, coap_header); + } + } + + if (retry) { + tr_info("M2MNsdlInterface::handle_request_response - continue file download after %" PRIu64, _download_retry_time); + set_request_context_to_be_resend(coap_header->token_ptr, coap_header->token_len); + _download_retry_timer.start_timer(_download_retry_time * 1000, M2MTimerObserver::RetryTimer); + } + + // Message sending has failed, inform application + } else { + failed_to_send_request(request_context, coap_header); + } + } +} + +void M2MNsdlInterface::handle_bootstrap_response(const sn_coap_hdr_s *coap_header) +{ +#ifndef MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE + tr_info("M2MNsdlInterface::handle_bootstrap_response"); + _bootstrap_id = 0; + M2MInterface::Error error_code = interface_error(*coap_header); + if (error_code != M2MInterface::ErrorNone) { + +#ifdef DISABLE_ERROR_DESCRIPTION + // this ifdef is saving +800B on ARMCC as it gets rid of the COAP_ERROR_* -strings from binary + const char *buffer = ""; +#else + char buffer[MAX_ALLOWED_ERROR_STRING_LENGTH]; + const char* error = coap_error(*coap_header); + snprintf(buffer, sizeof(buffer), "%s:%.*s", error, coap_header->payload_len, coap_header->payload_ptr); +#endif + handle_bootstrap_error(buffer, false); + } else { + _identity_accepted = true; + } +#else + (void)coap_header; +#endif //MBED_CLIENT_DISABLE_BOOTSTRAP_FEATURE +} + +bool M2MNsdlInterface::handle_post_response(sn_coap_hdr_s* coap_header, + sn_nsdl_addr_s* address, + sn_coap_hdr_s *&coap_response, + M2MObjectInstance *&obj_instance, + bool is_bootstrap_msg) +{ + bool execute_value_updated = false; + + if (is_bootstrap_msg) { + handle_bootstrap_finished(coap_header, address); + } else if (coap_header->uri_path_ptr) { + + String resource_name = coap_to_string(coap_header->uri_path_ptr, + coap_header->uri_path_len); + + String object_name; + int slash_found = resource_name.find_last_of('/'); + //The POST operation here is only allowed for non-existing object instances + if (slash_found != -1) { + object_name = resource_name.substr(0,slash_found); + if (object_name.find_last_of('/') != -1){ + coap_response = sn_nsdl_build_response(_nsdl_handle, + coap_header, + COAP_MSG_CODE_RESPONSE_NOT_FOUND); + } else { + int32_t instance_id = atoi(resource_name.substr(slash_found+1, + resource_name.size()-object_name.size()).c_str()); + M2MBase* base = find_resource(object_name); + if(base) { + if((instance_id >= 0) && (instance_id < UINT16_MAX)) { + if(coap_header->payload_ptr) { + M2MObject* object = static_cast<M2MObject*> (base); + obj_instance = object->create_object_instance(instance_id); + if(obj_instance) { + obj_instance->set_operation(M2MBase::GET_PUT_POST_ALLOWED); + coap_response = obj_instance->handle_post_request(_nsdl_handle, + coap_header, + this, + execute_value_updated); + } + if (coap_response && coap_response->msg_code != COAP_MSG_CODE_RESPONSE_CREATED) { + //Invalid request so remove created ObjectInstance + object->remove_object_instance(instance_id); + } else { + tr_debug("M2MNsdlInterface::handle_post_response - Send Update registration for Create"); + if (!send_update_registration()) { + // Most likely case would be memory allocation failure + _observer.registration_error(M2MInterface::MemoryFail, false); + } + } + } else { + tr_error("M2MNsdlInterface::handle_post_response - Missing Payload - Cannot create"); + coap_response = sn_nsdl_build_response(_nsdl_handle, + coap_header, + COAP_MSG_CODE_RESPONSE_BAD_REQUEST); + } + } else { // instance id out of range + tr_error("M2MNsdlInterface::handle_post_response - instance id out of range - Cannot create"); + coap_response = sn_nsdl_build_response(_nsdl_handle, + coap_header, + COAP_MSG_CODE_RESPONSE_BAD_REQUEST); + } + } else { // if(base) + tr_error("M2MNsdlInterface::handle_post_response - Missing BASE - Cannot create"); + coap_response = sn_nsdl_build_response(_nsdl_handle, + coap_header, + COAP_MSG_CODE_RESPONSE_NOT_FOUND); + } + } + } else { // if(slash_found != -1) + tr_error("M2MNsdlInterface::handle_post_response - slash_found - Cannot create"); + coap_response = sn_nsdl_build_response(_nsdl_handle, + coap_header, + COAP_MSG_CODE_RESPONSE_NOT_FOUND); + } + + } + return execute_value_updated; +} + +void M2MNsdlInterface::handle_empty_ack(const sn_coap_hdr_s *coap_header, bool is_bootstrap_msg) +{ + // Handle reset message + if (COAP_MSG_TYPE_RESET == coap_header->msg_type) { + coap_response_s *resp = find_response(coap_header->msg_id); + if (resp) { + if (resp->type == M2MBase::PING) { + remove_item_from_response_list(resp->uri_path, coap_header->msg_id); + } else { + M2MBase *base = find_resource(resp->uri_path); + if (base) { + if (resp->type == M2MBase::NOTIFICATION) { + M2MBase::BaseType type = base->base_type(); + switch (type) { + case M2MBase::Object: + base->remove_observation_level(M2MBase::O_Attribute); + break; + case M2MBase::Resource: + base->remove_observation_level(M2MBase::R_Attribute); + break; + case M2MBase::ObjectInstance: + base->remove_observation_level(M2MBase::OI_Attribute); + break; + default: + break; + } + base->set_under_observation(false, this); + _notification_send_ongoing = false; + base->send_notification_delivery_status(*base, NOTIFICATION_STATUS_UNSUBSCRIBED); + base->send_message_delivery_status(*base, M2MBase::MESSAGE_STATUS_UNSUBSCRIBED, M2MBase::NOTIFICATION); + _notification_handler->send_notification(this); + } else if (resp->type == M2MBase::DELAYED_POST_RESPONSE) { + base->send_message_delivery_status(*base, M2MBase::MESSAGE_STATUS_REJECTED, M2MBase::DELAYED_POST_RESPONSE); + } +#ifdef ENABLE_ASYNC_REST_RESPONSE + else if (resp->type == M2MBase::DELAYED_RESPONSE) { + base->send_message_delivery_status(*base, M2MBase::MESSAGE_STATUS_REJECTED, M2MBase::DELAYED_RESPONSE); + } +#endif // ENABLE_ASYNC_REST_RESPONSE + remove_item_from_response_list(resp->uri_path, coap_header->msg_id); + } + } + } + } else if (is_bootstrap_msg) { + if (!_bootstrap_finish_ack_received) { + // The _bootstrap_finish_ack_received flag is used to avoid sending the finish event + // twice incase we get the same ack before the event loop has handled the event. + _observer.bootstrap_wait(); + if (coap_header->coap_status == COAP_STATUS_BUILDER_MESSAGE_SENDING_FAILED || + coap_header->coap_status == COAP_STATUS_BUILDER_BLOCK_SENDING_FAILED) { + handle_bootstrap_error(ERROR_REASON_28, false); + } else { + tr_debug("M2MNsdlInterface::handle_empty_ack - sending finish event - status %d", coap_header->coap_status); + _event.data.event_type = MBED_CLIENT_NSDLINTERFACE_BS_FINISH_EVENT; + _event.data.event_data = coap_header->msg_id; + _event.data.data_ptr = _nsdl_handle; + _bootstrap_finish_ack_received = true; + eventOS_event_send_user_allocated(&_event); + } + } + else { + tr_debug("M2MNsdlInterface::handle_empty_ack - finish event already in progress"); + } + } else { + coap_response_s *data = find_response(coap_header->msg_id); + if (data) { + M2MBase *base = find_resource(data->uri_path); + if (base) { + bool report = true; + if (data->type == M2MBase::NOTIFICATION) { + if (base->report_handler()->blockwise_notify()) { + report = false; + } + } + + if (report) { + if (!data->blockwise_used) { + handle_message_delivered(base, data->type); + remove_item_from_response_list(NULL, coap_header->msg_id); + } + } + } + } + } +} + +void M2MNsdlInterface::handle_bootstrap_finish_ack(uint16_t msg_id) +{ + // EMPTY ACK for BS finished + tr_debug("M2MNsdlInterface::handle_bootstrap_finish_ack - id: %d", msg_id); + if (_bootstrap_id == msg_id) { + _observer.bootstrap_finish(); + _bootstrap_id = 0; + } else { + tr_error("M2MNsdlInterface::handle_empty_ack - empty ACK id does not match to BS finished response id!"); + char buffer[MAX_ALLOWED_ERROR_STRING_LENGTH]; + const char *desc = "message id does not match"; + snprintf(buffer, sizeof(buffer), ERROR_REASON_22, desc); + handle_bootstrap_error(buffer, false); + } +} + +void M2MNsdlInterface::handle_message_delivered(M2MBase *base, const M2MBase::MessageType type) +{ + if (M2MBase::NOTIFICATION == type) { + base->report_handler()->set_notification_send_in_progress(false); + _notification_send_ongoing = false; + base->send_notification_delivery_status(*base, NOTIFICATION_STATUS_DELIVERED); + + _notification_handler->send_notification(this); + + // Supported only in Resource level + // TODO! remove below code once old API is removed + if (M2MBase::Resource == base->base_type()) { + M2MResource *resource = static_cast<M2MResource *> (base); + resource->notification_sent(); + } + } + + base->send_message_delivery_status(*base, M2MBase::MESSAGE_STATUS_DELIVERED, type); +} + +void M2MNsdlInterface::set_retransmission_parameters() +{ + // in UDP mode, reconnection attempts must be scaled down so that last attempt does not slip + // past the client lifetime. + uint64_t lifetime = registration_time(); + uint32_t resend_count = MBED_CLIENT_RECONNECTION_COUNT; + + uint32_t reconnection_total_time = total_retransmission_time(resend_count); + tr_debug("M2MNsdlInterface::set_retransmission_parameters() - total resend time %" PRIu32, reconnection_total_time); + + while (resend_count > 1 && reconnection_total_time > lifetime) { + reconnection_total_time = total_retransmission_time(--resend_count); + } + + tr_info("M2MNsdlInterface::set_retransmission_parameters() - setting max resend count to %" PRIu32 " with total time: %" PRIu32, resend_count, reconnection_total_time); + sn_nsdl_set_retransmission_parameters(_nsdl_handle, resend_count, MBED_CLIENT_RECONNECTION_INTERVAL); +} + +uint32_t M2MNsdlInterface::total_retransmission_time(uint32_t resend_count) +{ + uint32_t reconnection_total_time = 1; + + for (uint32_t i = 0; i <= resend_count; i++) { + reconnection_total_time *= 2; + } + + reconnection_total_time--; + reconnection_total_time *= MBED_CLIENT_RECONNECTION_INTERVAL; + + // We need to take into account that CoAP specification mentions that each retransmission + // has to have a random multiplying factor between 1 - 1.5 , max of which can be 1.5 + reconnection_total_time *= RESPONSE_RANDOM_FACTOR; + + return reconnection_total_time; +} + +bool M2MNsdlInterface::is_update_register_ongoing() const +{ + return _nsdl_handle->update_register_token == 0 ? false : true; +} + +uint8_t M2MNsdlInterface::get_resend_count() +{ + return sn_nsdl_get_retransmission_count(_nsdl_handle); +} + +void M2MNsdlInterface::send_pending_request() +{ + // ns_list_foreach() replacement since it does not compile with IAR 7.x versions. + request_context_s *data = (request_context_s *)ns_list_get_first(&_request_context_list); + while (data) { + if (data->resend && data->msg_code == COAP_MSG_CODE_REQUEST_GET) { + send_request(data->download_type, + data->uri_path, + data->msg_code, + data->received_size, + data->async_req, + data->msg_token, + 0, + NULL, + data->on_request_data_cb, + data->on_request_error_cb, + data->context); + } + + data = (request_context_s *)ns_list_get_next(&_request_context_list, data); + } +} + +void M2MNsdlInterface::free_response_list() +{ + // ns_list_foreach() replacement since it does not compile with IAR 7.x versions. + while (!ns_list_is_empty(&_response_list)) { + coap_response_s* data = (coap_response_s*)ns_list_get_first(&_response_list); + ns_list_remove(&_response_list, data); + memory_free(data->uri_path); + memory_free(data); + } +} + +void M2MNsdlInterface::remove_item_from_response_list(const char* uri_path, const int32_t msg_id) +{ + // ns_list_foreach() replacement since it does not compile with IAR 7.x versions. + coap_response_s *data = (coap_response_s *)ns_list_get_first(&_response_list); + while (data) { + if (data->msg_id == msg_id) { + bool remove = true; + if (uri_path) { + remove = false; + if ((strcmp(uri_path, data->uri_path) == 0)) { + remove = true; + } + } + if (remove) { + ns_list_remove(&_response_list, data); + memory_free(data->uri_path); + memory_free(data); + return; + } + } + data = (coap_response_s *)ns_list_get_next(&_response_list, data); + } +} + +#if !defined(DISABLE_DELAYED_RESPONSE) || defined(ENABLE_ASYNC_REST_RESPONSE) +void M2MNsdlInterface::remove_items_from_response_list_for_uri(const char* uri_path) +{ + // ns_list_foreach() replacement since it does not compile with IAR 7.x versions. + coap_response_s *data = (coap_response_s *)ns_list_get_first(&_response_list); + while (data) { + bool remove = false; + if (uri_path) { + if ((strcmp(uri_path, data->uri_path) == 0)) { + remove = true; + } + } + coap_response_s *next = (coap_response_s *)ns_list_get_next(&_response_list, data); + if (remove) { + ns_list_remove(&_response_list, data); + memory_free(data->uri_path); + memory_free(data); + } + data = next; + } +} +#endif + +void M2MNsdlInterface::store_to_response_list(const char *uri, int32_t msg_id, M2MBase::MessageType type) +{ + coap_response_s *resp = (struct coap_response_s*)memory_alloc(sizeof(struct coap_response_s)); + if (resp) { + resp->uri_path = NULL; + if (uri) { + resp->uri_path = M2MBase::alloc_string_copy(uri); + if (resp->uri_path == NULL) { + tr_error("M2MNsdlInterface::store_to_response_list - failed to allocate uri_path!"); + memory_free(resp); + return; + } + } + + resp->msg_id = msg_id; + resp->type = type; + resp->blockwise_used = false; + ns_list_add_to_end(&_response_list, resp); + } else { + tr_error("M2MNsdlInterface::store_to_response_list - failed to allocate coap_response_s!"); + } +} + +struct M2MNsdlInterface::coap_response_s* M2MNsdlInterface::find_response(int32_t msg_id) +{ + coap_response_s *data = (coap_response_s *)ns_list_get_first(&_response_list); + while (data) { + if (data->msg_id == msg_id) { + return data; + } + data = (coap_response_s *)ns_list_get_next(&_response_list, data); + } + + return NULL; +} + +#if !defined(DISABLE_DELAYED_RESPONSE) || defined(ENABLE_ASYNC_REST_RESPONSE) +struct M2MNsdlInterface::coap_response_s* M2MNsdlInterface::find_delayed_response(const char* uri_path, + const M2MBase::MessageType type, + int32_t message_id) +{ + coap_response_s *data = (coap_response_s *)ns_list_get_first(&_response_list); + while (data) { + if (data->uri_path && + strcmp(data->uri_path, uri_path) == 0 && + data->type == type && + ((message_id == UNDEFINED_MSG_ID)) || (data->msg_id == message_id)) { + return data; + } + data = (coap_response_s *)ns_list_get_next(&_response_list, data); + } + + return NULL; +} +#endif // DISABLE_DELAYED_RESPONSE + +void M2MNsdlInterface::failed_to_send_request(request_context_s *request, const sn_coap_hdr_s *coap_header) +{ + sn_nsdl_remove_msg_from_retransmission(_nsdl_handle, + (uint8_t*)&request->msg_token, + sizeof(request->msg_token)); + free_request_context_list(coap_header, true, FAILED_TO_SEND_MSG); +} + +bool M2MNsdlInterface::coap_ping_in_process() const +{ + const coap_response_s *data = (coap_response_s *)ns_list_get_first(&_response_list); + while (data) { + if (data->type == M2MBase::PING) { + tr_info("M2MNsdlInterface::coap_ping_in_process() - already in process"); + return true; + } + data = (coap_response_s *)ns_list_get_next(&_response_list, data); + } + + return false; +} + +void M2MNsdlInterface::remove_ping_from_response_list() +{ + // ns_list_foreach() replacement since it does not compile with IAR 7.x versions. + coap_response_s *data = (coap_response_s *)ns_list_get_first(&_response_list); + while (data) { + if (data->type == M2MBase::PING) { + ns_list_remove(&_response_list, data); + memory_free(data->uri_path); + memory_free(data); + return; + } + data = (coap_response_s *)ns_list_get_next(&_response_list, data); + } +} + +#if !defined(DISABLE_DELAYED_RESPONSE) || defined(ENABLE_ASYNC_REST_RESPONSE) +bool M2MNsdlInterface::handle_delayed_response_store(const char* uri_path, + sn_coap_hdr_s* received_coap, + sn_nsdl_addr_s *address, + const M2MBase::MessageType message_type) +{ + coap_response_s *resp = NULL; + // When running client in Edge, it can store more than one request per resource +#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION + resp = find_delayed_response(uri_path, message_type, received_coap->msg_id); +#else + resp = find_delayed_response(uri_path, message_type); +#endif + bool success = true; + // Only one request can be in process at a time + if (resp) { + sn_coap_hdr_s *coap_response = sn_nsdl_build_response(_nsdl_handle, + received_coap, + COAP_MSG_CODE_RESPONSE_PRECONDITION_FAILED); + if (coap_response) { + sn_nsdl_send_coap_message(_nsdl_handle, address, coap_response); + sn_nsdl_release_allocated_coap_msg_mem(_nsdl_handle, coap_response); + } + + sn_nsdl_release_allocated_coap_msg_mem(_nsdl_handle, received_coap); + success = false; + } else { + // When running client in Edge, it can store more than one request per resource +#ifdef MBED_CLOUD_CLIENT_EDGE_EXTENSION + store_to_response_list(uri_path, received_coap->msg_id, message_type); +#else + store_to_response_list(uri_path, UNDEFINED_MSG_ID, message_type); +#endif + } + + return success; +} +#endif