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: core/registration.c
- Revision:
- 0:c2dff8cbb91a
diff -r 000000000000 -r c2dff8cbb91a core/registration.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/registration.c Wed Apr 19 11:27:34 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
+
+}
+