pick up wakaama files from https://github.com/eclipse/wakaama
Diff: core/registration.c
- Revision:
- 0:1fa43ab66921
diff -r 000000000000 -r 1fa43ab66921 core/registration.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/registration.c Wed Apr 19 11:30:02 2017 +0000 @@ -0,0 +1,1275 @@ +/******************************************************************************* + * + * Copyright (c) 2013, 2014 Intel Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * The Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * David Navarro, Intel Corporation - initial API and implementation + * domedambrosio - Please refer to git log + * Fabien Fleutot - Please refer to git log + * Simon Bernard - Please refer to git log + * Toby Jaffey - Please refer to git log + * Manuel Sangoi - Please refer to git log + * Julien Vermillard - Please refer to git log + * Bosch Software Innovations GmbH - Please refer to git log + * Pascal Rieux - Please refer to git log + * + *******************************************************************************/ + +/* + Copyright (c) 2013, 2014 Intel Corporation + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Intel Corporation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + David Navarro <david.navarro@intel.com> + +*/ + +#include "internals.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define MAX_LOCATION_LENGTH 10 // strlen("/rd/65534") + 1 + +#ifdef LWM2M_CLIENT_MODE + +static int prv_getRegistrationQuery(lwm2m_context_t * contextP, + lwm2m_server_t * server, + char * buffer, + size_t length) +{ + int index; + int res; + + index = utils_stringCopy(buffer, length, QUERY_STARTER QUERY_VERSION_FULL QUERY_DELIMITER QUERY_NAME); + if (index < 0) return 0; + res = utils_stringCopy(buffer + index, length - index, contextP->endpointName); + if (res < 0) return 0; + index += res; + + if (NULL != contextP->msisdn) + { + res = utils_stringCopy(buffer + index, length - index, QUERY_DELIMITER QUERY_SMS); + if (res < 0) return 0; + index += res; + res = utils_stringCopy(buffer + index, length - index, contextP->msisdn); + if (res < 0) return 0; + index += res; + } + + switch (server->binding) + { + case BINDING_U: + res = utils_stringCopy(buffer + index, length - index, "&b=U"); + break; + case BINDING_UQ: + res = utils_stringCopy(buffer + index, length - index, "&b=UQ"); + break; + case BINDING_S: + res = utils_stringCopy(buffer + index, length - index, "&b=S"); + break; + case BINDING_SQ: + res = utils_stringCopy(buffer + index, length - index, "&b=SQ"); + break; + case BINDING_US: + res = utils_stringCopy(buffer + index, length - index, "&b=US"); + break; + case BINDING_UQS: + res = utils_stringCopy(buffer + index, length - index, "&b=UQS"); + break; + default: + res = -1; + } + if (res < 0) return 0; + + return index + res; +} + +static void prv_handleRegistrationReply(lwm2m_transaction_t * transacP, + void * message) +{ + coap_packet_t * packet = (coap_packet_t *)message; + lwm2m_server_t * targetP = (lwm2m_server_t *)(transacP->userData); + + if (targetP->status == STATE_REG_PENDING) + { + time_t tv_sec = lwm2m_gettime(); + if (tv_sec >= 0) + { + targetP->registration = tv_sec; + } + if (packet != NULL && packet->code == COAP_201_CREATED) + { + targetP->status = STATE_REGISTERED; + if (NULL != targetP->location) + { + lwm2m_free(targetP->location); + } + targetP->location = coap_get_multi_option_as_string(packet->location_path); + + LOG("Registration successful"); + } + else + { + targetP->status = STATE_REG_FAILED; + LOG("Registration failed"); + } + } +} + +#define PRV_QUERY_BUFFER_LENGTH 200 + +// send the registration for a single server +static uint8_t prv_register(lwm2m_context_t * contextP, + lwm2m_server_t * server) +{ + char query[200]; + int query_length; + uint8_t payload[512]; + int payload_length; + lwm2m_transaction_t * transaction; + + payload_length = object_getRegisterPayload(contextP, payload, sizeof(payload)); + if (payload_length == 0) return COAP_500_INTERNAL_SERVER_ERROR; + + query_length = prv_getRegistrationQuery(contextP, server, query, sizeof(query)); + + if (query_length == 0) return COAP_500_INTERNAL_SERVER_ERROR; + + if (0 != server->lifetime) + { + int res; + + res = utils_stringCopy(query + query_length, PRV_QUERY_BUFFER_LENGTH - query_length, QUERY_DELIMITER QUERY_LIFETIME); + if (res < 0) return COAP_500_INTERNAL_SERVER_ERROR; + query_length += res; + res = utils_intCopy(query + query_length, PRV_QUERY_BUFFER_LENGTH - query_length, server->lifetime); + if (res < 0) return COAP_500_INTERNAL_SERVER_ERROR; + query_length += res; + } + + if (server->sessionH == NULL) + { + server->sessionH = lwm2m_connect_server(server->secObjInstID, contextP->userData); + } + + if (NULL == server->sessionH) return COAP_503_SERVICE_UNAVAILABLE; + + transaction = transaction_new(server->sessionH, COAP_POST, NULL, NULL, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + coap_set_header_uri_path(transaction->message, "/"URI_REGISTRATION_SEGMENT); + coap_set_header_uri_query(transaction->message, query); + coap_set_header_content_type(transaction->message, LWM2M_CONTENT_LINK); + coap_set_payload(transaction->message, payload, payload_length); + + transaction->callback = prv_handleRegistrationReply; + transaction->userData = (void *) server; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + if (transaction_send(contextP, transaction) != 0) return COAP_500_INTERNAL_SERVER_ERROR; + + server->status = STATE_REG_PENDING; + + return COAP_NO_ERROR; +} + +static void prv_handleRegistrationUpdateReply(lwm2m_transaction_t * transacP, + void * message) +{ + coap_packet_t * packet = (coap_packet_t *)message; + lwm2m_server_t * targetP = (lwm2m_server_t *)(transacP->userData); + + if (targetP->status == STATE_REG_UPDATE_PENDING) + { + time_t tv_sec = lwm2m_gettime(); + if (tv_sec >= 0) + { + targetP->registration = tv_sec; + } + if (packet != NULL && packet->code == COAP_204_CHANGED) + { + targetP->status = STATE_REGISTERED; + LOG("Registration update successful"); + } + else + { + targetP->status = STATE_REG_FAILED; + LOG("Registration update failed"); + } + } +} + +static int prv_updateRegistration(lwm2m_context_t * contextP, + lwm2m_server_t * server, + bool withObjects) +{ + lwm2m_transaction_t * transaction; + uint8_t payload[512]; + int payload_length; + + transaction = transaction_new(server->sessionH, COAP_POST, NULL, NULL, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + + coap_set_header_uri_path(transaction->message, server->location); + + if (withObjects == true) + { + payload_length = object_getRegisterPayload(contextP, payload, sizeof(payload)); + if (payload_length == 0) + { + transaction_free(transaction); + return COAP_500_INTERNAL_SERVER_ERROR; + } + coap_set_payload(transaction->message, payload, payload_length); + } + + transaction->callback = prv_handleRegistrationUpdateReply; + transaction->userData = (void *) server; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + + if (transaction_send(contextP, transaction) == 0) + { + server->status = STATE_REG_UPDATE_PENDING; + } + + return COAP_NO_ERROR; +} + +// update the registration of a given server +int lwm2m_update_registration(lwm2m_context_t * contextP, + uint16_t shortServerID, + bool withObjects) +{ + lwm2m_server_t * targetP; + uint8_t result; + + LOG_ARG("State: %s, shortServerID: %d", STR_STATE(contextP->state), shortServerID); + + result = COAP_NO_ERROR; + + targetP = contextP->serverList; + if (targetP == NULL) + { + if (object_getServers(contextP) == -1) + { + LOG("No server found"); + return COAP_404_NOT_FOUND; + } + } + while (targetP != NULL && result == COAP_NO_ERROR) + { + if (shortServerID != 0) + { + if (targetP->shortID == shortServerID) + { + // found the server, trigger the update transaction + if (targetP->status == STATE_REGISTERED + || targetP->status == STATE_REG_UPDATE_PENDING) + { + if (withObjects == true) + { + targetP->status = STATE_REG_FULL_UPDATE_NEEDED; + } + else + { + targetP->status = STATE_REG_UPDATE_NEEDED; + } + return COAP_NO_ERROR; + } + else if ((targetP->status == STATE_REG_FULL_UPDATE_NEEDED) + || (targetP->status == STATE_REG_UPDATE_NEEDED)) + { + // if REG (FULL) UPDATE is already set, returns COAP_NO_ERROR + if (withObjects == true) + { + targetP->status = STATE_REG_FULL_UPDATE_NEEDED; + } + return COAP_NO_ERROR; + } + else + { + return COAP_400_BAD_REQUEST; + } + } + } + else + { + if (targetP->status == STATE_REGISTERED + || targetP->status == STATE_REG_UPDATE_PENDING) + { + if (withObjects == true) + { + targetP->status = STATE_REG_FULL_UPDATE_NEEDED; + } + else + { + targetP->status = STATE_REG_UPDATE_NEEDED; + } + } + } + targetP = targetP->next; + } + + if (shortServerID != 0 + && targetP == NULL) + { + // no server found + result = COAP_404_NOT_FOUND; + } + + return result; +} + +uint8_t registration_start(lwm2m_context_t * contextP) +{ + lwm2m_server_t * targetP; + uint8_t result; + + LOG_ARG("State: %s", STR_STATE(contextP->state)); + + result = COAP_NO_ERROR; + + targetP = contextP->serverList; + while (targetP != NULL && result == COAP_NO_ERROR) + { + if (targetP->status == STATE_DEREGISTERED + || targetP->status == STATE_REG_FAILED) + { + result = prv_register(contextP, targetP); + } + targetP = targetP->next; + } + + return result; +} + + +/* + * Returns STATE_REG_PENDING if at least one registration is still pending + * Returns STATE_REGISTERED if no registration is pending and there is at least one server the client is registered to + * Returns STATE_REG_FAILED if all registration failed. + */ +lwm2m_status_t registration_getStatus(lwm2m_context_t * contextP) +{ + lwm2m_server_t * targetP; + lwm2m_status_t reg_status; + + LOG_ARG("State: %s", STR_STATE(contextP->state)); + + targetP = contextP->serverList; + reg_status = STATE_REG_FAILED; + + while (targetP != NULL) + { + LOG_ARG("targetP->status: %s", STR_STATUS(targetP->status)); + switch (targetP->status) + { + case STATE_REGISTERED: + case STATE_REG_UPDATE_NEEDED: + case STATE_REG_FULL_UPDATE_NEEDED: + case STATE_REG_UPDATE_PENDING: + if (reg_status == STATE_REG_FAILED) + { + reg_status = STATE_REGISTERED; + } + break; + + case STATE_REG_PENDING: + reg_status = STATE_REG_PENDING; + break; + + case STATE_REG_FAILED: + case STATE_DEREG_PENDING: + case STATE_DEREGISTERED: + default: + break; + } + LOG_ARG("reg_status: %s", STR_STATUS(reg_status)); + + targetP = targetP->next; + } + + return reg_status; +} + +static void prv_handleDeregistrationReply(lwm2m_transaction_t * transacP, + void * message) +{ + lwm2m_server_t * targetP; + + targetP = (lwm2m_server_t *)(transacP->userData); + if (NULL != targetP) + { + if (targetP->status == STATE_DEREG_PENDING) + { + targetP->status = STATE_DEREGISTERED; + } + } +} + +void registration_deregister(lwm2m_context_t * contextP, + lwm2m_server_t * serverP) +{ + lwm2m_transaction_t * transaction; + + LOG_ARG("State: %s, serverP->status: %s", STR_STATE(contextP->state), STR_STATUS(serverP->status)); + + if (serverP->status == STATE_DEREGISTERED + || serverP->status == STATE_REG_PENDING + || serverP->status == STATE_DEREG_PENDING + || serverP->status == STATE_REG_FAILED + || serverP->location == NULL) + { + return; + } + + transaction = transaction_new(serverP->sessionH, COAP_DELETE, NULL, NULL, contextP->nextMID++, 4, NULL); + if (transaction == NULL) return; + + coap_set_header_uri_path(transaction->message, serverP->location); + + transaction->callback = prv_handleDeregistrationReply; + transaction->userData = (void *) contextP; + + contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction); + if (transaction_send(contextP, transaction) == 0) + { + serverP->status = STATE_DEREG_PENDING; + } +} +#endif + +#ifdef LWM2M_SERVER_MODE +static void prv_freeClientObjectList(lwm2m_client_object_t * objects) +{ + while (objects != NULL) + { + lwm2m_client_object_t * objP; + + while (objects->instanceList != NULL) + { + lwm2m_list_t * target; + + target = objects->instanceList; + objects->instanceList = objects->instanceList->next; + lwm2m_free(target); + } + + objP = objects; + objects = objects->next; + lwm2m_free(objP); + } +} + +static int prv_getParameters(multi_option_t * query, + char ** nameP, + uint32_t * lifetimeP, + char ** msisdnP, + lwm2m_binding_t * bindingP, + char ** versionP) +{ + *nameP = NULL; + *lifetimeP = 0; + *msisdnP = NULL; + *bindingP = BINDING_UNKNOWN; + *versionP = NULL; + + while (query != NULL) + { + if (lwm2m_strncmp((char *)query->data, QUERY_NAME, QUERY_NAME_LEN) == 0) + { + if (*nameP != NULL) goto error; + if (query->len == QUERY_NAME_LEN) goto error; + + *nameP = (char *)lwm2m_malloc(query->len - QUERY_NAME_LEN + 1); + if (*nameP != NULL) + { + memcpy(*nameP, query->data + QUERY_NAME_LEN, query->len - QUERY_NAME_LEN); + (*nameP)[query->len - QUERY_NAME_LEN] = 0; + } + } + else if (lwm2m_strncmp((char *)query->data, QUERY_SMS, QUERY_SMS_LEN) == 0) + { + if (*msisdnP != NULL) goto error; + if (query->len == QUERY_SMS_LEN) goto error; + + *msisdnP = (char *)lwm2m_malloc(query->len - QUERY_SMS_LEN + 1); + if (*msisdnP != NULL) + { + memcpy(*msisdnP, query->data + QUERY_SMS_LEN, query->len - QUERY_SMS_LEN); + (*msisdnP)[query->len - QUERY_SMS_LEN] = 0; + } + } + else if (lwm2m_strncmp((char *)query->data, QUERY_LIFETIME, QUERY_LIFETIME_LEN) == 0) + { + int i; + + if (*lifetimeP != 0) goto error; + if (query->len == QUERY_LIFETIME_LEN) goto error; + + for (i = QUERY_LIFETIME_LEN ; i < query->len ; i++) + { + if (query->data[i] < '0' || query->data[i] > '9') goto error; + *lifetimeP = (*lifetimeP * 10) + (query->data[i] - '0'); + } + } + else if (lwm2m_strncmp((char *)query->data, QUERY_VERSION, QUERY_VERSION_LEN) == 0) + { + if (*versionP != NULL) goto error; + if (query->len == QUERY_VERSION_LEN) goto error; + + *versionP = (char *)lwm2m_malloc(query->len - QUERY_VERSION_LEN + 1); + if (*versionP != NULL) + { + memcpy(*versionP, query->data + QUERY_VERSION_LEN, query->len - QUERY_VERSION_LEN); + (*versionP)[query->len - QUERY_VERSION_LEN] = 0; + } + } + else if (lwm2m_strncmp((char *)query->data, QUERY_BINDING, QUERY_BINDING_LEN) == 0) + { + if (*bindingP != BINDING_UNKNOWN) goto error; + if (query->len == QUERY_BINDING_LEN) goto error; + + *bindingP = utils_stringToBinding(query->data + QUERY_BINDING_LEN, query->len - QUERY_BINDING_LEN); + } + query = query->next; + } + + return 0; + +error: + if (*nameP != NULL) lwm2m_free(*nameP); + if (*msisdnP != NULL) lwm2m_free(*msisdnP); + if (*versionP != NULL) lwm2m_free(*versionP); + + return -1; +} + +static uint16_t prv_splitLinkAttribute(uint8_t * data, + uint16_t length, + uint16_t * keyStart, + uint16_t * keyLength, + uint16_t * valueStart, + uint16_t * valueLength) +{ + uint16_t index; + uint16_t end; + + index = 0; + while (index < length && data[index] == ' ') index++; + if (index == length) return 0; + + if (data[index] == REG_ATTR_SEPARATOR) + { + index++; + } + if (index == length) return 0; + + *keyStart = index; + + while (index < length && data[index] != REG_ATTR_EQUALS) index++; + if (index == *keyStart || index == length) return 0; + + *keyLength = index - *keyStart; + + index++; + while (index < length && data[index] == ' ') index++; + if (index == length) return 0; + + *valueStart = index; + + while (index < length && data[index] != REG_ATTR_SEPARATOR) index++; + end = index; + + index--; + while (index > *valueStart && data[index] == ' ') index--; + if (index == *valueStart) return 0; + + *valueLength = index - *valueStart + 1; + + return end; +} + +static int prv_parseLinkAttributes(uint8_t * data, + uint16_t length, + bool * supportJSON, + char ** altPath) +{ + uint16_t index; + uint16_t pathStart; + uint16_t pathLength; + bool isValid; + + isValid = false; + + // Expecting application/link-format (RFC6690) + // leading space were removed before. Remove trailing spaces. + while (length > 0 && data[length-1] == ' ') length--; + + // strip open tag + if (length >= 2 && data[0] == REG_URI_START) + { + data += 1; + length -= 1; + } + else + { + return 0; + } + + pathStart = 0; + index = length - 1; + while (index > 0 && data[index] != REG_URI_END) index--; + // link attributes are required + if (index == 0 || index == length - 1) return 0; + + // If there is a preceding /, remove it + if (data[pathStart] == '/') + { + pathStart += 1; + } + pathLength = index - pathStart; + + index++; + if (index >= length || data[index] != REG_ATTR_SEPARATOR) return 0; + index++; + + while (index < length) + { + uint16_t result; + uint16_t keyStart; + uint16_t keyLength; + uint16_t valueStart; + uint16_t valueLength; + + result = prv_splitLinkAttribute(data + index, length - index, &keyStart, &keyLength, &valueStart, &valueLength); + if (result == 0) return 0; + + if (keyLength == REG_ATTR_TYPE_KEY_LEN + && 0 == lwm2m_strncmp(REG_ATTR_TYPE_KEY, data + index + keyStart, keyLength)) + { + if (isValid == true) return 0; // declared twice + if (valueLength != REG_ATTR_TYPE_VALUE_LEN + || 0 != lwm2m_strncmp(REG_ATTR_TYPE_VALUE, data + index + valueStart, valueLength)) + { + return 0; + } + isValid = true; + } + else if (keyLength == REG_ATTR_CONTENT_KEY_LEN + && 0 == lwm2m_strncmp(REG_ATTR_CONTENT_KEY, data + index + keyStart, keyLength)) + { + if (*supportJSON == true) return 0; // declared twice + if (valueLength == REG_ATTR_CONTENT_JSON_LEN + && 0 == lwm2m_strncmp(REG_ATTR_CONTENT_JSON, data + index + valueStart, valueLength)) + { + *supportJSON = true; + } + else + { + return 0; + } + } + // else ignore this one + + index += result; + } + + if (isValid == false) return 0; + + if (pathLength != 0) + { + *altPath = (char *)lwm2m_malloc(pathLength + 1); + if (*altPath == NULL) return 0; + memcpy(*altPath, data + pathStart, pathLength); + (*altPath)[pathLength] = 0; + } + + return 1; +} + +static int prv_getId(uint8_t * data, + uint16_t length, + uint16_t * objId, + uint16_t * instanceId) +{ + int value; + uint16_t limit; + + // Expecting application/link-format (RFC6690) + // leading space were removed before. Remove trailing spaces. + while (length > 0 && data[length-1] == ' ') length--; + + // strip open and close tags + if (length >= 1 && data[0] == REG_URI_START && data[length-1] == REG_URI_END) + { + data += 1; + length -= 2; + } + else + { + return 0; + } + + // If there is a preceding /, remove it + if (length >= 1 && data[0] == '/') + { + data += 1; + length -= 1; + } + + limit = 0; + while (limit < length && data[limit] != '/') limit++; + value = uri_getNumber(data, limit); + if (value < 0 || value >= LWM2M_MAX_ID) return 0; + *objId = value; + + if (limit < length) + { + limit += 1; + data += limit; + length -= limit; + + if (length > 0) + { + value = uri_getNumber(data, length); + if (value >= 0 && value < LWM2M_MAX_ID) + { + *instanceId = value; + return 2; + } + else + { + return 0; + } + } + } + + return 1; +} + +static lwm2m_client_object_t * prv_decodeRegisterPayload(uint8_t * payload, + uint16_t payloadLength, + bool * supportJSON, + char ** altPath) +{ + uint16_t index; + lwm2m_client_object_t * objList; + bool linkAttrFound; + + *altPath = NULL; + *supportJSON = false; + objList = NULL; + linkAttrFound = false; + index = 0; + + while (index <= payloadLength) + { + uint16_t start; + uint16_t length; + int result; + uint16_t id; + uint16_t instance; + + while (index < payloadLength && payload[index] == ' ') index++; + if (index == payloadLength) break; + + start = index; + while (index < payloadLength && payload[index] != REG_DELIMITER) index++; + length = index - start; + + result = prv_getId(payload + start, length, &id, &instance); + if (result != 0) + { + lwm2m_client_object_t * objectP; + + objectP = (lwm2m_client_object_t *)lwm2m_list_find((lwm2m_list_t *)objList, id); + if (objectP == NULL) + { + objectP = (lwm2m_client_object_t *)lwm2m_malloc(sizeof(lwm2m_client_object_t)); + memset(objectP, 0, sizeof(lwm2m_client_object_t)); + if (objectP == NULL) goto error; + objectP->id = id; + objList = (lwm2m_client_object_t *)LWM2M_LIST_ADD(objList, objectP); + } + if (result == 2) + { + lwm2m_list_t * instanceP; + + instanceP = lwm2m_list_find(objectP->instanceList, instance); + if (instanceP == NULL) + { + instanceP = (lwm2m_list_t *)lwm2m_malloc(sizeof(lwm2m_list_t)); + memset(instanceP, 0, sizeof(lwm2m_list_t)); + instanceP->id = instance; + objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, instanceP); + } + } + } + else if (linkAttrFound == false) + { + result = prv_parseLinkAttributes(payload + start, length, supportJSON, altPath); + if (result == 0) goto error; + + linkAttrFound = true; + } + else goto error; + + index++; + } + + return objList; + +error: + if (*altPath != NULL) + { + lwm2m_free(*altPath); + *altPath = NULL; + } + prv_freeClientObjectList(objList); + + return NULL; +} + +static lwm2m_client_t * prv_getClientByName(lwm2m_context_t * contextP, + char * name) +{ + lwm2m_client_t * targetP; + + targetP = contextP->clientList; + while (targetP != NULL && strcmp(name, targetP->name) != 0) + { + targetP = targetP->next; + } + + return targetP; +} + +void registration_freeClient(lwm2m_client_t * clientP) +{ + LOG("Entering"); + if (clientP->name != NULL) lwm2m_free(clientP->name); + if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn); + if (clientP->altPath != NULL) lwm2m_free(clientP->altPath); + prv_freeClientObjectList(clientP->objectList); + while(clientP->observationList != NULL) + { + lwm2m_observation_t * targetP; + + targetP = clientP->observationList; + clientP->observationList = clientP->observationList->next; + lwm2m_free(targetP); + } + lwm2m_free(clientP); +} + +static int prv_getLocationString(uint16_t id, + char location[MAX_LOCATION_LENGTH]) +{ + int index; + int result; + + memset(location, 0, MAX_LOCATION_LENGTH); + + result = utils_stringCopy(location, MAX_LOCATION_LENGTH, "/"URI_REGISTRATION_SEGMENT"/"); + if (result < 0) return 0; + index = result; + + result = utils_intCopy(location + index, MAX_LOCATION_LENGTH - index, id); + if (result < 0) return 0; + + return index + result; +} + +coap_status_t registration_handleRequest(lwm2m_context_t * contextP, + lwm2m_uri_t * uriP, + void * fromSessionH, + coap_packet_t * message, + coap_packet_t * response) +{ + coap_status_t result; + time_t tv_sec; + + LOG_URI(uriP); + tv_sec = lwm2m_gettime(); + if (tv_sec < 0) return COAP_500_INTERNAL_SERVER_ERROR; + + switch(message->code) + { + case COAP_POST: + { + char * name = NULL; + uint32_t lifetime; + char * msisdn; + char * altPath; + char * version; + lwm2m_binding_t binding; + lwm2m_client_object_t * objects; + bool supportJSON; + lwm2m_client_t * clientP; + char location[MAX_LOCATION_LENGTH]; + + if (0 != prv_getParameters(message->uri_query, &name, &lifetime, &msisdn, &binding, &version)) + { + return COAP_400_BAD_REQUEST; + } + if (message->content_type != LWM2M_CONTENT_LINK + && message->content_type != LWM2M_CONTENT_TEXT) + { + return COAP_400_BAD_REQUEST; + } + + objects = prv_decodeRegisterPayload(message->payload, message->payload_len, &supportJSON, &altPath); + + switch (uriP->flag & LWM2M_URI_MASK_ID) + { + case 0: + // Register operation + // Version is mandatory + if (version == NULL) + { + if (name != NULL) lwm2m_free(name); + if (msisdn != NULL) lwm2m_free(msisdn); + return COAP_400_BAD_REQUEST; + } + // Endpoint client name is mandatory + if (name == NULL) + { + lwm2m_free(version); + if (msisdn != NULL) lwm2m_free(msisdn); + return COAP_400_BAD_REQUEST; + } + // Object list is mandatory + if (objects == NULL) + { + lwm2m_free(version); + lwm2m_free(name); + if (msisdn != NULL) lwm2m_free(msisdn); + return COAP_400_BAD_REQUEST; + } + // version must be 1.0 + if (strlen(version) != LWM2M_VERSION_LEN + || lwm2m_strncmp(version, LWM2M_VERSION, LWM2M_VERSION_LEN)) + { + lwm2m_free(version); + lwm2m_free(name); + if (msisdn != NULL) lwm2m_free(msisdn); + return COAP_412_PRECONDITION_FAILED; + } + + if (lifetime == 0) + { + lifetime = LWM2M_DEFAULT_LIFETIME; + } + + clientP = prv_getClientByName(contextP, name); + if (clientP != NULL) + { + // we reset this registration + lwm2m_free(clientP->name); + if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn); + if (clientP->altPath != NULL) lwm2m_free(clientP->altPath); + prv_freeClientObjectList(clientP->objectList); + clientP->objectList = NULL; + } + else + { + clientP = (lwm2m_client_t *)lwm2m_malloc(sizeof(lwm2m_client_t)); + if (clientP == NULL) + { + lwm2m_free(name); + lwm2m_free(altPath); + if (msisdn != NULL) lwm2m_free(msisdn); + prv_freeClientObjectList(objects); + return COAP_500_INTERNAL_SERVER_ERROR; + } + memset(clientP, 0, sizeof(lwm2m_client_t)); + clientP->internalID = lwm2m_list_newId((lwm2m_list_t *)contextP->clientList); + contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_ADD(contextP->clientList, clientP); + } + clientP->name = name; + clientP->binding = binding; + clientP->msisdn = msisdn; + clientP->altPath = altPath; + clientP->supportJSON = supportJSON; + clientP->lifetime = lifetime; + clientP->endOfLife = tv_sec + lifetime; + clientP->objectList = objects; + clientP->sessionH = fromSessionH; + + if (prv_getLocationString(clientP->internalID, location) == 0) + { + registration_freeClient(clientP); + return COAP_500_INTERNAL_SERVER_ERROR; + } + if (coap_set_header_location_path(response, location) == 0) + { + registration_freeClient(clientP); + return COAP_500_INTERNAL_SERVER_ERROR; + } + + if (contextP->monitorCallback != NULL) + { + contextP->monitorCallback(clientP->internalID, NULL, COAP_201_CREATED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData); + } + result = COAP_201_CREATED; + break; + + case LWM2M_URI_FLAG_OBJECT_ID: + clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, uriP->objectId); + if (clientP == NULL) return COAP_404_NOT_FOUND; + + // Endpoint client name MUST NOT be present + if (name != NULL) + { + lwm2m_free(name); + if (msisdn != NULL) lwm2m_free(msisdn); + return COAP_400_BAD_REQUEST; + } + + if (binding != BINDING_UNKNOWN) + { + clientP->binding = binding; + } + if (msisdn != NULL) + { + if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn); + clientP->msisdn = msisdn; + } + if (lifetime != 0) + { + clientP->lifetime = lifetime; + } + // client IP address, port or MSISDN may have changed + clientP->sessionH = fromSessionH; + + if (objects != NULL) + { + lwm2m_observation_t * observationP; + + // remove observations on object/instance no longer existing + observationP = clientP->observationList; + while (observationP != NULL) + { + lwm2m_client_object_t * objP; + lwm2m_observation_t * nextP; + + nextP = observationP->next; + + objP = (lwm2m_client_object_t *)lwm2m_list_find((lwm2m_list_t *)objects, observationP->uri.objectId); + if (objP == NULL) + { + observationP->callback(clientP->internalID, + &observationP->uri, + COAP_202_DELETED, + LWM2M_CONTENT_TEXT, NULL, 0, + observationP->userData); + observe_remove(observationP); + } + else + { + if ((observationP->uri.flag & LWM2M_URI_FLAG_INSTANCE_ID) != 0) + { + if (lwm2m_list_find((lwm2m_list_t *)objP->instanceList, observationP->uri.instanceId) == NULL) + { + observationP->callback(clientP->internalID, + &observationP->uri, + COAP_202_DELETED, + LWM2M_CONTENT_TEXT, NULL, 0, + observationP->userData); + observe_remove(observationP); + } + } + } + + observationP = nextP; + } + + prv_freeClientObjectList(clientP->objectList); + clientP->objectList = objects; + } + + clientP->endOfLife = tv_sec + clientP->lifetime; + + if (contextP->monitorCallback != NULL) + { + contextP->monitorCallback(clientP->internalID, NULL, COAP_204_CHANGED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData); + } + result = COAP_204_CHANGED; + break; + + default: + return COAP_400_BAD_REQUEST; + } + } + break; + + case COAP_DELETE: + { + lwm2m_client_t * clientP; + + if ((uriP->flag & LWM2M_URI_MASK_ID) != LWM2M_URI_FLAG_OBJECT_ID) return COAP_400_BAD_REQUEST; + + contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_RM(contextP->clientList, uriP->objectId, &clientP); + if (clientP == NULL) return COAP_400_BAD_REQUEST; + if (contextP->monitorCallback != NULL) + { + contextP->monitorCallback(clientP->internalID, NULL, COAP_202_DELETED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData); + } + registration_freeClient(clientP); + result = COAP_202_DELETED; + } + break; + + default: + return COAP_400_BAD_REQUEST; + } + + return result; +} + +void lwm2m_set_monitoring_callback(lwm2m_context_t * contextP, + lwm2m_result_callback_t callback, + void * userData) +{ + LOG("Entering"); + contextP->monitorCallback = callback; + contextP->monitorUserData = userData; +} +#endif + +// for each server update the registration if needed +// for each client check if the registration expired +void registration_step(lwm2m_context_t * contextP, + time_t currentTime, + time_t * timeoutP) +{ +#ifdef LWM2M_CLIENT_MODE + lwm2m_server_t * targetP = contextP->serverList; + + LOG_ARG("State: %s", STR_STATE(contextP->state)); + + targetP = contextP->serverList; + while (targetP != NULL) + { + switch (targetP->status) + { + case STATE_REGISTERED: + { + time_t nextUpdate; + time_t interval; + + nextUpdate = targetP->lifetime; + if (COAP_MAX_TRANSMIT_WAIT < nextUpdate) + { + nextUpdate -= COAP_MAX_TRANSMIT_WAIT; + } + else + { + nextUpdate = nextUpdate >> 1; + } + + interval = targetP->registration + nextUpdate - currentTime; + if (0 >= interval) + { + LOG("Updating registration"); + prv_updateRegistration(contextP, targetP, false); + } + else if (interval < *timeoutP) + { + *timeoutP = interval; + } + } + break; + + case STATE_REG_UPDATE_NEEDED: + prv_updateRegistration(contextP, targetP, false); + break; + + case STATE_REG_FULL_UPDATE_NEEDED: + prv_updateRegistration(contextP, targetP, true); + break; + + case STATE_REG_FAILED: + if (targetP->sessionH != NULL) + { + lwm2m_close_connection(targetP->sessionH, contextP->userData); + targetP->sessionH = NULL; + } + break; + + default: + break; + } + targetP = targetP->next; + } + +#endif +#ifdef LWM2M_SERVER_MODE + lwm2m_client_t * clientP; + + LOG("Entering"); + // monitor clients lifetime + clientP = contextP->clientList; + while (clientP != NULL) + { + lwm2m_client_t * nextP = clientP->next; + + if (clientP->endOfLife <= currentTime) + { + contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_RM(contextP->clientList, clientP->internalID, NULL); + if (contextP->monitorCallback != NULL) + { + contextP->monitorCallback(clientP->internalID, NULL, COAP_202_DELETED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData); + } + registration_freeClient(clientP); + } + else + { + time_t interval; + + interval = clientP->endOfLife - currentTime; + + if (*timeoutP > interval) + { + *timeoutP = interval; + } + } + clientP = nextP; + } +#endif + +} +