pick up wakaama files from https://github.com/eclipse/wakaama

Files at this revision

API Documentation at this revision

Comitter:
terencez
Date:
Wed Apr 19 11:28:00 2017 +0000
Commit message:
Initial commit

Changed in this revision

core/block1.c Show annotated file Show diff for this revision Revisions of this file
core/bootstrap.c Show annotated file Show diff for this revision Revisions of this file
core/data.c Show annotated file Show diff for this revision Revisions of this file
core/discover.c Show annotated file Show diff for this revision Revisions of this file
core/er-coap-13/er-coap-13.c Show annotated file Show diff for this revision Revisions of this file
core/er-coap-13/er-coap-13.h Show annotated file Show diff for this revision Revisions of this file
core/internals.h Show annotated file Show diff for this revision Revisions of this file
core/json.c Show annotated file Show diff for this revision Revisions of this file
core/liblwm2m.c Show annotated file Show diff for this revision Revisions of this file
core/liblwm2m.h Show annotated file Show diff for this revision Revisions of this file
core/list.c Show annotated file Show diff for this revision Revisions of this file
core/management.c Show annotated file Show diff for this revision Revisions of this file
core/objects.c Show annotated file Show diff for this revision Revisions of this file
core/observe.c Show annotated file Show diff for this revision Revisions of this file
core/packet.c Show annotated file Show diff for this revision Revisions of this file
core/registration.c Show annotated file Show diff for this revision Revisions of this file
core/tlv.c Show annotated file Show diff for this revision Revisions of this file
core/transaction.c Show annotated file Show diff for this revision Revisions of this file
core/uri.c Show annotated file Show diff for this revision Revisions of this file
core/utils.c Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 3f48af28ebcd core/block1.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/block1.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2016 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:
+ *    Simon Bernard - initial API and implementation
+ *
+ *******************************************************************************/
+/*
+ Copyright (c) 2016 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.
+*/
+#include "internals.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+// the maximum payload transferred by block1 we accumulate per server
+#define MAX_BLOCK1_SIZE 4096
+
+coap_status_t coap_block1_handler(lwm2m_block1_data_t ** pBlock1Data,
+                                  uint16_t mid,
+                                  uint8_t * buffer,
+                                  size_t length,
+                                  uint16_t blockSize,
+                                  uint32_t blockNum,
+                                  bool blockMore,
+                                  uint8_t ** outputBuffer,
+                                  size_t * outputLength)
+{
+    lwm2m_block1_data_t * block1Data = *pBlock1Data;;
+
+    // manage new block1 transfer
+    if (blockNum == 0)
+    {
+       // we already have block1 data for this server, clear it
+       if (block1Data != NULL)
+       {
+           lwm2m_free(block1Data->block1buffer);
+       }
+       else
+       {
+           block1Data = lwm2m_malloc(sizeof(lwm2m_block1_data_t));
+           *pBlock1Data = block1Data;
+           if (NULL == block1Data) return COAP_500_INTERNAL_SERVER_ERROR;
+       }
+
+       block1Data->block1buffer = lwm2m_malloc(length);
+       block1Data->block1bufferSize = length;
+
+       // write new block in buffer
+       memcpy(block1Data->block1buffer, buffer, length);
+       block1Data->lastmid = mid;
+    }
+    // manage already started block1 transfer
+    else
+    {
+       if (block1Data == NULL)
+       {
+           // we never receive the first block
+           // TODO should we clean block1 data for this server ?
+           return COAP_408_REQ_ENTITY_INCOMPLETE;
+       }
+
+       // If this is a retransmission, we already did that.
+       if (block1Data->lastmid != mid)
+       {
+          uint8_t * oldBuffer = block1Data->block1buffer;
+          size_t oldSize = block1Data->block1bufferSize;
+
+          if (block1Data->block1bufferSize != blockSize * blockNum)
+          {
+              // we don't receive block in right order
+              // TODO should we clean block1 data for this server ?
+              return COAP_408_REQ_ENTITY_INCOMPLETE;
+          }
+
+          // is it too large?
+          if (block1Data->block1bufferSize + length >= MAX_BLOCK1_SIZE) {
+              return COAP_413_ENTITY_TOO_LARGE;
+          }
+          // re-alloc new buffer
+          block1Data->block1bufferSize = oldSize+length;
+          block1Data->block1buffer = lwm2m_malloc(block1Data->block1bufferSize);
+          if (NULL == block1Data->block1buffer) return COAP_500_INTERNAL_SERVER_ERROR;
+          memcpy(block1Data->block1buffer, oldBuffer, oldSize);
+          lwm2m_free(oldBuffer);
+
+          // write new block in buffer
+          memcpy(block1Data->block1buffer + oldSize, buffer, length);
+          block1Data->lastmid = mid;
+       }
+    }
+
+    if (blockMore)
+    {
+        *outputLength = -1;
+        return COAP_231_CONTINUE;
+    }
+    else
+    {
+        // buffer is full, set output parameter
+        // we don't free it to be able to send retransmission
+        *outputLength = block1Data->block1bufferSize;
+        *outputBuffer = block1Data->block1buffer;
+
+        return NO_ERROR;
+    }
+}
+
+void free_block1_buffer(lwm2m_block1_data_t * block1Data)
+{
+    if (block1Data != NULL)
+    {
+        // free block1 buffer
+        lwm2m_free(block1Data->block1buffer);
+        block1Data->block1bufferSize = 0 ;
+
+        // free current element
+        lwm2m_free(block1Data);
+    }
+}
diff -r 000000000000 -r 3f48af28ebcd core/bootstrap.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/bootstrap.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,751 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2015 Sierra Wireless 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:
+ *    Pascal Rieux - Please refer to git log
+ *    Bosch Software Innovations GmbH - Please refer to git log
+ *    David Navarro, Intel Corporation - Please refer to git log
+ *
+ *******************************************************************************/
+
+#include "internals.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef LWM2M_CLIENT_MODE
+#ifdef LWM2M_BOOTSTRAP
+
+#define PRV_QUERY_BUFFER_LENGTH 200
+
+
+static void prv_handleResponse(lwm2m_server_t * bootstrapServer,
+                               coap_packet_t * message)
+{
+    if (COAP_204_CHANGED == message->code)
+    {
+        LOG("Received ACK/2.04, Bootstrap pending, waiting for DEL/PUT from BS server...");
+        bootstrapServer->status = STATE_BS_PENDING;
+    }
+    else
+    {
+        bootstrapServer->status = STATE_BS_FAILING;
+    }
+}
+
+static void prv_handleBootstrapReply(lwm2m_transaction_t * transaction,
+                                     void * message)
+{
+    lwm2m_server_t * bootstrapServer = (lwm2m_server_t *)transaction->userData;
+    coap_packet_t * coapMessage = (coap_packet_t *)message;
+
+    LOG("Entering");
+
+    if (bootstrapServer->status == STATE_BS_INITIATED)
+    {
+        if (NULL != coapMessage && COAP_TYPE_RST != coapMessage->type)
+        {
+            prv_handleResponse(bootstrapServer, coapMessage);
+        }
+        else
+        {
+            bootstrapServer->status = STATE_BS_FAILING;
+        }
+    }
+}
+
+// start a device initiated bootstrap
+static void prv_requestBootstrap(lwm2m_context_t * context,
+                                 lwm2m_server_t * bootstrapServer)
+{
+    char query[PRV_QUERY_BUFFER_LENGTH];
+    int query_length = 0;
+    int res;
+
+    LOG("Entering");
+
+    query_length = utils_stringCopy(query, PRV_QUERY_BUFFER_LENGTH, QUERY_STARTER QUERY_NAME);
+    if (query_length < 0)
+    {
+        bootstrapServer->status = STATE_BS_FAILING;
+        return;
+    }
+    res = utils_stringCopy(query + query_length, PRV_QUERY_BUFFER_LENGTH - query_length, context->endpointName);
+    if (res < 0)
+    {
+        bootstrapServer->status = STATE_BS_FAILING;
+        return;
+    }
+    query_length += res;
+
+    if (bootstrapServer->sessionH == NULL)
+    {
+        bootstrapServer->sessionH = lwm2m_connect_server(bootstrapServer->secObjInstID, context->userData);
+    }
+
+    if (bootstrapServer->sessionH != NULL)
+    {
+        lwm2m_transaction_t * transaction = NULL;
+
+        LOG("Bootstrap server connection opened");
+
+        transaction = transaction_new(bootstrapServer->sessionH, COAP_POST, NULL, NULL, context->nextMID++, 4, NULL);
+        if (transaction == NULL)
+        {
+            bootstrapServer->status = STATE_BS_FAILING;
+            return;
+        }
+
+        coap_set_header_uri_path(transaction->message, "/"URI_BOOTSTRAP_SEGMENT);
+        coap_set_header_uri_query(transaction->message, query);
+        transaction->callback = prv_handleBootstrapReply;
+        transaction->userData = (void *)bootstrapServer;
+        context->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(context->transactionList, transaction);
+        if (transaction_send(context, transaction) == 0)
+        {
+            LOG("CI bootstrap requested to BS server");
+            bootstrapServer->status = STATE_BS_INITIATED;
+        }
+    }
+    else
+    {
+        LOG("Connecting bootstrap server failed");
+        bootstrapServer->status = STATE_BS_FAILED;
+    }
+}
+
+void bootstrap_step(lwm2m_context_t * contextP,
+                    uint32_t currentTime,
+                    time_t * timeoutP)
+{
+    lwm2m_server_t * targetP;
+
+    LOG("entering");
+    targetP = contextP->bootstrapServerList;
+    while (targetP != NULL)
+    {
+        LOG_ARG("Initial status: %s", STR_STATUS(targetP->status));
+        switch (targetP->status)
+        {
+        case STATE_DEREGISTERED:
+            targetP->registration = currentTime + targetP->lifetime;
+            targetP->status = STATE_BS_HOLD_OFF;
+            if (*timeoutP > targetP->lifetime)
+            {
+                *timeoutP = targetP->lifetime;
+            }
+            break;
+
+        case STATE_BS_HOLD_OFF:
+            if (targetP->registration <= currentTime)
+            {
+                prv_requestBootstrap(contextP, targetP);
+            }
+            else if (*timeoutP > targetP->registration - currentTime)
+            {
+                *timeoutP = targetP->registration - currentTime;
+            }
+            break;
+
+        case STATE_BS_INITIATED:
+            // waiting
+            break;
+
+        case STATE_BS_PENDING:
+            // waiting
+            break;
+
+        case STATE_BS_FINISHING:
+            if (targetP->sessionH != NULL)
+            {
+                lwm2m_close_connection(targetP->sessionH, contextP->userData);
+                targetP->sessionH = NULL;
+            }
+            targetP->status = STATE_BS_FINISHED;
+            *timeoutP = 0;
+            break;
+
+        case STATE_BS_FAILING:
+            if (targetP->sessionH != NULL)
+            {
+                lwm2m_close_connection(targetP->sessionH, contextP->userData);
+                targetP->sessionH = NULL;
+            }
+            targetP->status = STATE_BS_FAILED;
+            *timeoutP = 0;
+            break;
+
+        default:
+            break;
+        }
+        LOG_ARG("Finalal status: %s", STR_STATUS(targetP->status));
+        targetP = targetP->next;
+    }
+}
+
+coap_status_t bootstrap_handleFinish(lwm2m_context_t * context,
+                                     void * fromSessionH)
+{
+    lwm2m_server_t * bootstrapServer;
+
+    LOG("Entering");
+    bootstrapServer = utils_findBootstrapServer(context, fromSessionH);
+    if (bootstrapServer != NULL
+     && bootstrapServer->status == STATE_BS_PENDING)
+    {
+        LOG("Bootstrap server status changed to STATE_BS_FINISHING");
+        bootstrapServer->status = STATE_BS_FINISHING;
+        return COAP_204_CHANGED;
+    }
+
+    return COAP_IGNORE;
+}
+
+/*
+ * Reset the bootstrap servers statuses
+ *
+ * TODO: handle LWM2M Servers the client is registered to ?
+ *
+ */
+void bootstrap_start(lwm2m_context_t * contextP)
+{
+    lwm2m_server_t * targetP;
+
+    LOG("Entering");
+    targetP = contextP->bootstrapServerList;
+    while (targetP != NULL)
+    {
+        targetP->status = STATE_DEREGISTERED;
+        if (targetP->sessionH == NULL)
+        {
+            targetP->sessionH = lwm2m_connect_server(targetP->secObjInstID, contextP->userData);
+        }
+        targetP = targetP->next;
+    }
+}
+
+/*
+ * Returns STATE_BS_PENDING if at least one bootstrap is still pending
+ * Returns STATE_BS_FINISHED if at least one bootstrap succeeded and no bootstrap is pending
+ * Returns STATE_BS_FAILED if all bootstrap failed.
+ */
+lwm2m_status_t bootstrap_getStatus(lwm2m_context_t * contextP)
+{
+    lwm2m_server_t * targetP;
+    lwm2m_status_t bs_status;
+
+    LOG("Entering");
+    targetP = contextP->bootstrapServerList;
+    bs_status = STATE_BS_FAILED;
+
+    while (targetP != NULL)
+    {
+        switch (targetP->status)
+        {
+            case STATE_BS_FINISHED:
+                if (bs_status == STATE_BS_FAILED)
+                {
+                    bs_status = STATE_BS_FINISHED;
+                }
+                break;
+
+            case STATE_BS_HOLD_OFF:
+            case STATE_BS_INITIATED:
+            case STATE_BS_PENDING:
+            case STATE_BS_FINISHING:
+                bs_status = STATE_BS_PENDING;
+                break;
+
+            default:
+                break;
+        }
+        targetP = targetP->next;
+    }
+
+    LOG_ARG("Returned status: %s", STR_STATUS(bs_status));
+
+    return bs_status;
+}
+
+static coap_status_t prv_checkServerStatus(lwm2m_server_t * serverP)
+{
+    LOG_ARG("Initial status: %s", STR_STATUS(serverP->status));
+
+    switch (serverP->status)
+    {
+    case STATE_BS_HOLD_OFF:
+        serverP->status = STATE_BS_PENDING;
+        LOG_ARG("Status changed to: %s", STR_STATUS(serverP->status));
+        break;
+
+    case STATE_BS_INITIATED:
+        // The ACK was probably lost
+        serverP->status = STATE_BS_PENDING;
+        LOG_ARG("Status changed to: %s", STR_STATUS(serverP->status));
+        break;
+
+    case STATE_DEREGISTERED:
+        // server initiated bootstrap
+    case STATE_BS_PENDING:
+        // do nothing
+        break;
+
+    case STATE_BS_FINISHED:
+    case STATE_BS_FINISHING:
+    case STATE_BS_FAILING:
+    case STATE_BS_FAILED:
+    default:
+        LOG("Returning COAP_IGNORE");
+        return COAP_IGNORE;
+    }
+
+    return COAP_NO_ERROR;
+}
+
+static void prv_tagServer(lwm2m_context_t * contextP,
+                          uint16_t id)
+{
+    lwm2m_server_t * targetP;
+
+    targetP = (lwm2m_server_t *)LWM2M_LIST_FIND(contextP->bootstrapServerList, id);
+    if (targetP == NULL)
+    {
+        targetP = (lwm2m_server_t *)LWM2M_LIST_FIND(contextP->serverList, id);
+    }
+    if (targetP != NULL)
+    {
+        targetP->dirty = true;
+    }
+}
+
+static void prv_tagAllServer(lwm2m_context_t * contextP,
+                             lwm2m_server_t * serverP)
+{
+    lwm2m_server_t * targetP;
+
+    targetP = contextP->bootstrapServerList;
+    while (targetP != NULL)
+    {
+        if (targetP != serverP)
+        {
+            targetP->dirty = true;
+        }
+        targetP = targetP->next;
+    }
+    targetP = contextP->serverList;
+    while (targetP != NULL)
+    {
+        targetP->dirty = true;
+        targetP = targetP->next;
+    }
+}
+
+coap_status_t bootstrap_handleCommand(lwm2m_context_t * contextP,
+                                      lwm2m_uri_t * uriP,
+                                      lwm2m_server_t * serverP,
+                                      coap_packet_t * message,
+                                      coap_packet_t * response)
+{
+    coap_status_t result;
+    lwm2m_media_type_t format;
+
+    LOG_ARG("Code: %02X", message->code);
+    LOG_URI(uriP);
+    format = utils_convertMediaType(message->content_type);
+
+    result = prv_checkServerStatus(serverP);
+    if (result != COAP_NO_ERROR) return result;
+
+    switch (message->code)
+    {
+    case COAP_PUT:
+        {
+            if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+            {
+                if (object_isInstanceNew(contextP, uriP->objectId, uriP->instanceId))
+                {
+                    result = object_create(contextP, uriP, format, message->payload, message->payload_len);
+                    if (COAP_201_CREATED == result)
+                    {
+                        result = COAP_204_CHANGED;
+                    }
+                }
+                else
+                {
+                    result = object_write(contextP, uriP, format, message->payload, message->payload_len);
+                    if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID
+                     && result == COAP_204_CHANGED)
+                    {
+                        prv_tagServer(contextP, uriP->instanceId);
+                    }
+                }
+            }
+            else
+            {
+                lwm2m_data_t * dataP = NULL;
+                int size = 0;
+                int i;
+
+                if (message->payload_len == 0 || message->payload == 0)
+                {
+                    result = COAP_400_BAD_REQUEST;
+                }
+                else
+                {
+                    size = lwm2m_data_parse(uriP, message->payload, message->payload_len, format, &dataP);
+                    if (size == 0)
+                    {
+                        result = COAP_500_INTERNAL_SERVER_ERROR;
+                        break;
+                    }
+
+                    for (i = 0 ; i < size ; i++)
+                    {
+                        if(dataP[i].type == LWM2M_TYPE_OBJECT_INSTANCE)
+                        {
+                            if (object_isInstanceNew(contextP, uriP->objectId, dataP[i].id))
+                            {
+                                result = object_createInstance(contextP, uriP, &dataP[i]);
+                                if (COAP_201_CREATED == result)
+                                {
+                                    result = COAP_204_CHANGED;
+                                }
+                            }
+                            else
+                            {
+                                result = object_writeInstance(contextP, uriP, &dataP[i]);
+                                if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID
+                                 && result == COAP_204_CHANGED)
+                                {
+                                    prv_tagServer(contextP, dataP[i].id);
+                                }
+                            }
+                            
+                            if(result != COAP_204_CHANGED) // Stop object create or write when result is error
+                            {
+                                break;
+                            }
+                        }
+                        else
+                        {
+                            result = COAP_400_BAD_REQUEST;
+                        }
+                    }
+                    lwm2m_data_free(size, dataP);
+                }
+            }
+        }
+        break;
+
+    case COAP_DELETE:
+        {
+            if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+            {
+                result = COAP_400_BAD_REQUEST;
+            }
+            else
+            {
+                result = object_delete(contextP, uriP);
+                if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID
+                 && result == COAP_202_DELETED)
+                {
+                    if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+                    {
+                        prv_tagServer(contextP, uriP->instanceId);
+                    }
+                    else
+                    {
+                        prv_tagAllServer(contextP, NULL);
+                    }
+                }
+            }
+        }
+        break;
+
+    case COAP_GET:
+    case COAP_POST:
+    default:
+        result = COAP_400_BAD_REQUEST;
+        break;
+    }
+
+    if (result == COAP_202_DELETED
+     || result == COAP_204_CHANGED)
+    {
+        if (serverP->status != STATE_BS_PENDING)
+        {
+            serverP->status = STATE_BS_PENDING;
+            contextP->state = STATE_BOOTSTRAPPING;
+        }
+    }
+    LOG_ARG("Server status: %s", STR_STATUS(serverP->status));
+
+    return result;
+}
+
+coap_status_t bootstrap_handleDeleteAll(lwm2m_context_t * contextP,
+                                        void * fromSessionH)
+{
+    lwm2m_server_t * serverP;
+    coap_status_t result;
+    lwm2m_object_t * objectP;
+
+    LOG("Entering");
+    serverP = utils_findBootstrapServer(contextP, fromSessionH);
+    if (serverP == NULL) return COAP_IGNORE;
+    result = prv_checkServerStatus(serverP);
+    if (result != COAP_NO_ERROR) return result;
+
+    result = COAP_202_DELETED;
+    for (objectP = contextP->objectList; objectP != NULL; objectP = objectP->next)
+    {
+        lwm2m_uri_t uri;
+
+        memset(&uri, 0, sizeof(lwm2m_uri_t));
+        uri.flag = LWM2M_URI_FLAG_OBJECT_ID;
+        uri.objectId = objectP->objID;
+
+        if (objectP->objID == LWM2M_SECURITY_OBJECT_ID)
+        {
+            lwm2m_list_t * instanceP;
+
+            instanceP = objectP->instanceList;
+            while (NULL != instanceP
+                && result == COAP_202_DELETED)
+            {
+                if (instanceP->id == serverP->secObjInstID)
+                {
+                    instanceP = instanceP->next;
+                }
+                else
+                {
+                    uri.flag = LWM2M_URI_FLAG_OBJECT_ID | LWM2M_URI_FLAG_INSTANCE_ID;
+                    uri.instanceId = instanceP->id;
+                    result = object_delete(contextP, &uri);
+                    instanceP = objectP->instanceList;
+                }
+            }
+            if (result == COAP_202_DELETED)
+            {
+                prv_tagAllServer(contextP, serverP);
+            }
+        }
+        else
+        {
+            result = object_delete(contextP, &uri);
+            if (result == COAP_405_METHOD_NOT_ALLOWED)
+            {
+                // Fake a successful deletion for static objects like the Device object.
+                result = COAP_202_DELETED;
+            }
+        }
+    }
+
+    return result;
+}
+#endif
+#endif
+
+#ifdef LWM2M_BOOTSTRAP_SERVER_MODE
+uint8_t bootstrap_handleRequest(lwm2m_context_t * contextP,
+                                lwm2m_uri_t * uriP,
+                                void * fromSessionH,
+                                coap_packet_t * message,
+                                coap_packet_t * response)
+{
+    uint8_t result;
+    char * name;
+
+    LOG_URI(uriP);
+    if (contextP->bootstrapCallback == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+    if (message->code != COAP_POST) return COAP_400_BAD_REQUEST;
+    if (message->uri_query == NULL) return COAP_400_BAD_REQUEST;
+    if (message->payload != NULL) return COAP_400_BAD_REQUEST;
+
+    if (lwm2m_strncmp((char *)message->uri_query->data, QUERY_NAME, QUERY_NAME_LEN) != 0)
+    {
+        return COAP_400_BAD_REQUEST;
+    }
+
+    if (message->uri_query->len == QUERY_NAME_LEN) return COAP_400_BAD_REQUEST;
+    if (message->uri_query->next != NULL) return COAP_400_BAD_REQUEST;
+
+    name = (char *)lwm2m_malloc(message->uri_query->len - QUERY_NAME_LEN + 1);
+    if (name == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    memcpy(name, message->uri_query->data + QUERY_NAME_LEN, message->uri_query->len - QUERY_NAME_LEN);
+    name[message->uri_query->len - QUERY_NAME_LEN] = 0;
+
+    result = contextP->bootstrapCallback(fromSessionH, COAP_NO_ERROR, NULL, name, contextP->bootstrapUserData);
+
+    lwm2m_free(name);
+
+    return result;
+}
+
+void lwm2m_set_bootstrap_callback(lwm2m_context_t * contextP,
+                                  lwm2m_bootstrap_callback_t callback,
+                                  void * userData)
+{
+    LOG("Entering");
+    contextP->bootstrapCallback = callback;
+    contextP->bootstrapUserData = userData;
+}
+
+static void prv_resultCallback(lwm2m_transaction_t * transacP,
+                               void * message)
+{
+    bs_data_t * dataP = (bs_data_t *)transacP->userData;
+    lwm2m_uri_t * uriP;
+
+    if (dataP->isUri == true)
+    {
+        uriP = &dataP->uri;
+    }
+    else
+    {
+        uriP = NULL;
+    }
+
+    if (message == NULL)
+    {
+        dataP->callback(transacP->peerH,
+                        COAP_503_SERVICE_UNAVAILABLE,
+                        uriP,
+                        NULL,
+                        dataP->userData);
+    }
+    else
+    {
+        coap_packet_t * packet = (coap_packet_t *)message;
+
+        dataP->callback(transacP->peerH,
+                        packet->code,
+                        uriP,
+                        NULL,
+                        dataP->userData);
+    }
+    lwm2m_free(dataP);
+}
+
+int lwm2m_bootstrap_delete(lwm2m_context_t * contextP,
+                           void * sessionH,
+                           lwm2m_uri_t * uriP)
+{
+    lwm2m_transaction_t * transaction;
+    bs_data_t * dataP;
+
+    LOG_URI(uriP);
+    transaction = transaction_new(sessionH, COAP_DELETE, NULL, uriP, contextP->nextMID++, 4, NULL);
+    if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t));
+    if (dataP == NULL)
+    {
+        transaction_free(transaction);
+        return COAP_500_INTERNAL_SERVER_ERROR;
+    }
+    if (uriP == NULL)
+    {
+        dataP->isUri = false;
+    }
+    else
+    {
+        dataP->isUri = true;
+        memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t));
+    }
+    dataP->callback = contextP->bootstrapCallback;
+    dataP->userData = contextP->bootstrapUserData;
+
+    transaction->callback = prv_resultCallback;
+    transaction->userData = (void *)dataP;
+
+    contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
+
+    return transaction_send(contextP, transaction);
+}
+
+int lwm2m_bootstrap_write(lwm2m_context_t * contextP,
+                          void * sessionH,
+                          lwm2m_uri_t * uriP,
+                          lwm2m_media_type_t format,
+                          uint8_t * buffer,
+                          size_t length)
+{
+    lwm2m_transaction_t * transaction;
+    bs_data_t * dataP;
+
+    LOG_URI(uriP);
+    if (uriP == NULL
+    || buffer == NULL
+    || length == 0)
+    {
+        return COAP_400_BAD_REQUEST;
+    }
+
+    transaction = transaction_new(sessionH, COAP_PUT, NULL, uriP, contextP->nextMID++, 4, NULL);
+    if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    coap_set_header_content_type(transaction->message, format);
+    coap_set_payload(transaction->message, buffer, length);
+
+    dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t));
+    if (dataP == NULL)
+    {
+        transaction_free(transaction);
+        return COAP_500_INTERNAL_SERVER_ERROR;
+    }
+    dataP->isUri = true;
+    memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t));
+    dataP->callback = contextP->bootstrapCallback;
+    dataP->userData = contextP->bootstrapUserData;
+
+    transaction->callback = prv_resultCallback;
+    transaction->userData = (void *)dataP;
+
+    contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
+
+    return transaction_send(contextP, transaction);
+}
+
+int lwm2m_bootstrap_finish(lwm2m_context_t * contextP,
+                           void * sessionH)
+{
+    lwm2m_transaction_t * transaction;
+    bs_data_t * dataP;
+
+    LOG("Entering");
+    transaction = transaction_new(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_BOOTSTRAP_SEGMENT);
+
+    dataP = (bs_data_t *)lwm2m_malloc(sizeof(bs_data_t));
+    if (dataP == NULL)
+    {
+        transaction_free(transaction);
+        return COAP_500_INTERNAL_SERVER_ERROR;
+    }
+    dataP->isUri = false;
+    dataP->callback = contextP->bootstrapCallback;
+    dataP->userData = contextP->bootstrapUserData;
+
+    transaction->callback = prv_resultCallback;
+    transaction->userData = (void *)dataP;
+
+    contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
+
+    return transaction_send(contextP, transaction);
+}
+
+#endif
diff -r 000000000000 -r 3f48af28ebcd core/data.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/data.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,545 @@
+/*******************************************************************************
+*
+* 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
+*    Fabien Fleutot - Please refer to git log
+*    Bosch Software Innovations GmbH - Please refer to git log
+*
+*******************************************************************************/
+
+#include "internals.h"
+#include <float.h>
+
+// dataP array length is assumed to be 1.
+static int prv_textSerialize(lwm2m_data_t * dataP,
+                             uint8_t ** bufferP)
+{
+    size_t res;
+
+    switch (dataP->type)
+    {
+    case LWM2M_TYPE_STRING:
+        *bufferP = (uint8_t *)lwm2m_malloc(dataP->value.asBuffer.length);
+        if (*bufferP == NULL) return 0;
+        memcpy(*bufferP, dataP->value.asBuffer.buffer, dataP->value.asBuffer.length);
+        return (int)dataP->value.asBuffer.length;
+
+    case LWM2M_TYPE_INTEGER:
+        res = utils_int64ToPlainText(dataP->value.asInteger, bufferP);
+        if (res == 0) return -1;
+        return (int)res;
+
+    case LWM2M_TYPE_FLOAT:
+        res = utils_float64ToPlainText(dataP->value.asFloat, bufferP);
+        if (res == 0) return -1;
+        return (int)res;
+
+    case LWM2M_TYPE_BOOLEAN:
+        res = utils_boolToPlainText(dataP->value.asBoolean, bufferP);
+        if (res == 0) return -1;
+        return (int)res;
+
+    case LWM2M_TYPE_OBJECT_LINK:
+    {
+        char stringBuffer[20];
+        int len = snprintf(stringBuffer, 20, "%d:%d",
+                dataP->value.asObjLink.objectId,
+                dataP->value.asObjLink.objectInstanceId);
+        *bufferP = (uint8_t *)lwm2m_malloc(len);
+        if (*bufferP == NULL) return -1;
+        memcpy(*bufferP, stringBuffer, len);
+        return len;
+    }
+    case LWM2M_TYPE_OPAQUE:
+    case LWM2M_TYPE_UNDEFINED:
+    default:
+        return -1;
+    }
+}
+
+static int prv_setBuffer(lwm2m_data_t * dataP,
+                         uint8_t * buffer,
+                         size_t bufferLen)
+{
+    dataP->value.asBuffer.buffer = (uint8_t *)lwm2m_malloc(bufferLen);
+    if (dataP->value.asBuffer.buffer == NULL)
+    {
+        lwm2m_data_free(1, dataP);
+        return 0;
+    }
+    dataP->value.asBuffer.length = bufferLen;
+    memcpy(dataP->value.asBuffer.buffer, buffer, bufferLen);
+
+    return 1;
+}
+
+lwm2m_data_t * lwm2m_data_new(int size)
+{
+    lwm2m_data_t * dataP;
+
+    LOG_ARG("size: %d", size);
+    if (size <= 0) return NULL;
+
+    dataP = (lwm2m_data_t *)lwm2m_malloc(size * sizeof(lwm2m_data_t));
+
+    if (dataP != NULL)
+    {
+        memset(dataP, 0, size * sizeof(lwm2m_data_t));
+    }
+
+    return dataP;
+}
+
+void lwm2m_data_free(int size,
+                     lwm2m_data_t * dataP)
+{
+    int i;
+
+    LOG_ARG("size: %d", size);
+    if (size == 0 || dataP == NULL) return;
+
+    for (i = 0; i < size; i++)
+    {
+        switch (dataP[i].type)
+        {
+        case LWM2M_TYPE_MULTIPLE_RESOURCE:
+        case LWM2M_TYPE_OBJECT_INSTANCE:
+        case LWM2M_TYPE_OBJECT:
+            lwm2m_data_free(dataP[i].value.asChildren.count, dataP[i].value.asChildren.array);
+            break;
+
+        case LWM2M_TYPE_STRING:
+        case LWM2M_TYPE_OPAQUE:
+            if (dataP[i].value.asBuffer.buffer != NULL)
+            {
+                lwm2m_free(dataP[i].value.asBuffer.buffer);
+            }
+
+        default:
+            // do nothing
+            break;
+        }
+    }
+    lwm2m_free(dataP);
+}
+
+void lwm2m_data_encode_string(const char * string,
+                              lwm2m_data_t * dataP)
+{
+    size_t len;
+    int res;
+
+    LOG_ARG("\"%s\"", string);
+    if (string == NULL)
+    {
+        len = 0;
+    }
+    else
+    {
+        for (len = 0; string[len] != 0; len++);
+    }
+
+    if (len == 0)
+    {
+        dataP->value.asBuffer.length = 0;
+        dataP->value.asBuffer.buffer = NULL;
+        res = 1;
+    }
+    else
+    {
+        res = prv_setBuffer(dataP, (uint8_t *)string, len);
+    }
+
+    if (res == 1)
+    {
+        dataP->type = LWM2M_TYPE_STRING;
+    }
+    else
+    {
+        dataP->type = LWM2M_TYPE_UNDEFINED;
+    }
+}
+
+void lwm2m_data_encode_opaque(uint8_t * buffer,
+                              size_t length,
+                              lwm2m_data_t * dataP)
+{
+    int res;
+
+    LOG_ARG("length: %d", length);
+    if (length == 0)
+    {
+        dataP->value.asBuffer.length = 0;
+        dataP->value.asBuffer.buffer = NULL;
+        res = 1;
+    }
+    else
+    {
+        res = prv_setBuffer(dataP, buffer, length);
+    }
+
+    if (res == 1)
+    {
+        dataP->type = LWM2M_TYPE_OPAQUE;
+    }
+    else
+    {
+        dataP->type = LWM2M_TYPE_UNDEFINED;
+    }
+}
+
+void lwm2m_data_encode_nstring(const char * string,
+                               size_t length,
+                               lwm2m_data_t * dataP)
+{
+    LOG_ARG("length: %d, string: \"%s\"", length, string);
+    lwm2m_data_encode_opaque((uint8_t *)string, length, dataP);
+
+    if (dataP->type == LWM2M_TYPE_OPAQUE)
+    {
+        dataP->type = LWM2M_TYPE_STRING;
+    }
+}
+
+void lwm2m_data_encode_int(int64_t value,
+                           lwm2m_data_t * dataP)
+{
+    LOG_ARG("value: %" PRId64 "", value);
+    dataP->type = LWM2M_TYPE_INTEGER;
+    dataP->value.asInteger = value;
+}
+
+int lwm2m_data_decode_int(const lwm2m_data_t * dataP,
+                          int64_t * valueP)
+{
+    int result;
+
+    LOG("Entering");
+    switch (dataP->type)
+    {
+    case LWM2M_TYPE_INTEGER:
+        *valueP = dataP->value.asInteger;
+        result = 1;
+        break;
+
+    case LWM2M_TYPE_STRING:
+        result = utils_plainTextToInt64(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, valueP);
+        break;
+
+    case LWM2M_TYPE_OPAQUE:
+        result = utils_opaqueToInt(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, valueP);
+        if (result == (int)dataP->value.asBuffer.length)
+        {
+            result = 1;
+        }
+        else
+        {
+            result = 0;
+        }
+        break;
+
+    default:
+        return 0;
+    }
+    LOG_ARG("result: %d, value: %" PRId64, result, *valueP);
+
+    return result;
+}
+
+void lwm2m_data_encode_float(double value,
+                             lwm2m_data_t * dataP)
+{
+    LOG_ARG("value: %f", value);
+    dataP->type = LWM2M_TYPE_FLOAT;
+    dataP->value.asFloat = value;
+}
+
+int lwm2m_data_decode_float(const lwm2m_data_t * dataP,
+                            double * valueP)
+{
+    int result;
+
+    LOG("Entering");
+    switch (dataP->type)
+    {
+    case LWM2M_TYPE_FLOAT:
+        *valueP = dataP->value.asFloat;
+        result = 1;
+        break;
+
+    case LWM2M_TYPE_INTEGER:
+        *valueP = (double)dataP->value.asInteger;
+        result = 1;
+        break;
+
+    case LWM2M_TYPE_STRING:
+        result = utils_plainTextToFloat64(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, valueP);
+        break;
+
+    case LWM2M_TYPE_OPAQUE:
+        result = utils_opaqueToFloat(dataP->value.asBuffer.buffer, dataP->value.asBuffer.length, valueP);
+        if (result == (int)dataP->value.asBuffer.length)
+        {
+            result = 1;
+        }
+        else
+        {
+            result = 0;
+        }
+        break;
+
+    default:
+        return 0;
+    }
+
+    LOG_ARG("result: %d, value: %f", result, *valueP);
+
+    return result;
+}
+
+void lwm2m_data_encode_bool(bool value,
+                            lwm2m_data_t * dataP)
+{
+    LOG_ARG("value: %s", value?"true":"false");
+    dataP->type = LWM2M_TYPE_BOOLEAN;
+    dataP->value.asBoolean = value;
+}
+
+int lwm2m_data_decode_bool(const lwm2m_data_t * dataP,
+                           bool * valueP)
+{
+    int result;
+
+    LOG("Entering");
+    switch (dataP->type)
+    {
+    case LWM2M_TYPE_BOOLEAN:
+        *valueP = dataP->value.asBoolean;
+        result = 1;
+        break;
+
+    case LWM2M_TYPE_STRING:
+        if (dataP->value.asBuffer.length != 1) return 0;
+
+        switch (dataP->value.asBuffer.buffer[0])
+        {
+        case '0':
+            *valueP = false;
+            result = 1;
+            break;
+        case '1':
+            *valueP = true;
+            result = 1;
+            break;
+        default:
+            result = 0;
+            break;
+        }
+        break;
+
+    case LWM2M_TYPE_OPAQUE:
+        if (dataP->value.asBuffer.length != 1) return 0;
+
+        switch (dataP->value.asBuffer.buffer[0])
+        {
+        case 0:
+            *valueP = false;
+            result = 1;
+            break;
+        case 1:
+            *valueP = true;
+            result = 1;
+            break;
+        default:
+            result = 0;
+            break;
+        }
+        break;
+
+    default:
+        result = 0;
+        break;
+    }
+
+    LOG_ARG("result: %d, value: %s", result, *valueP ? "true" : "false");
+
+    return result;
+}
+
+void lwm2m_data_encode_objlink(uint16_t objectId,
+                           uint16_t objectInstanceId,
+                           lwm2m_data_t * dataP)
+{
+    LOG_ARG("value: %d/%d", objectId, objectInstanceId);
+    dataP->type = LWM2M_TYPE_OBJECT_LINK;
+    dataP->value.asObjLink.objectId = objectId;
+    dataP->value.asObjLink.objectInstanceId = objectInstanceId;
+}
+
+void lwm2m_data_include(lwm2m_data_t * subDataP,
+                        size_t count,
+                        lwm2m_data_t * dataP)
+{
+    LOG_ARG("count: %d", count);
+    if (subDataP == NULL || count == 0) return;
+
+    switch (subDataP[0].type)
+    {
+    case LWM2M_TYPE_STRING:
+    case LWM2M_TYPE_OPAQUE:
+    case LWM2M_TYPE_INTEGER:
+    case LWM2M_TYPE_FLOAT:
+    case LWM2M_TYPE_BOOLEAN:
+    case LWM2M_TYPE_OBJECT_LINK:
+    case LWM2M_TYPE_MULTIPLE_RESOURCE:
+        dataP->type = LWM2M_TYPE_OBJECT_INSTANCE;
+        break;
+    case LWM2M_TYPE_OBJECT_INSTANCE:
+        dataP->type = LWM2M_TYPE_OBJECT;
+        break;
+    default:
+        return;
+    }
+    dataP->value.asChildren.count = count;
+    dataP->value.asChildren.array = subDataP;
+}
+
+void lwm2m_data_encode_instances(lwm2m_data_t * subDataP,
+                                 size_t count,
+                                 lwm2m_data_t * dataP)
+{
+    LOG_ARG("count: %d", count);
+    lwm2m_data_include(subDataP, count, dataP);
+    dataP->type = LWM2M_TYPE_MULTIPLE_RESOURCE;
+}
+
+int lwm2m_data_parse(lwm2m_uri_t * uriP,
+                     uint8_t * buffer,
+                     size_t bufferLen,
+                     lwm2m_media_type_t format,
+                     lwm2m_data_t ** dataP)
+{
+    LOG_ARG("format: %s, bufferLen: %d", STR_MEDIA_TYPE(format), bufferLen);
+    LOG_URI(uriP);
+    switch (format)
+    {
+    case LWM2M_CONTENT_TEXT:
+        if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return 0;
+        *dataP = lwm2m_data_new(1);
+        if (*dataP == NULL) return 0;
+        (*dataP)->id = uriP->resourceId;
+        (*dataP)->type = LWM2M_TYPE_STRING;
+        return prv_setBuffer(*dataP, buffer, bufferLen);
+
+    case LWM2M_CONTENT_OPAQUE:
+        if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return 0;
+        *dataP = lwm2m_data_new(1);
+        if (*dataP == NULL) return 0;
+        (*dataP)->id = uriP->resourceId;
+        (*dataP)->type = LWM2M_TYPE_OPAQUE;
+        return prv_setBuffer(*dataP, buffer, bufferLen);
+
+#ifdef LWM2M_OLD_CONTENT_FORMAT_SUPPORT
+    case LWM2M_CONTENT_TLV_OLD:
+#endif
+    case LWM2M_CONTENT_TLV:
+        return tlv_parse(buffer, bufferLen, dataP);
+
+#ifdef LWM2M_SUPPORT_JSON
+#ifdef LWM2M_OLD_CONTENT_FORMAT_SUPPORT
+    case LWM2M_CONTENT_JSON_OLD:
+#endif
+    case LWM2M_CONTENT_JSON:
+        return json_parse(uriP, buffer, bufferLen, dataP);
+#endif
+
+    default:
+        return 0;
+    }
+}
+
+int lwm2m_data_serialize(lwm2m_uri_t * uriP,
+                         int size,
+                         lwm2m_data_t * dataP,
+                         lwm2m_media_type_t * formatP,
+                         uint8_t ** bufferP)
+{
+    LOG_URI(uriP);
+    LOG_ARG("size: %d, formatP: %s", size, STR_MEDIA_TYPE(*formatP));
+
+    // Check format
+    if (*formatP == LWM2M_CONTENT_TEXT
+     || *formatP == LWM2M_CONTENT_OPAQUE)
+    {
+        if (size != 1
+         || (uriP != NULL && !LWM2M_URI_IS_SET_RESOURCE(uriP))
+         || dataP->type == LWM2M_TYPE_OBJECT
+         || dataP->type == LWM2M_TYPE_OBJECT_INSTANCE
+         || dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE)
+        {
+#ifdef LWM2M_SUPPORT_JSON
+            *formatP = LWM2M_CONTENT_JSON;
+#else
+            *formatP = LWM2M_CONTENT_TLV;
+#endif
+        }
+    }
+
+    if (*formatP == LWM2M_CONTENT_TEXT
+     && dataP->type == LWM2M_TYPE_OPAQUE)
+    {
+        *formatP = LWM2M_CONTENT_OPAQUE;
+    }
+    LOG_ARG("Final format: %s", STR_MEDIA_TYPE(*formatP));
+
+    switch (*formatP)
+    {
+    case LWM2M_CONTENT_TEXT:
+        return prv_textSerialize(dataP, bufferP);
+
+    case LWM2M_CONTENT_OPAQUE:
+        *bufferP = (uint8_t *)lwm2m_malloc(dataP->value.asBuffer.length);
+        if (*bufferP == NULL) return -1;
+        memcpy(*bufferP, dataP->value.asBuffer.buffer, dataP->value.asBuffer.length);
+        return (int)dataP->value.asBuffer.length;
+
+    case LWM2M_CONTENT_TLV:
+        {
+            bool isResourceInstance;
+
+            if (uriP != NULL && LWM2M_URI_IS_SET_RESOURCE(uriP)
+             && (size != 1 || dataP->id != uriP->resourceId))
+            {
+                isResourceInstance = true;
+            }
+            else
+            {
+                isResourceInstance = false;
+            }
+            return tlv_serialize(isResourceInstance, size, dataP, bufferP);
+        }
+
+#ifdef LWM2M_CLIENT_MODE
+    case LWM2M_CONTENT_LINK:
+        return discover_serialize(NULL, uriP, NULL, size, dataP, bufferP);
+#endif
+#ifdef LWM2M_SUPPORT_JSON
+    case LWM2M_CONTENT_JSON:
+        return json_serialize(uriP, size, dataP, bufferP);
+#endif
+
+    default:
+        return -1;
+    }
+}
+
diff -r 000000000000 -r 3f48af28ebcd core/discover.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/discover.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,442 @@
+/*******************************************************************************
+*
+* Copyright (c) 2015 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
+*
+*******************************************************************************/
+
+
+#include "internals.h"
+
+#define PRV_LINK_BUFFER_SIZE  1024
+
+
+#define PRV_CONCAT_STR(buf, len, index, str, str_len)    \
+    {                                                    \
+        if ((len)-(index) < (str_len)) return -1;        \
+        memcpy((buf)+(index), (str), (str_len));         \
+        (index) += (str_len);                            \
+    }
+
+
+#ifdef LWM2M_CLIENT_MODE
+
+static lwm2m_attributes_t * prv_findAttributes(lwm2m_context_t * contextP,
+                                               lwm2m_uri_t * uriP,
+                                               lwm2m_server_t * serverP)
+{
+    lwm2m_observed_t * observedP;
+    lwm2m_watcher_t * watcherP;
+    lwm2m_attributes_t * paramP;
+
+    paramP = NULL;
+
+    if (contextP == NULL) return NULL;
+    if (serverP == NULL) return NULL;
+
+    observedP = observe_findByUri(contextP, uriP);
+    if (observedP == NULL || observedP->watcherList == NULL) return NULL;
+
+    for (watcherP = observedP->watcherList; watcherP != NULL; watcherP = watcherP->next)
+    {
+        if (watcherP->server == serverP)
+        {
+            paramP = watcherP->parameters;
+        }
+    }
+
+    return paramP;
+}
+
+static int prv_serializeAttributes(lwm2m_context_t * contextP,
+                                   lwm2m_uri_t * uriP,
+                                   lwm2m_server_t * serverP,
+                                   lwm2m_attributes_t * objectParamP,
+                                   uint8_t * buffer,
+                                   size_t uriLen,
+                                   size_t bufferLen)
+{
+    int head;
+    int res;
+    lwm2m_attributes_t * paramP;
+
+    head = 0;
+
+    paramP = prv_findAttributes(contextP, uriP, serverP);
+    if (paramP == NULL) paramP = objectParamP;
+
+    if (paramP != NULL)
+    {
+        head = uriLen;
+
+        if (paramP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD)
+        {
+            PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE);
+            PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN);
+
+            res = utils_intToText(paramP->minPeriod, buffer + head, bufferLen - head);
+            if (res <= 0) return -1;
+            head += res;
+        }
+        else if (objectParamP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD)
+        {
+            PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE);
+            PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN);
+
+            res = utils_intToText(objectParamP->minPeriod, buffer + head, bufferLen - head);
+            if (res <= 0) return -1;
+            head += res;
+        }
+
+        if (paramP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD)
+        {
+            PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE);
+            PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN);
+
+            res = utils_intToText(paramP->maxPeriod, buffer + head, bufferLen - head);
+            if (res <= 0) return -1;
+            head += res;
+        }
+        else if (objectParamP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD)
+        {
+            PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE);
+            PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN);
+
+            res = utils_intToText(objectParamP->maxPeriod, buffer + head, bufferLen - head);
+            if (res <= 0) return -1;
+            head += res;
+        }
+
+        if (paramP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN)
+        {
+            PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE);
+            PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN);
+
+            res = utils_floatToText(paramP->greaterThan, buffer + head, bufferLen - head);
+            if (res <= 0) return -1;
+            head += res;
+        }
+        if (paramP->toSet & LWM2M_ATTR_FLAG_LESS_THAN)
+        {
+            PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE);
+            PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN);
+
+            res = utils_floatToText(paramP->lessThan, buffer + head, bufferLen - head);
+            if (res <= 0) return -1;
+            head += res;
+        }
+        if (paramP->toSet & LWM2M_ATTR_FLAG_STEP)
+        {
+            PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ATTR_SEPARATOR, LINK_ATTR_SEPARATOR_SIZE);
+            PRV_CONCAT_STR(buffer, bufferLen, head, ATTR_STEP_STR, ATTR_STEP_LEN);
+
+            res = utils_floatToText(paramP->step, buffer + head, bufferLen - head);
+            if (res <= 0) return -1;
+            head += res;
+        }
+        PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ITEM_ATTR_END, LINK_ITEM_ATTR_END_SIZE);
+    }
+
+    if (head > 0) head -= uriLen + 1;
+
+    return head;
+}
+
+static int prv_serializeLinkData(lwm2m_context_t * contextP,
+                                 lwm2m_data_t * tlvP,
+                                 lwm2m_server_t * serverP,
+                                 lwm2m_attributes_t * objectParamP,
+                                 lwm2m_uri_t * parentUriP,
+                                 uint8_t * parentUriStr,
+                                 size_t parentUriLen,
+                                 uint8_t * buffer,
+                                 size_t bufferLen)
+{
+    int head;
+    int res;
+    lwm2m_uri_t uri;
+
+    head = 0;
+
+    switch (tlvP->type)
+    {
+    case LWM2M_TYPE_UNDEFINED:
+    case LWM2M_TYPE_STRING:
+    case LWM2M_TYPE_OPAQUE:
+    case LWM2M_TYPE_INTEGER:
+    case LWM2M_TYPE_FLOAT:
+    case LWM2M_TYPE_BOOLEAN:
+    case LWM2M_TYPE_OBJECT_LINK:
+    case LWM2M_TYPE_MULTIPLE_RESOURCE:
+        if (bufferLen < LINK_ITEM_START_SIZE) return -1;
+        memcpy(buffer + head, LINK_ITEM_START, LINK_ITEM_START_SIZE);
+        head = LINK_ITEM_START_SIZE;
+
+        if (parentUriLen > 0)
+        {
+            if (bufferLen - head < parentUriLen) return -1;
+            memcpy(buffer + head, parentUriStr, parentUriLen);
+            head += parentUriLen;
+        }
+
+        if (bufferLen - head < LINK_URI_SEPARATOR_SIZE) return -1;
+        memcpy(buffer + head, LINK_URI_SEPARATOR, LINK_URI_SEPARATOR_SIZE);
+        head += LINK_URI_SEPARATOR_SIZE;
+
+        res = utils_intToText(tlvP->id, buffer + head, bufferLen - head);
+        if (res <= 0) return -1;
+        head += res;
+
+        if (tlvP->type == LWM2M_TYPE_MULTIPLE_RESOURCE)
+        {
+            if (bufferLen - head < LINK_ITEM_DIM_START_SIZE) return -1;
+            memcpy(buffer + head, LINK_ITEM_DIM_START, LINK_ITEM_DIM_START_SIZE);
+            head += LINK_ITEM_DIM_START_SIZE;
+
+            res = utils_intToText(tlvP->value.asChildren.count, buffer + head, bufferLen - head);
+            if (res <= 0) return -1;
+            head += res;
+
+            if (bufferLen - head < LINK_ITEM_ATTR_END_SIZE) return -1;
+            memcpy(buffer + head, LINK_ITEM_ATTR_END, LINK_ITEM_ATTR_END_SIZE);
+            head += LINK_ITEM_ATTR_END_SIZE;
+        }
+        else
+        {
+            if (bufferLen - head < LINK_ITEM_END_SIZE) return -1;
+            memcpy(buffer + head, LINK_ITEM_END, LINK_ITEM_END_SIZE);
+            head += LINK_ITEM_END_SIZE;
+        }
+
+        if (serverP != NULL)
+        {
+            memcpy(&uri, parentUriP, sizeof(lwm2m_uri_t));
+            uri.resourceId = tlvP->id;
+            uri.flag |= LWM2M_URI_FLAG_RESOURCE_ID;
+            res = prv_serializeAttributes(contextP, &uri, serverP, objectParamP, buffer, head - 1, bufferLen);
+            if (res < 0) return -1;    // careful, 0 is valid
+            if (res > 0) head += res;
+        }
+        break;
+
+    case LWM2M_TYPE_OBJECT_INSTANCE:
+    {
+        uint8_t uriStr[URI_MAX_STRING_LEN];
+        size_t uriLen;
+        size_t index;
+
+        if (parentUriLen > 0)
+        {
+            if (URI_MAX_STRING_LEN < parentUriLen) return -1;
+            memcpy(uriStr, parentUriStr, parentUriLen);
+            uriLen = parentUriLen;
+        }
+        else
+        {
+            uriLen = 0;
+        }
+
+        if (URI_MAX_STRING_LEN - uriLen < LINK_URI_SEPARATOR_SIZE) return -1;
+        memcpy(uriStr + uriLen, LINK_URI_SEPARATOR, LINK_URI_SEPARATOR_SIZE);
+        uriLen += LINK_URI_SEPARATOR_SIZE;
+
+        res = utils_intToText(tlvP->id, uriStr + uriLen, URI_MAX_STRING_LEN - uriLen);
+        if (res <= 0) return -1;
+        uriLen += res;
+
+        memcpy(&uri, parentUriP, sizeof(lwm2m_uri_t));
+        uri.instanceId = tlvP->id;
+        uri.flag |= LWM2M_URI_FLAG_INSTANCE_ID;
+
+        head = 0;
+        PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ITEM_START, LINK_ITEM_START_SIZE);
+        PRV_CONCAT_STR(buffer, bufferLen, head, uriStr, uriLen);
+        PRV_CONCAT_STR(buffer, bufferLen, head, LINK_ITEM_END, LINK_ITEM_END_SIZE);
+        if (serverP != NULL)
+        {
+            res = prv_serializeAttributes(contextP, &uri, serverP, NULL, buffer, head - 1, bufferLen);
+            if (res < 0) return -1;    // careful, 0 is valid
+            if (res == 0) head = 0;    // rewind
+            else head += res;
+        }
+        for (index = 0; index < tlvP->value.asChildren.count; index++)
+        {
+            res = prv_serializeLinkData(contextP, tlvP->value.asChildren.array + index, serverP, objectParamP, &uri, uriStr, uriLen, buffer + head, bufferLen - head);
+            if (res < 0) return -1;
+            head += res;
+        }
+    }
+    break;
+
+    case LWM2M_TYPE_OBJECT:
+    default:
+        return -1;
+    }
+
+    return head;
+}
+
+int discover_serialize(lwm2m_context_t * contextP,
+                       lwm2m_uri_t * uriP,
+                       lwm2m_server_t * serverP,
+                       int size,
+                       lwm2m_data_t * dataP,
+                       uint8_t ** bufferP)
+{
+    uint8_t bufferLink[PRV_LINK_BUFFER_SIZE];
+    uint8_t baseUriStr[URI_MAX_STRING_LEN];
+    int baseUriLen;
+    int index;
+    size_t head;
+    int res;
+    lwm2m_uri_t parentUri;
+    lwm2m_attributes_t * paramP;
+    lwm2m_attributes_t mergedParam;
+
+    LOG_ARG("size: %d", size);
+    LOG_URI(uriP);
+
+    head = 0;
+    memset(&parentUri, 0, sizeof(lwm2m_uri_t));
+    parentUri.objectId = uriP->objectId;
+    parentUri.flag = LWM2M_URI_FLAG_OBJECT_ID;
+
+    if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+    {
+        lwm2m_uri_t tempUri;
+        lwm2m_attributes_t * objParamP;
+        lwm2m_attributes_t * instParamP;
+
+        memset(&parentUri, 0, sizeof(lwm2m_uri_t));
+        tempUri.objectId = uriP->objectId;
+        tempUri.flag = LWM2M_URI_FLAG_OBJECT_ID;
+
+        // get object level attributes
+        objParamP = prv_findAttributes(contextP, &tempUri, serverP);
+        
+        // get object instance level attributes
+        tempUri.instanceId = uriP->instanceId;
+        tempUri.flag = LWM2M_URI_FLAG_INSTANCE_ID;
+        instParamP = prv_findAttributes(contextP, &tempUri, serverP);
+
+        if (objParamP != NULL)
+        {
+            if (instParamP != NULL)
+            {
+                memset(&mergedParam, 0, sizeof(lwm2m_attributes_t));
+                mergedParam.toSet = objParamP->toSet | instParamP->toSet;
+                if (mergedParam.toSet & LWM2M_ATTR_FLAG_MIN_PERIOD)
+                {
+                    if (instParamP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD)
+                    {
+                        mergedParam.minPeriod = instParamP->minPeriod;
+                    }
+                    else
+                    {
+                        mergedParam.minPeriod = objParamP->minPeriod;
+                    }
+                }
+                if (mergedParam.toSet & LWM2M_ATTR_FLAG_MAX_PERIOD)
+                {
+                    if (instParamP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD)
+                    {
+                        mergedParam.maxPeriod = instParamP->maxPeriod;
+                    }
+                    else
+                    {
+                        mergedParam.maxPeriod = objParamP->maxPeriod;
+                    }
+                }
+                paramP = &mergedParam;
+            }
+            else
+            {
+                paramP = objParamP;
+            }
+        }
+        else
+        {
+            paramP = instParamP;
+        }
+        uriP->flag &= ~LWM2M_URI_FLAG_RESOURCE_ID;
+    }
+    else
+    {
+        paramP = NULL;
+
+        if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+        {
+            PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_ITEM_START, LINK_ITEM_START_SIZE);
+            PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_URI_SEPARATOR, LINK_URI_SEPARATOR_SIZE);
+            res = utils_intToText(uriP->objectId, bufferLink + head, PRV_LINK_BUFFER_SIZE - head);
+            if (res <= 0) return -1;
+            head += res;
+            PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_URI_SEPARATOR, LINK_URI_SEPARATOR_SIZE);
+            res = utils_intToText(uriP->instanceId, bufferLink + head, PRV_LINK_BUFFER_SIZE - head);
+            if (res <= 0) return -1;
+            head += res;
+            PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_ITEM_END, LINK_ITEM_END_SIZE);
+            parentUri.instanceId = uriP->instanceId;
+            parentUri.flag = LWM2M_URI_FLAG_INSTANCE_ID;
+            if (serverP != NULL)
+            {
+                res = prv_serializeAttributes(contextP, &parentUri, serverP, NULL, bufferLink, head - 1, PRV_LINK_BUFFER_SIZE);
+                if (res < 0) return -1;    // careful, 0 is valid
+            }
+            else
+            {
+                res = 0;
+            }
+            head += res;
+        }
+        else
+        {
+            PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_ITEM_START, LINK_ITEM_START_SIZE);
+            PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_URI_SEPARATOR, LINK_URI_SEPARATOR_SIZE);
+            res = utils_intToText(uriP->objectId, bufferLink + head, PRV_LINK_BUFFER_SIZE - head);
+            if (res <= 0) return -1;
+            head += res;
+            PRV_CONCAT_STR(bufferLink, PRV_LINK_BUFFER_SIZE, head, LINK_ITEM_END, LINK_ITEM_END_SIZE);
+
+            if (serverP != NULL)
+            {
+                res = prv_serializeAttributes(contextP, &parentUri, serverP, NULL, bufferLink, head - 1, PRV_LINK_BUFFER_SIZE);
+                if (res < 0) return -1;    // careful, 0 is valid
+                head += res;
+            }
+        }
+    }
+
+    baseUriLen = uri_toString(uriP, baseUriStr, URI_MAX_STRING_LEN, NULL);
+    if (baseUriLen < 0) return -1;
+    baseUriLen -= 1;
+
+    for (index = 0; index < size && head < PRV_LINK_BUFFER_SIZE; index++)
+    {
+        res = prv_serializeLinkData(contextP, dataP + index, serverP, paramP, uriP, baseUriStr, baseUriLen, bufferLink + head, PRV_LINK_BUFFER_SIZE - head);
+        if (res < 0) return -1;
+        head += res;
+    }
+
+    if (head > 0)
+    {
+        head -= 1;
+
+        *bufferP = (uint8_t *)lwm2m_malloc(head);
+        if (*bufferP == NULL) return 0;
+        memcpy(*bufferP, bufferLink, head);
+    }
+
+    return (int)head;
+}
+#endif
\ No newline at end of file
diff -r 000000000000 -r 3f48af28ebcd core/er-coap-13/er-coap-13.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/er-coap-13/er-coap-13.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,1371 @@
+/*
+ * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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.
+ *
+ * This file is part of the Contiki operating system.
+ */
+
+/**
+ * \file
+ *      An implementation of the Constrained Application Protocol (draft 12)
+ * \author
+ *      Matthias Kovatsch <kovatsch@inf.ethz.ch>
+ * \contributors
+ *    David Navarro, Intel Corporation - Adapt to usage in liblwm2m
+ */
+
+
+#include <stdlib.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#include "er-coap-13.h"
+
+#include "liblwm2m.h" /* for lwm2m_malloc() and lwm2m_free() */
+
+#define DEBUG 0
+#if DEBUG
+#include <stdio.h>
+#define PRINTF(...) printf(__VA_ARGS__)
+#define PRINT6ADDR(addr) PRINTF("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", ((uint8_t *)addr)[0], ((uint8_t *)addr)[1], ((uint8_t *)addr)[2], ((uint8_t *)addr)[3], ((uint8_t *)addr)[4], ((uint8_t *)addr)[5], ((uint8_t *)addr)[6], ((uint8_t *)addr)[7], ((uint8_t *)addr)[8], ((uint8_t *)addr)[9], ((uint8_t *)addr)[10], ((uint8_t *)addr)[11], ((uint8_t *)addr)[12], ((uint8_t *)addr)[13], ((uint8_t *)addr)[14], ((uint8_t *)addr)[15])
+#define PRINTLLADDR(lladdr) PRINTF("[%02x:%02x:%02x:%02x:%02x:%02x]",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5])
+#else
+#define PRINTF(...)
+#define PRINT6ADDR(addr)
+#define PRINTLLADDR(addr)
+#endif
+
+/*-----------------------------------------------------------------------------------*/
+/*- Variables -----------------------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+static uint16_t current_mid = 0;
+
+coap_status_t coap_error_code = NO_ERROR;
+const char *coap_error_message = "";
+/*-----------------------------------------------------------------------------------*/
+/*- LOCAL HELP FUNCTIONS ------------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+static
+uint16_t
+coap_log_2(uint16_t value)
+{
+  uint16_t result = 0;
+  do {
+    value = value >> 1;
+    result++;
+  } while (value);
+
+  return result ? result - 1 : result;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+uint32_t
+coap_parse_int_option(uint8_t *bytes, size_t length)
+{
+  uint32_t var = 0;
+  size_t i = 0;
+  while (i<length)
+  {
+    var <<= 8;
+    var |= bytes[i++];
+  }
+  return var;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+uint8_t
+coap_option_nibble(unsigned int value)
+{
+  if (value<13)
+  {
+    return value;
+  }
+  else if (value<=0xFF+13)
+  {
+    return 13;
+  }
+  else
+  {
+    return 14;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+static
+size_t
+coap_set_option_header(unsigned int delta, size_t length, uint8_t *buffer)
+{
+  size_t written = 0;
+  unsigned int *x = &delta;
+
+  buffer[0] = coap_option_nibble(delta)<<4 | coap_option_nibble(length);
+
+  /* avoids code duplication without function overhead */
+  do
+  {
+    if (*x>268)
+    {
+      buffer[++written] = (*x-269)>>8;
+      buffer[++written] = (*x-269);
+    }
+    else if (*x>12)
+    {
+      buffer[++written] = (*x-13);
+    }
+  }
+  while (x!=(unsigned int *)&length && (x=(unsigned int *)&length));
+
+  PRINTF("WRITTEN %u B opt header\n", written);
+
+  return ++written;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+size_t
+coap_serialize_int_option(unsigned int number, unsigned int current_number, uint8_t *buffer, uint32_t value)
+{
+  size_t i = 0;
+
+  if (0xFF000000 & value) ++i;
+  if (0xFFFF0000 & value) ++i;
+  if (0xFFFFFF00 & value) ++i;
+  if (0xFFFFFFFF & value) ++i;
+
+  PRINTF("OPTION %u (delta %u, len %u)\n", number, number - current_number, i);
+
+  i = coap_set_option_header(number - current_number, i, buffer);
+
+  if (0xFF000000 & value) buffer[i++] = (uint8_t) (value>>24);
+  if (0xFFFF0000 & value) buffer[i++] = (uint8_t) (value>>16);
+  if (0xFFFFFF00 & value) buffer[i++] = (uint8_t) (value>>8);
+  if (0xFFFFFFFF & value) buffer[i++] = (uint8_t) (value);
+
+  return i;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+size_t
+coap_serialize_array_option(unsigned int number, unsigned int current_number, uint8_t *buffer, uint8_t *array, size_t length, char split_char)
+{
+  size_t i = 0;
+
+  if (split_char!='\0')
+  {
+    size_t j;
+    uint8_t *part_start = array;
+    uint8_t *part_end = NULL;
+    size_t temp_length;
+
+    for (j = 0; j<=length; ++j)
+    {
+      if (array[j]==split_char || j==length)
+      {
+        part_end = array + j;
+        temp_length = part_end-part_start;
+
+        i += coap_set_option_header(number - current_number, temp_length, &buffer[i]);
+        memcpy(&buffer[i], part_start, temp_length);
+        i += temp_length;
+
+        PRINTF("OPTION type %u, delta %u, len %u, part [%.*s]\n", number, number - current_number, i, temp_length, part_start);
+
+        ++j; /* skip the splitter */
+        current_number = number;
+        part_start = array + j;
+      }
+    } /* for */
+  }
+  else
+  {
+    i += coap_set_option_header(number - current_number, length, &buffer[i]);
+    memcpy(&buffer[i], array, length);
+    i += length;
+
+    PRINTF("OPTION type %u, delta %u, len %u\n", number, number - current_number, length);
+  }
+
+  return i;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+size_t
+coap_serialize_multi_option(unsigned int number, unsigned int current_number, uint8_t *buffer, multi_option_t *array)
+{
+  size_t i = 0;
+  multi_option_t * j;
+
+  for (j = array; j != NULL; j= j->next)
+  {
+     i += coap_set_option_header(number - current_number, j->len, &buffer[i]);
+     current_number = number;
+     memcpy(&buffer[i], j->data, j->len);
+     i += j->len;
+  } /* for */
+
+  return i;
+}
+/*-----------------------------------------------------------------------------------*/
+static
+void
+coap_merge_multi_option(uint8_t **dst, size_t *dst_len, uint8_t *option, size_t option_len, char separator)
+{
+  /* Merge multiple options. */
+  if (*dst_len > 0)
+  {
+    /* dst already contains an option: concatenate */
+    (*dst)[*dst_len] = separator;
+    *dst_len += 1;
+
+    /* memmove handles 2-byte option headers */
+    memmove((*dst)+(*dst_len), option, option_len);
+
+    *dst_len += option_len;
+  }
+  else
+  {
+    /* dst is empty: set to option */
+    *dst = option;
+    *dst_len = option_len;
+  }
+}
+
+void
+coap_add_multi_option(multi_option_t **dst, uint8_t *option, size_t option_len, uint8_t is_static)
+{
+  multi_option_t *opt = (multi_option_t *)lwm2m_malloc(sizeof(multi_option_t));
+
+  if (opt)
+  {
+    opt->next = NULL;
+    opt->len = (uint8_t)option_len;
+    if (is_static)
+    {
+      opt->data = option;
+      opt->is_static = 1;
+    }
+    else
+    {
+        opt->is_static = 0;
+        opt->data = (uint8_t *)lwm2m_malloc(option_len);
+        if (opt->data == NULL)
+        {
+            lwm2m_free(opt);
+            return;
+        }
+        memcpy(opt->data, option, option_len);
+    }
+
+    if (*dst)
+    {
+      multi_option_t * i = *dst;
+      while (i->next)
+      {
+        i = i->next;
+      }
+      i->next = opt;
+    }
+    else
+    {
+      *dst = opt;
+    }
+  }
+}
+
+void
+free_multi_option(multi_option_t *dst)
+{
+  if (dst)
+  {
+    multi_option_t *n = dst->next;
+    dst->next = NULL;
+    if (dst->is_static == 0)
+    {
+        lwm2m_free(dst->data);
+    }
+    lwm2m_free(dst);
+    free_multi_option(n);
+  }
+}
+
+char * coap_get_multi_option_as_string(multi_option_t * option)
+{
+    size_t len = 0;
+    multi_option_t * opt;
+    char * output;
+
+    for (opt = option; opt != NULL; opt = opt->next)
+    {
+       len += opt->len + 1;     // for separator
+    }
+
+    output = lwm2m_malloc(len + 1); // for String terminator
+    if (output != NULL)
+    {
+        size_t i = 0;
+
+        for (opt = option; opt != NULL; opt = opt->next)
+        {
+            output[i] = '/';
+            i += 1;
+
+            memmove(output + i, opt->data, opt->len);
+            i += opt->len;
+        }
+        output[i] = 0;
+    }
+
+    return output;
+}
+
+/*-----------------------------------------------------------------------------------*/
+static
+int
+coap_get_variable(const uint8_t *buffer, size_t length, const char *name, const char **output)
+{
+  const uint8_t *start = NULL;
+  const uint8_t *end = NULL;
+  const uint8_t *value_end = NULL;
+  size_t name_len = 0;
+
+  /*initialize the output buffer first*/
+  *output = 0;
+
+  name_len = strlen(name);
+  end = buffer + length;
+
+  for (start = buffer; start + name_len < end; ++start){
+    if ((start == buffer || start[-1] == '&') && start[name_len] == '=' &&
+        strncmp(name, (char *)start, name_len)==0) {
+
+      /* Point start to variable value */
+      start += name_len + 1;
+
+      /* Point end to the end of the value */
+      value_end = (const uint8_t *) memchr(start, '&', end - start);
+      if (value_end == NULL) {
+        value_end = end;
+      }
+
+      *output = (char *)start;
+
+      return (value_end - start);
+    }
+  }
+
+  return 0;
+}
+
+/*-----------------------------------------------------------------------------------*/
+uint16_t
+coap_get_mid()
+{
+  return ++current_mid;
+}
+/*-----------------------------------------------------------------------------------*/
+/*- MEASSAGE PROCESSING -------------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+void
+coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  /* Important thing */
+  memset(coap_pkt, 0, sizeof(coap_packet_t));
+
+  coap_pkt->type = type;
+  coap_pkt->code = code;
+  coap_pkt->mid = mid;
+}
+
+void
+coap_free_header(void *packet)
+{
+    coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+    free_multi_option(coap_pkt->uri_path);
+    free_multi_option(coap_pkt->uri_query);
+    free_multi_option(coap_pkt->location_path);
+    coap_pkt->uri_path = NULL;
+    coap_pkt->uri_query = NULL;
+    coap_pkt->location_path = NULL;
+}
+
+/*-----------------------------------------------------------------------------------*/
+size_t coap_serialize_get_size(void *packet)
+{
+    coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+    size_t length = 0;
+
+    length = COAP_HEADER_LEN + coap_pkt->payload_len + coap_pkt->token_len;
+
+    if (IS_OPTION(coap_pkt, COAP_OPTION_IF_MATCH))
+    {
+        length += COAP_MAX_OPTION_HEADER_LEN + coap_pkt->if_match_len;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_URI_HOST))
+    {
+        length += COAP_MAX_OPTION_HEADER_LEN + coap_pkt->uri_host_len;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_ETAG))
+    {
+        length += COAP_MAX_OPTION_HEADER_LEN + coap_pkt->etag_len;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_IF_NONE_MATCH))
+    {
+        // can be stored in extended fields
+        length += COAP_MAX_OPTION_HEADER_LEN;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE))
+    {
+        // can be stored in extended fields
+        length += COAP_MAX_OPTION_HEADER_LEN;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_URI_PORT))
+    {
+        // can be stored in extended fields
+        length += COAP_MAX_OPTION_HEADER_LEN;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH))
+    {
+        multi_option_t * optP;
+
+        for (optP = coap_pkt->location_path ; optP != NULL ; optP = optP->next)
+        {
+            length += COAP_MAX_OPTION_HEADER_LEN + optP->len;
+        }
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_URI_PATH))
+    {
+        multi_option_t * optP;
+
+        for (optP = coap_pkt->uri_path ; optP != NULL ; optP = optP->next)
+        {
+            length += COAP_MAX_OPTION_HEADER_LEN + optP->len;
+        }
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE))
+    {
+        // can be stored in extended fields
+        length += COAP_MAX_OPTION_HEADER_LEN;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_MAX_AGE))
+    {
+        // can be stored in extended fields
+        length += COAP_MAX_OPTION_HEADER_LEN;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY))
+    {
+        multi_option_t * optP;
+
+        for (optP = coap_pkt->uri_query ; optP != NULL ; optP = optP->next)
+        {
+            length += COAP_MAX_OPTION_HEADER_LEN + optP->len;
+        }
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_ACCEPT))
+    {
+        length += coap_pkt->accept_num * COAP_MAX_OPTION_HEADER_LEN;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY))
+    {
+        length += COAP_MAX_OPTION_HEADER_LEN + coap_pkt->location_query_len;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_BLOCK2))
+    {
+        // can be stored in extended fields
+        length += COAP_MAX_OPTION_HEADER_LEN;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_BLOCK1))
+    {
+        // can be stored in extended fields
+        length += COAP_MAX_OPTION_HEADER_LEN;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_SIZE))
+    {
+        // can be stored in extended fields
+        length += COAP_MAX_OPTION_HEADER_LEN;
+    }
+    if (IS_OPTION(coap_pkt, COAP_OPTION_PROXY_URI))
+    {
+        length += COAP_MAX_OPTION_HEADER_LEN + coap_pkt->proxy_uri_len;
+    }
+
+    return length;
+}
+
+/*-----------------------------------------------------------------------------------*/
+size_t
+coap_serialize_message(void *packet, uint8_t *buffer)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+  uint8_t *option;
+  unsigned int current_number = 0;
+
+  /* Initialize */
+  coap_pkt->buffer = buffer;
+  coap_pkt->version = 1;
+
+  PRINTF("-Serializing MID %u to %p, ", coap_pkt->mid, coap_pkt->buffer);
+
+  /* set header fields */
+  coap_pkt->buffer[0]  = 0x00;
+  coap_pkt->buffer[0] |= COAP_HEADER_VERSION_MASK & (coap_pkt->version)<<COAP_HEADER_VERSION_POSITION;
+  coap_pkt->buffer[0] |= COAP_HEADER_TYPE_MASK & (coap_pkt->type)<<COAP_HEADER_TYPE_POSITION;
+  coap_pkt->buffer[0] |= COAP_HEADER_TOKEN_LEN_MASK & (coap_pkt->token_len)<<COAP_HEADER_TOKEN_LEN_POSITION;
+  coap_pkt->buffer[1] = coap_pkt->code;
+  coap_pkt->buffer[2] = (uint8_t) ((coap_pkt->mid)>>8);
+  coap_pkt->buffer[3] = (uint8_t) (coap_pkt->mid);
+
+  /* set Token */
+  PRINTF("Token (len %u)", coap_pkt->token_len);
+  option = coap_pkt->buffer + COAP_HEADER_LEN;
+  for (current_number=0; current_number<coap_pkt->token_len; ++current_number)
+  {
+    PRINTF(" %02X", coap_pkt->token[current_number]);
+    *option = coap_pkt->token[current_number];
+    ++option;
+  }
+  PRINTF("-\n");
+
+  /* Serialize options */
+  current_number = 0;
+
+  PRINTF("-Serializing options at %p-\n", option);
+
+  /* The options must be serialized in the order of their number */
+  COAP_SERIALIZE_BYTE_OPTION(   COAP_OPTION_IF_MATCH,       if_match, "If-Match")
+  COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_URI_HOST,       uri_host, '\0', "Uri-Host")
+  COAP_SERIALIZE_BYTE_OPTION(   COAP_OPTION_ETAG,           etag, "ETag")
+  COAP_SERIALIZE_INT_OPTION(    COAP_OPTION_IF_NONE_MATCH,  content_type-coap_pkt->content_type, "If-None-Match") /* hack to get a zero field */
+  COAP_SERIALIZE_INT_OPTION(    COAP_OPTION_OBSERVE,        observe, "Observe")
+  COAP_SERIALIZE_INT_OPTION(    COAP_OPTION_URI_PORT,       uri_port, "Uri-Port")
+  COAP_SERIALIZE_MULTI_OPTION(  COAP_OPTION_LOCATION_PATH,  location_path, "Location-Path")
+  COAP_SERIALIZE_MULTI_OPTION(  COAP_OPTION_URI_PATH,       uri_path, "Uri-Path")
+  COAP_SERIALIZE_INT_OPTION(    COAP_OPTION_CONTENT_TYPE,   content_type, "Content-Format")
+  COAP_SERIALIZE_INT_OPTION(    COAP_OPTION_MAX_AGE,        max_age, "Max-Age")
+  COAP_SERIALIZE_MULTI_OPTION(  COAP_OPTION_URI_QUERY,      uri_query, "Uri-Query")
+  COAP_SERIALIZE_ACCEPT_OPTION( COAP_OPTION_ACCEPT,         accept, "Accept")
+  COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_LOCATION_QUERY, location_query, '&', "Location-Query")
+  COAP_SERIALIZE_BLOCK_OPTION(  COAP_OPTION_BLOCK2,         block2, "Block2")
+  COAP_SERIALIZE_BLOCK_OPTION(  COAP_OPTION_BLOCK1,         block1, "Block1")
+  COAP_SERIALIZE_INT_OPTION(    COAP_OPTION_SIZE,           size, "Size")
+  COAP_SERIALIZE_STRING_OPTION( COAP_OPTION_PROXY_URI,      proxy_uri, '\0', "Proxy-Uri")
+
+  PRINTF("-Done serializing at %p----\n", option);
+
+  /* Free allocated header fields */
+  coap_free_header(packet);
+
+  /* Pack payload */
+  /* Payload marker */
+  if (coap_pkt->payload_len)
+  {
+    *option = 0xFF;
+    ++option;
+  }
+
+  memmove(option, coap_pkt->payload, coap_pkt->payload_len);
+
+  PRINTF("-Done %u B (header len %u, payload len %u)-\n", coap_pkt->payload_len + option - buffer, option - buffer, coap_pkt->payload_len);
+
+  PRINTF("Dump [0x%02X %02X %02X %02X  %02X %02X %02X %02X]\n",
+      coap_pkt->buffer[0],
+      coap_pkt->buffer[1],
+      coap_pkt->buffer[2],
+      coap_pkt->buffer[3],
+      coap_pkt->buffer[4],
+      coap_pkt->buffer[5],
+      coap_pkt->buffer[6],
+      coap_pkt->buffer[7]
+    );
+
+  return (option - buffer) + coap_pkt->payload_len; /* packet length */
+}
+/*-----------------------------------------------------------------------------------*/
+coap_status_t
+coap_parse_message(void *packet, uint8_t *data, uint16_t data_len)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+  uint8_t *current_option;
+  unsigned int option_number = 0;
+  unsigned int option_delta = 0;
+  size_t option_length = 0;
+  unsigned int *x;
+
+  /* Initialize packet */
+  memset(coap_pkt, 0, sizeof(coap_packet_t));
+
+  /* pointer to packet bytes */
+  coap_pkt->buffer = data;
+
+  /* parse header fields */
+  coap_pkt->version = (COAP_HEADER_VERSION_MASK & coap_pkt->buffer[0])>>COAP_HEADER_VERSION_POSITION;
+  coap_pkt->type = (COAP_HEADER_TYPE_MASK & coap_pkt->buffer[0])>>COAP_HEADER_TYPE_POSITION;
+  coap_pkt->token_len = MIN(COAP_TOKEN_LEN, (COAP_HEADER_TOKEN_LEN_MASK & coap_pkt->buffer[0])>>COAP_HEADER_TOKEN_LEN_POSITION);
+  coap_pkt->code = coap_pkt->buffer[1];
+  coap_pkt->mid = coap_pkt->buffer[2]<<8 | coap_pkt->buffer[3];
+
+  if (coap_pkt->version != 1)
+  {
+    coap_error_message = "CoAP version must be 1";
+    return BAD_REQUEST_4_00;
+  }
+
+  current_option = data + COAP_HEADER_LEN;
+
+  if (coap_pkt->token_len != 0)
+  {
+      memcpy(coap_pkt->token, current_option, coap_pkt->token_len);
+      SET_OPTION(coap_pkt, COAP_OPTION_TOKEN);
+
+      PRINTF("Token (len %u) [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->token_len,
+        coap_pkt->token[0],
+        coap_pkt->token[1],
+        coap_pkt->token[2],
+        coap_pkt->token[3],
+        coap_pkt->token[4],
+        coap_pkt->token[5],
+        coap_pkt->token[6],
+        coap_pkt->token[7]
+      ); /*FIXME always prints 8 bytes */
+  }
+
+  /* parse options */
+  current_option += coap_pkt->token_len;
+
+  while (current_option < data+data_len)
+  {
+    /* Payload marker 0xFF, currently only checking for 0xF* because rest is reserved */
+    if ((current_option[0] & 0xF0)==0xF0)
+    {
+      coap_pkt->payload = ++current_option;
+      coap_pkt->payload_len = data_len - (coap_pkt->payload - data);
+
+      break;
+    }
+
+    option_delta = current_option[0]>>4;
+    option_length = current_option[0] & 0x0F;
+    ++current_option;
+
+    /* avoids code duplication without function overhead */
+    x = &option_delta;
+    do
+    {
+      if (*x==13)
+      {
+        *x += current_option[0];
+        ++current_option;
+      }
+      else if (*x==14)
+      {
+        *x += 255;
+        *x += current_option[0]<<8;
+        ++current_option;
+        *x += current_option[0];
+        ++current_option;
+      }
+    }
+    while (x!=(unsigned int *)&option_length && (x=(unsigned int *)&option_length));
+
+    option_number += option_delta;
+
+    PRINTF("OPTION %u (delta %u, len %u): ", option_number, option_delta, option_length);
+
+    SET_OPTION(coap_pkt, option_number);
+
+    switch (option_number)
+    {
+      case COAP_OPTION_CONTENT_TYPE:
+        coap_pkt->content_type = coap_parse_int_option(current_option, option_length);
+        PRINTF("Content-Format [%u]\n", coap_pkt->content_type);
+        break;
+      case COAP_OPTION_MAX_AGE:
+        coap_pkt->max_age = coap_parse_int_option(current_option, option_length);
+        PRINTF("Max-Age [%lu]\n", coap_pkt->max_age);
+        break;
+      case COAP_OPTION_ETAG:
+        coap_pkt->etag_len = (uint8_t)(MIN(COAP_ETAG_LEN, option_length));
+        memcpy(coap_pkt->etag, current_option, coap_pkt->etag_len);
+        PRINTF("ETag %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->etag_len,
+          coap_pkt->etag[0],
+          coap_pkt->etag[1],
+          coap_pkt->etag[2],
+          coap_pkt->etag[3],
+          coap_pkt->etag[4],
+          coap_pkt->etag[5],
+          coap_pkt->etag[6],
+          coap_pkt->etag[7]
+        ); /*FIXME always prints 8 bytes */
+        break;
+      case COAP_OPTION_ACCEPT:
+        if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM)
+        {
+          coap_pkt->accept[coap_pkt->accept_num] = coap_parse_int_option(current_option, option_length);
+          coap_pkt->accept_num += 1;
+          PRINTF("Accept [%u]\n", coap_pkt->content_type);
+        }
+        break;
+      case COAP_OPTION_IF_MATCH:
+        /*FIXME support multiple ETags */
+        coap_pkt->if_match_len = (uint8_t)(MIN(COAP_ETAG_LEN, option_length));
+        memcpy(coap_pkt->if_match, current_option, coap_pkt->if_match_len);
+        PRINTF("If-Match %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->if_match_len,
+          coap_pkt->if_match[0],
+          coap_pkt->if_match[1],
+          coap_pkt->if_match[2],
+          coap_pkt->if_match[3],
+          coap_pkt->if_match[4],
+          coap_pkt->if_match[5],
+          coap_pkt->if_match[6],
+          coap_pkt->if_match[7]
+        ); /*FIXME always prints 8 bytes */
+        break;
+      case COAP_OPTION_IF_NONE_MATCH:
+        coap_pkt->if_none_match = 1;
+        PRINTF("If-None-Match\n");
+        break;
+
+      case COAP_OPTION_URI_HOST:
+        coap_pkt->uri_host = current_option;
+        coap_pkt->uri_host_len = option_length;
+        PRINTF("Uri-Host [%.*s]\n", coap_pkt->uri_host_len, coap_pkt->uri_host);
+        break;
+      case COAP_OPTION_URI_PORT:
+        coap_pkt->uri_port = coap_parse_int_option(current_option, option_length);
+        PRINTF("Uri-Port [%u]\n", coap_pkt->uri_port);
+        break;
+      case COAP_OPTION_URI_PATH:
+        /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */
+        // coap_merge_multi_option( (char **) &(coap_pkt->uri_path), &(coap_pkt->uri_path_len), current_option, option_length, 0);
+        coap_add_multi_option( &(coap_pkt->uri_path), current_option, option_length, 1);
+        PRINTF("Uri-Path [%.*s]\n", option_length, current_option);
+        break;
+      case COAP_OPTION_URI_QUERY:
+        /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */
+        // coap_merge_multi_option( (char **) &(coap_pkt->uri_query), &(coap_pkt->uri_query_len), current_option, option_length, '&');
+        coap_add_multi_option( &(coap_pkt->uri_query), current_option, option_length, 1);
+        PRINTF("Uri-Query [%.*s]\n", option_length, current_option);
+        break;
+
+      case COAP_OPTION_LOCATION_PATH:
+        coap_add_multi_option( &(coap_pkt->location_path), current_option, option_length, 1);
+        break;
+      case COAP_OPTION_LOCATION_QUERY:
+        /* coap_merge_multi_option() operates in-place on the IPBUF, but final packet field should be const string -> cast to string */
+        coap_merge_multi_option( &(coap_pkt->location_query), &(coap_pkt->location_query_len), current_option, option_length, '&');
+        PRINTF("Location-Query [%.*s]\n", option_length, current_option);
+        break;
+
+      case COAP_OPTION_PROXY_URI:
+        /*FIXME check for own end-point */
+        coap_pkt->proxy_uri = current_option;
+        coap_pkt->proxy_uri_len = option_length;
+        /*TODO length > 270 not implemented (actually not required) */
+        PRINTF("Proxy-Uri NOT IMPLEMENTED [%.*s]\n", coap_pkt->proxy_uri_len, coap_pkt->proxy_uri);
+        coap_error_message = "This is a constrained server (Contiki)";
+        return PROXYING_NOT_SUPPORTED_5_05;
+        break;
+
+      case COAP_OPTION_OBSERVE:
+        coap_pkt->observe = coap_parse_int_option(current_option, option_length);
+        PRINTF("Observe [%lu]\n", coap_pkt->observe);
+        break;
+      case COAP_OPTION_BLOCK2:
+        coap_pkt->block2_num = coap_parse_int_option(current_option, option_length);
+        coap_pkt->block2_more = (coap_pkt->block2_num & 0x08)>>3;
+        coap_pkt->block2_size = 16 << (coap_pkt->block2_num & 0x07);
+        coap_pkt->block2_offset = (coap_pkt->block2_num & ~0x0000000F)<<(coap_pkt->block2_num & 0x07);
+        coap_pkt->block2_num >>= 4;
+        PRINTF("Block2 [%lu%s (%u B/blk)]\n", coap_pkt->block2_num, coap_pkt->block2_more ? "+" : "", coap_pkt->block2_size);
+        break;
+      case COAP_OPTION_BLOCK1:
+        coap_pkt->block1_num = coap_parse_int_option(current_option, option_length);
+        coap_pkt->block1_more = (coap_pkt->block1_num & 0x08)>>3;
+        coap_pkt->block1_size = 16 << (coap_pkt->block1_num & 0x07);
+        coap_pkt->block1_offset = (coap_pkt->block1_num & ~0x0000000F)<<(coap_pkt->block1_num & 0x07);
+        coap_pkt->block1_num >>= 4;
+        PRINTF("Block1 [%lu%s (%u B/blk)]\n", coap_pkt->block1_num, coap_pkt->block1_more ? "+" : "", coap_pkt->block1_size);
+        break;
+      case COAP_OPTION_SIZE:
+        coap_pkt->size = coap_parse_int_option(current_option, option_length);
+        PRINTF("Size [%lu]\n", coap_pkt->size);
+        break;
+      default:
+        PRINTF("unknown (%u)\n", option_number);
+        /* Check if critical (odd) */
+        if (option_number & 1)
+        {
+          coap_error_message = "Unsupported critical option";
+          return BAD_OPTION_4_02;
+        }
+    }
+
+    current_option += option_length;
+  } /* for */
+  PRINTF("-Done parsing-------\n");
+
+
+
+  return NO_ERROR;
+}
+/*-----------------------------------------------------------------------------------*/
+/*- REST FRAMEWORK FUNCTIONS --------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_query_variable(void *packet, const char *name, const char **output)
+{
+/*
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) {
+    return coap_get_variable(coap_pkt->uri_query, coap_pkt->uri_query_len, name, output);
+  }
+*/
+  return 0;
+}
+
+int
+coap_get_post_variable(void *packet, const char *name, const char **output)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (coap_pkt->payload_len) {
+    return coap_get_variable(coap_pkt->payload, coap_pkt->payload_len, name, output);
+  }
+  return 0;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_set_status_code(void *packet, unsigned int code)
+{
+  if (code <= 0xFF)
+  {
+    ((coap_packet_t *)packet)->code = (uint8_t) code;
+    return 1;
+  }
+  else
+  {
+    return 0;
+  }
+}
+/*-----------------------------------------------------------------------------------*/
+/*- HEADER OPTION GETTERS AND SETTERS -----------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+unsigned int
+coap_get_header_content_type(void *packet)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE)) return -1;
+
+  return coap_pkt->content_type;
+}
+
+int
+coap_set_header_content_type(void *packet, unsigned int content_type)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  coap_pkt->content_type = (coap_content_type_t) content_type;
+  SET_OPTION(coap_pkt, COAP_OPTION_CONTENT_TYPE);
+  return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_accept(void *packet, const uint16_t **accept)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_ACCEPT)) return 0;
+
+  *accept = coap_pkt->accept;
+  return coap_pkt->accept_num;
+}
+
+int
+coap_set_header_accept(void *packet, uint16_t accept)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (coap_pkt->accept_num < COAP_MAX_ACCEPT_NUM)
+  {
+    coap_pkt->accept[coap_pkt->accept_num] = accept;
+    coap_pkt->accept_num += 1;
+
+    SET_OPTION(coap_pkt, COAP_OPTION_ACCEPT);
+  }
+  return coap_pkt->accept_num;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_max_age(void *packet, uint32_t *age)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_MAX_AGE)) {
+    *age = COAP_DEFAULT_MAX_AGE;
+  } else {
+    *age = coap_pkt->max_age;
+  }
+  return 1;
+}
+
+int
+coap_set_header_max_age(void *packet, uint32_t age)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  coap_pkt->max_age = age;
+  SET_OPTION(coap_pkt, COAP_OPTION_MAX_AGE);
+  return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_etag(void *packet, const uint8_t **etag)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_ETAG)) return 0;
+
+  *etag = coap_pkt->etag;
+  return coap_pkt->etag_len;
+}
+
+int
+coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  coap_pkt->etag_len = (uint8_t)(MIN(COAP_ETAG_LEN, etag_len));
+  memcpy(coap_pkt->etag, etag, coap_pkt->etag_len);
+
+  SET_OPTION(coap_pkt, COAP_OPTION_ETAG);
+  return coap_pkt->etag_len;
+}
+/*-----------------------------------------------------------------------------------*/
+/*FIXME support multiple ETags */
+int
+coap_get_header_if_match(void *packet, const uint8_t **etag)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_IF_MATCH)) return 0;
+
+  *etag = coap_pkt->if_match;
+  return coap_pkt->if_match_len;
+}
+
+int
+coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  coap_pkt->if_match_len = (uint8_t)(MIN(COAP_ETAG_LEN, etag_len));
+  memcpy(coap_pkt->if_match, etag, coap_pkt->if_match_len);
+
+  SET_OPTION(coap_pkt, COAP_OPTION_IF_MATCH);
+  return coap_pkt->if_match_len;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_if_none_match(void *packet)
+{
+  return IS_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH) ? 1 : 0;
+}
+
+int
+coap_set_header_if_none_match(void *packet)
+{
+  SET_OPTION((coap_packet_t *)packet, COAP_OPTION_IF_NONE_MATCH);
+  return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_token(void *packet, const uint8_t **token)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_TOKEN)) return 0;
+
+  *token = coap_pkt->token;
+  return coap_pkt->token_len;
+}
+
+int
+coap_set_header_token(void *packet, const uint8_t *token, size_t token_len)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  coap_pkt->token_len = (uint8_t)(MIN(COAP_TOKEN_LEN, token_len));
+  memcpy(coap_pkt->token, token, coap_pkt->token_len);
+
+  SET_OPTION(coap_pkt, COAP_OPTION_TOKEN);
+  return coap_pkt->token_len;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_proxy_uri(void *packet, const char **uri)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_PROXY_URI)) return 0;
+
+  *uri = (const char *)coap_pkt->proxy_uri;
+  return coap_pkt->proxy_uri_len;
+}
+
+int
+coap_set_header_proxy_uri(void *packet, const char *uri)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  coap_pkt->proxy_uri = (uint8_t *)uri;
+  coap_pkt->proxy_uri_len = strlen(uri);
+
+  SET_OPTION(coap_pkt, COAP_OPTION_PROXY_URI);
+  return coap_pkt->proxy_uri_len;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_uri_host(void *packet, const char **host)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_HOST)) return 0;
+
+  *host = (char *)coap_pkt->uri_host;
+  return coap_pkt->uri_host_len;
+}
+
+int
+coap_set_header_uri_host(void *packet, const char *host)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  coap_pkt->uri_host = (uint8_t *)host;
+  coap_pkt->uri_host_len = strlen(host);
+
+  SET_OPTION(coap_pkt, COAP_OPTION_URI_HOST);
+  return coap_pkt->uri_host_len;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_uri_path(void *packet, const char **path)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_PATH)) return 0;
+
+  *path = NULL; //coap_pkt->uri_path;
+  return 0; //coap_pkt->uri_path_len;
+}
+
+int
+coap_set_header_uri_path(void *packet, const char *path)
+{
+  coap_packet_t *coap_pkt = (coap_packet_t *) packet;
+  int length = 0;
+
+  free_multi_option(coap_pkt->uri_path);
+  coap_pkt->uri_path = NULL;
+
+  if (path[0]=='/') ++path;
+
+  do
+  {
+      int i = 0;
+
+      while (path[i] != 0 && path[i] != '/') i++;
+      coap_add_multi_option(&(coap_pkt->uri_path), (uint8_t *)path, i, 0);
+
+      if (path[i] == '/') i++;
+      path += i;
+      length += i;
+  } while (path[0] != 0);
+
+  SET_OPTION(coap_pkt, COAP_OPTION_URI_PATH);
+  return length;
+}
+
+int
+coap_set_header_uri_path_segment(void *packet, const char *segment)
+{
+  coap_packet_t *coap_pkt = (coap_packet_t *) packet;
+  int length;
+
+  if (segment == NULL || segment[0] == 0)
+  {
+      coap_add_multi_option(&(coap_pkt->uri_path), NULL, 0, 1);
+      length = 0;
+  }
+  else
+  {
+      length = strlen(segment);
+      coap_add_multi_option(&(coap_pkt->uri_path), (uint8_t *)segment, length, 0);
+  }
+
+  SET_OPTION(coap_pkt, COAP_OPTION_URI_PATH);
+  return length;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_uri_query(void *packet, const char **query)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_URI_QUERY)) return 0;
+
+  *query = NULL; //coap_pkt->uri_query;
+  return 0; //coap_pkt->uri_query_len;
+}
+
+int
+coap_set_header_uri_query(void *packet, const char *query)
+{
+    int length = 0;
+    coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+    free_multi_option(coap_pkt->uri_query);
+    coap_pkt->uri_query = NULL;
+
+    if (query[0]=='?') ++query;
+
+    do
+    {
+        int i = 0;
+
+        while (query[i] != 0 && query[i] != '&') i++;
+        coap_add_multi_option(&(coap_pkt->uri_query), (uint8_t *)query, i, 0);
+
+        if (query[i] == '&') i++;
+        query += i;
+        length += i;
+    } while (query[0] != 0);
+
+    SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    return length;
+ }
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_location_path(void *packet, const char **path)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH)) return 0;
+
+  *path = NULL; //coap_pkt->location_path;
+  return 0; //coap_pkt->location_path_len;
+}
+
+int
+coap_set_header_location_path(void *packet, const char *path)
+{
+    coap_packet_t *coap_pkt = (coap_packet_t *) packet;
+    int length = 0;
+
+    free_multi_option(coap_pkt->location_path);
+    coap_pkt->location_path = NULL;
+
+    if (path[0]=='/') ++path;
+
+    do
+    {
+        int i = 0;
+
+        while (path[i] != 0 && path[i] != '/') i++;
+        coap_add_multi_option(&(coap_pkt->location_path), (uint8_t *)path, i, 0);
+
+        if (path[i] == '/') i++;
+        path += i;
+        length += i;
+    } while (path[0] != 0);
+
+    SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_PATH);
+    return length;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_location_query(void *packet, const char **query)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY)) return 0;
+
+  *query = (const char*)coap_pkt->location_query;
+  return coap_pkt->location_query_len;
+}
+
+int
+coap_set_header_location_query(void *packet, char *query)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  while (query[0]=='?') ++query;
+
+  coap_pkt->location_query = (uint8_t *)query;
+  coap_pkt->location_query_len = strlen(query);
+
+  SET_OPTION(coap_pkt, COAP_OPTION_LOCATION_QUERY);
+  return coap_pkt->location_query_len;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_observe(void *packet, uint32_t *observe)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_OBSERVE)) return 0;
+
+  *observe = coap_pkt->observe;
+  return 1;
+}
+
+int
+coap_set_header_observe(void *packet, uint32_t observe)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  coap_pkt->observe = 0x00FFFFFF & observe;
+  SET_OPTION(coap_pkt, COAP_OPTION_OBSERVE);
+  return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK2)) return 0;
+
+  /* pointers may be NULL to get only specific block parameters */
+  if (num!=NULL) *num = coap_pkt->block2_num;
+  if (more!=NULL) *more = coap_pkt->block2_more;
+  if (size!=NULL) *size = coap_pkt->block2_size;
+  if (offset!=NULL) *offset = coap_pkt->block2_offset;
+
+  return 1;
+}
+
+int
+coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (size<16) return 0;
+  if (size>2048) return 0;
+  if (num>0x0FFFFF) return 0;
+
+  coap_pkt->block2_num = num;
+  coap_pkt->block2_more = more ? 1 : 0;
+  coap_pkt->block2_size = size;
+
+  SET_OPTION(coap_pkt, COAP_OPTION_BLOCK2);
+  return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_BLOCK1)) return 0;
+
+  /* pointers may be NULL to get only specific block parameters */
+  if (num!=NULL) *num = coap_pkt->block1_num;
+  if (more!=NULL) *more = coap_pkt->block1_more;
+  if (size!=NULL) *size = coap_pkt->block1_size;
+  if (offset!=NULL) *offset = coap_pkt->block1_offset;
+
+  return 1;
+}
+
+int
+coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (size<16) return 0;
+  if (size>2048) return 0;
+  if (num>0x0FFFFF) return 0;
+
+  coap_pkt->block1_num = num;
+  coap_pkt->block1_more = more;
+  coap_pkt->block1_size = size;
+
+  SET_OPTION(coap_pkt, COAP_OPTION_BLOCK1);
+  return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_header_size(void *packet, uint32_t *size)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (!IS_OPTION(coap_pkt, COAP_OPTION_SIZE)) return 0;
+  
+  *size = coap_pkt->size;
+  return 1;
+}
+
+int
+coap_set_header_size(void *packet, uint32_t size)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  coap_pkt->size = size;
+  SET_OPTION(coap_pkt, COAP_OPTION_SIZE);
+  return 1;
+}
+/*-----------------------------------------------------------------------------------*/
+/*- PAYLOAD -------------------------------------------------------------------------*/
+/*-----------------------------------------------------------------------------------*/
+int
+coap_get_payload(void *packet, const uint8_t **payload)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  if (coap_pkt->payload) {
+    *payload = coap_pkt->payload;
+    return coap_pkt->payload_len;
+  } else {
+    *payload = NULL;
+    return 0;
+  }
+}
+
+int
+coap_set_payload(void *packet, const void *payload, size_t length)
+{
+  coap_packet_t *const coap_pkt = (coap_packet_t *) packet;
+
+  coap_pkt->payload = (uint8_t *) payload;
+  coap_pkt->payload_len = (uint16_t)(length);
+
+  return coap_pkt->payload_len;
+}
+/*-----------------------------------------------------------------------------------*/
diff -r 000000000000 -r 3f48af28ebcd core/er-coap-13/er-coap-13.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/er-coap-13/er-coap-13.h	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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.
+ *
+ * This file is part of the Contiki operating system.
+ */
+
+/**
+ * \file
+ *      An implementation of the Constrained Application Protocol (draft 12)
+ * \author
+ *      Matthias Kovatsch <kovatsch@inf.ethz.ch>
+ * \contributors
+ *    David Navarro, Intel Corporation - Adapt to usage in liblwm2m
+ */
+
+
+#ifndef COAP_13_H_
+#define COAP_13_H_
+
+#include <stdint.h>
+#include <stddef.h> /* for size_t */
+
+/*
+ * The maximum buffer size that is provided for resource responses and must be respected due to the limited IP buffer.
+ * Larger data must be handled by the resource and will be sent chunk-wise through a TCP stream or CoAP blocks.
+ */
+#ifndef REST_MAX_CHUNK_SIZE
+#define REST_MAX_CHUNK_SIZE     128
+#endif
+
+#define COAP_DEFAULT_MAX_AGE                 60
+#define COAP_RESPONSE_TIMEOUT                2
+#define COAP_MAX_RETRANSMIT                  4
+#define COAP_ACK_RANDOM_FACTOR               1.5
+
+#define COAP_MAX_TRANSMIT_WAIT               ((COAP_RESPONSE_TIMEOUT * ( (1 << (COAP_MAX_RETRANSMIT + 1) ) - 1) * COAP_ACK_RANDOM_FACTOR))
+
+#define COAP_HEADER_LEN                      4 /* | version:0x03 type:0x0C tkl:0xF0 | code | mid:0x00FF | mid:0xFF00 | */
+#define COAP_ETAG_LEN                        8 /* The maximum number of bytes for the ETag */
+#define COAP_TOKEN_LEN                       8 /* The maximum number of bytes for the Token */
+#define COAP_MAX_ACCEPT_NUM                  2 /* The maximum number of accept preferences to parse/store */
+
+#define COAP_MAX_OPTION_HEADER_LEN           5
+
+#define COAP_HEADER_VERSION_MASK             0xC0
+#define COAP_HEADER_VERSION_POSITION         6
+#define COAP_HEADER_TYPE_MASK                0x30
+#define COAP_HEADER_TYPE_POSITION            4
+#define COAP_HEADER_TOKEN_LEN_MASK           0x0F
+#define COAP_HEADER_TOKEN_LEN_POSITION       0
+
+#define COAP_HEADER_OPTION_DELTA_MASK        0xF0
+#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F
+
+/*
+ * Conservative size limit, as not all options have to be set at the same time.
+ */
+#ifndef COAP_MAX_HEADER_SIZE
+/*                            Hdr CoT Age  Tag              Obs  Tok               Blo strings */
+#define COAP_MAX_HEADER_SIZE  (4 + 3 + 5 + 1+COAP_ETAG_LEN + 3 + 1+COAP_TOKEN_LEN + 4 + 30) /* 70 */
+#endif /* COAP_MAX_HEADER_SIZE */
+
+#define COAP_MAX_PACKET_SIZE  (COAP_MAX_HEADER_SIZE + REST_MAX_CHUNK_SIZE)
+/*                                        0/14          48 for IPv6 (28 for IPv4) */
+#if COAP_MAX_PACKET_SIZE > (UIP_BUFSIZE - UIP_LLH_LEN - UIP_IPUDPH_LEN)
+//#error "UIP_CONF_BUFFER_SIZE too small for REST_MAX_CHUNK_SIZE"
+#endif
+
+
+/* Bitmap for set options */
+enum { OPTION_MAP_SIZE = sizeof(uint8_t) * 8 };
+#define SET_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] |= 1 << (opt % OPTION_MAP_SIZE))
+#define IS_OPTION(packet, opt) ((packet)->options[opt / OPTION_MAP_SIZE] & (1 << (opt % OPTION_MAP_SIZE)))
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b)? (a) : (b))
+#endif /* MIN */
+
+/* CoAP message types */
+typedef enum {
+  COAP_TYPE_CON, /* confirmables */
+  COAP_TYPE_NON, /* non-confirmables */
+  COAP_TYPE_ACK, /* acknowledgements */
+  COAP_TYPE_RST  /* reset */
+} coap_message_type_t;
+
+/* CoAP request method codes */
+typedef enum {
+  COAP_GET = 1,
+  COAP_POST,
+  COAP_PUT,
+  COAP_DELETE
+} coap_method_t;
+
+/* CoAP response codes */
+typedef enum {
+  NO_ERROR = 0,
+
+  CREATED_2_01 = 65,                    /* CREATED */
+  DELETED_2_02 = 66,                    /* DELETED */
+  VALID_2_03 = 67,                      /* NOT_MODIFIED */
+  CHANGED_2_04 = 68,                    /* CHANGED */
+  CONTENT_2_05 = 69,                    /* OK */
+
+  BAD_REQUEST_4_00 = 128,               /* BAD_REQUEST */
+  UNAUTHORIZED_4_01 = 129,              /* UNAUTHORIZED */
+  BAD_OPTION_4_02 = 130,                /* BAD_OPTION */
+  FORBIDDEN_4_03 = 131,                 /* FORBIDDEN */
+  NOT_FOUND_4_04 = 132,                 /* NOT_FOUND */
+  METHOD_NOT_ALLOWED_4_05 = 133,        /* METHOD_NOT_ALLOWED */
+  NOT_ACCEPTABLE_4_06 = 134,            /* NOT_ACCEPTABLE */
+  PRECONDITION_FAILED_4_12 = 140,       /* BAD_REQUEST */
+  REQUEST_ENTITY_TOO_LARGE_4_13 = 141,  /* REQUEST_ENTITY_TOO_LARGE */
+  UNSUPPORTED_MEDIA_TYPE_4_15 = 143,    /* UNSUPPORTED_MEDIA_TYPE */
+
+  INTERNAL_SERVER_ERROR_5_00 = 160,     /* INTERNAL_SERVER_ERROR */
+  NOT_IMPLEMENTED_5_01 = 161,           /* NOT_IMPLEMENTED */
+  BAD_GATEWAY_5_02 = 162,               /* BAD_GATEWAY */
+  SERVICE_UNAVAILABLE_5_03 = 163,       /* SERVICE_UNAVAILABLE */
+  GATEWAY_TIMEOUT_5_04 = 164,           /* GATEWAY_TIMEOUT */
+  PROXYING_NOT_SUPPORTED_5_05 = 165,    /* PROXYING_NOT_SUPPORTED */
+
+  /* Erbium errors */
+  MEMORY_ALLOCATION_ERROR = 192,
+  PACKET_SERIALIZATION_ERROR,
+
+  /* Erbium hooks */
+  MANUAL_RESPONSE
+
+} coap_status_t;
+
+/* CoAP header options */
+typedef enum {
+  COAP_OPTION_IF_MATCH = 1,       /* 0-8 B */
+  COAP_OPTION_URI_HOST = 3,       /* 1-255 B */
+  COAP_OPTION_ETAG = 4,           /* 1-8 B */
+  COAP_OPTION_IF_NONE_MATCH = 5,  /* 0 B */
+  COAP_OPTION_OBSERVE = 6,        /* 0-3 B */
+  COAP_OPTION_URI_PORT = 7,       /* 0-2 B */
+  COAP_OPTION_LOCATION_PATH = 8,  /* 0-255 B */
+  COAP_OPTION_URI_PATH = 11,      /* 0-255 B */
+  COAP_OPTION_CONTENT_TYPE = 12,  /* 0-2 B */
+  COAP_OPTION_MAX_AGE = 14,       /* 0-4 B */
+  COAP_OPTION_URI_QUERY = 15,     /* 0-270 B */
+  COAP_OPTION_ACCEPT = 17,        /* 0-2 B */
+  COAP_OPTION_TOKEN = 19,         /* 1-8 B */
+  COAP_OPTION_LOCATION_QUERY = 20, /* 1-270 B */
+  COAP_OPTION_BLOCK2 = 23,        /* 1-3 B */
+  COAP_OPTION_BLOCK1 = 27,        /* 1-3 B */
+  COAP_OPTION_SIZE = 28,          /* 0-4 B */
+  COAP_OPTION_PROXY_URI = 35,     /* 1-270 B */
+  OPTION_MAX_VALUE = 0xFFFF
+} coap_option_t;
+
+/* CoAP Content-Types */
+typedef enum {
+  TEXT_PLAIN = 0,
+  TEXT_XML = 1, /* Indented types are not in the initial registry. */
+  TEXT_CSV = 2,
+  TEXT_HTML = 3,
+  IMAGE_GIF = 21,
+  IMAGE_JPEG = 22,
+  IMAGE_PNG = 23,
+  IMAGE_TIFF = 24,
+  AUDIO_RAW = 25,
+  VIDEO_RAW = 26,
+  APPLICATION_LINK_FORMAT = 40,
+  APPLICATION_XML = 41,
+  APPLICATION_OCTET_STREAM = 42,
+  APPLICATION_RDF_XML = 43,
+  APPLICATION_SOAP_XML = 44,
+  APPLICATION_ATOM_XML = 45,
+  APPLICATION_XMPP_XML = 46,
+  APPLICATION_EXI = 47,
+  APPLICATION_FASTINFOSET = 48,
+  APPLICATION_SOAP_FASTINFOSET = 49,
+  APPLICATION_JSON = 50,
+  APPLICATION_X_OBIX_BINARY = 51,
+  CONTENT_MAX_VALUE = 0xFFFF
+} coap_content_type_t;
+
+typedef struct _multi_option_t {
+  struct _multi_option_t *next;
+  uint8_t is_static;
+  uint8_t len;
+  uint8_t *data;
+} multi_option_t;
+
+/* Parsed message struct */
+typedef struct {
+  uint8_t *buffer; /* pointer to CoAP header / incoming packet buffer / memory to serialize packet */
+
+  uint8_t version;
+  coap_message_type_t type;
+  uint8_t code;
+  uint16_t mid;
+
+  uint8_t options[COAP_OPTION_PROXY_URI / OPTION_MAP_SIZE + 1]; /* Bitmap to check if option is set */
+
+  coap_content_type_t content_type; /* Parse options once and store; allows setting options in random order  */
+  uint32_t max_age;
+  size_t proxy_uri_len;
+  const uint8_t *proxy_uri;
+  uint8_t etag_len;
+  uint8_t etag[COAP_ETAG_LEN];
+  size_t uri_host_len;
+  const uint8_t *uri_host;
+  multi_option_t *location_path;
+  uint16_t uri_port;
+  size_t location_query_len;
+  uint8_t *location_query;
+  multi_option_t *uri_path;
+  uint32_t observe;
+  uint8_t token_len;
+  uint8_t token[COAP_TOKEN_LEN];
+  uint8_t accept_num;
+  uint16_t accept[COAP_MAX_ACCEPT_NUM];
+  uint8_t if_match_len;
+  uint8_t if_match[COAP_ETAG_LEN];
+  uint32_t block2_num;
+  uint8_t block2_more;
+  uint16_t block2_size;
+  uint32_t block2_offset;
+  uint32_t block1_num;
+  uint8_t block1_more;
+  uint16_t block1_size;
+  uint32_t block1_offset;
+  uint32_t size;
+  multi_option_t *uri_query;
+  uint8_t if_none_match;
+
+  uint16_t payload_len;
+  uint8_t *payload;
+
+} coap_packet_t;
+
+/* Option format serialization*/
+#define COAP_SERIALIZE_INT_OPTION(number, field, text)  \
+    if (IS_OPTION(coap_pkt, number)) { \
+      PRINTF(text" [%u]\n", coap_pkt->field); \
+      option += coap_serialize_int_option(number, current_number, option, coap_pkt->field); \
+      current_number = number; \
+    }
+#define COAP_SERIALIZE_BYTE_OPTION(number, field, text)      \
+    if (IS_OPTION(coap_pkt, number)) { \
+      PRINTF(text" %u [0x%02X%02X%02X%02X%02X%02X%02X%02X]\n", coap_pkt->field##_len, \
+        coap_pkt->field[0], \
+        coap_pkt->field[1], \
+        coap_pkt->field[2], \
+        coap_pkt->field[3], \
+        coap_pkt->field[4], \
+        coap_pkt->field[5], \
+        coap_pkt->field[6], \
+        coap_pkt->field[7] \
+      ); /*FIXME always prints 8 bytes */ \
+      option += coap_serialize_array_option(number, current_number, option, coap_pkt->field, coap_pkt->field##_len, '\0'); \
+      current_number = number; \
+    }
+#define COAP_SERIALIZE_STRING_OPTION(number, field, splitter, text)      \
+    if (IS_OPTION(coap_pkt, number)) { \
+      PRINTF(text" [%.*s]\n", coap_pkt->field##_len, coap_pkt->field); \
+      option += coap_serialize_array_option(number, current_number, option, (uint8_t *) coap_pkt->field, coap_pkt->field##_len, splitter); \
+      current_number = number; \
+    }
+#define COAP_SERIALIZE_MULTI_OPTION(number, field, text)      \
+        if (IS_OPTION(coap_pkt, number)) { \
+          PRINTF(text); \
+          option += coap_serialize_multi_option(number, current_number, option, coap_pkt->field); \
+          current_number = number; \
+        }
+#define COAP_SERIALIZE_ACCEPT_OPTION(number, field, text)  \
+    if (IS_OPTION(coap_pkt, number)) { \
+      int i; \
+      for (i=0; i<coap_pkt->field##_num; ++i) \
+      { \
+        PRINTF(text" [%u]\n", coap_pkt->field[i]); \
+        option += coap_serialize_int_option(number, current_number, option, coap_pkt->field[i]); \
+        current_number = number; \
+      } \
+    }
+#define COAP_SERIALIZE_BLOCK_OPTION(number, field, text)      \
+    if (IS_OPTION(coap_pkt, number)) \
+    { \
+      uint32_t block = coap_pkt->field##_num << 4; \
+      PRINTF(text" [%lu%s (%u B/blk)]\n", coap_pkt->field##_num, coap_pkt->field##_more ? "+" : "", coap_pkt->field##_size); \
+      if (coap_pkt->field##_more) block |= 0x8; \
+      block |= 0xF & coap_log_2(coap_pkt->field##_size/16); \
+      PRINTF(text" encoded: 0x%lX\n", block); \
+      option += coap_serialize_int_option(number, current_number, option, block); \
+      current_number = number; \
+    }
+
+/* To store error code and human-readable payload */
+extern const char *coap_error_message;
+
+uint16_t coap_get_mid(void);
+
+void coap_init_message(void *packet, coap_message_type_t type, uint8_t code, uint16_t mid);
+size_t coap_serialize_get_size(void *packet);
+size_t coap_serialize_message(void *packet, uint8_t *buffer);
+coap_status_t coap_parse_message(void *request, uint8_t *data, uint16_t data_len);
+void coap_free_header(void *packet);
+
+char * coap_get_multi_option_as_string(multi_option_t * option);
+void coap_add_multi_option(multi_option_t **dst, uint8_t *option, size_t option_len, uint8_t is_static);
+void free_multi_option(multi_option_t *dst);
+
+int coap_get_query_variable(void *packet, const char *name, const char **output);
+int coap_get_post_variable(void *packet, const char *name, const char **output);
+
+/*-----------------------------------------------------------------------------------*/
+
+int coap_set_status_code(void *packet, unsigned int code);
+
+unsigned int coap_get_header_content_type(void *packet);
+int coap_set_header_content_type(void *packet, unsigned int content_type);
+
+int coap_get_header_accept(void *packet, const uint16_t **accept);
+int coap_set_header_accept(void *packet, uint16_t accept);
+
+int coap_get_header_max_age(void *packet, uint32_t *age);
+int coap_set_header_max_age(void *packet, uint32_t age);
+
+int coap_get_header_etag(void *packet, const uint8_t **etag);
+int coap_set_header_etag(void *packet, const uint8_t *etag, size_t etag_len);
+
+int coap_get_header_if_match(void *packet, const uint8_t **etag);
+int coap_set_header_if_match(void *packet, const uint8_t *etag, size_t etag_len);
+
+int coap_get_header_if_none_match(void *packet);
+int coap_set_header_if_none_match(void *packet);
+
+int coap_get_header_token(void *packet, const uint8_t **token);
+int coap_set_header_token(void *packet, const uint8_t *token, size_t token_len);
+
+int coap_get_header_proxy_uri(void *packet, const char **uri); /* In-place string might not be 0-terminated. */
+int coap_set_header_proxy_uri(void *packet, const char *uri);
+
+int coap_get_header_uri_host(void *packet, const char **host); /* In-place string might not be 0-terminated. */
+int coap_set_header_uri_host(void *packet, const char *host);
+
+int coap_get_header_uri_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */
+int coap_set_header_uri_path(void *packet, const char *path);
+int coap_set_header_uri_path_segment(void *packet, const char *path);
+
+int coap_get_header_uri_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */
+int coap_set_header_uri_query(void *packet, const char *query);
+
+int coap_get_header_location_path(void *packet, const char **path); /* In-place string might not be 0-terminated. */
+int coap_set_header_location_path(void *packet, const char *path); /* Also splits optional query into Location-Query option. */
+
+int coap_get_header_location_query(void *packet, const char **query); /* In-place string might not be 0-terminated. */
+int coap_set_header_location_query(void *packet, char *query);
+
+int coap_get_header_observe(void *packet, uint32_t *observe);
+int coap_set_header_observe(void *packet, uint32_t observe);
+
+int coap_get_header_block2(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
+int coap_set_header_block2(void *packet, uint32_t num, uint8_t more, uint16_t size);
+
+int coap_get_header_block1(void *packet, uint32_t *num, uint8_t *more, uint16_t *size, uint32_t *offset);
+int coap_set_header_block1(void *packet, uint32_t num, uint8_t more, uint16_t size);
+
+int coap_get_header_size(void *packet, uint32_t *size);
+int coap_set_header_size(void *packet, uint32_t size);
+
+int coap_get_payload(void *packet, const uint8_t **payload);
+int coap_set_payload(void *packet, const void *payload, size_t length);
+
+#endif /* COAP_13_H_ */
diff -r 000000000000 -r 3f48af28ebcd core/internals.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/internals.h	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,347 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Fabien Fleutot - Please refer to git log
+ *    Toby Jaffey - 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>
+
+*/
+
+#ifndef _LWM2M_INTERNALS_H_
+#define _LWM2M_INTERNALS_H_
+
+#include "liblwm2m.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "er-coap-13/er-coap-13.h"
+
+#ifdef LWM2M_WITH_LOGS
+#include <inttypes.h>
+#define LOG(STR) lwm2m_printf("[%s:%d] " STR "\r\n", __func__ , __LINE__)
+#define LOG_ARG(FMT, ...) lwm2m_printf("[%s:%d] " FMT "\r\n", __func__ , __LINE__ , __VA_ARGS__)
+#define LOG_URI(URI)                                                                \
+{                                                                                   \
+    if ((URI) == NULL) lwm2m_printf("[%s:%d] NULL\r\n", __func__ , __LINE__);     \
+    else                                                                            \
+    {                                                                               \
+        lwm2m_printf("[%s:%d] /%d", __func__ , __LINE__ , (URI)->objectId);       \
+        if (LWM2M_URI_IS_SET_INSTANCE(URI)) lwm2m_printf("/%d", (URI)->instanceId); \
+        if (LWM2M_URI_IS_SET_RESOURCE(URI)) lwm2m_printf("/%d", (URI)->resourceId); \
+        lwm2m_printf("\r\n");                                                       \
+    }                                                                               \
+}
+#define STR_STATUS(S)                                           \
+((S) == STATE_DEREGISTERED ? "STATE_DEREGISTERED" :             \
+((S) == STATE_REG_PENDING ? "STATE_REG_PENDING" :               \
+((S) == STATE_REGISTERED ? "STATE_REGISTERED" :                 \
+((S) == STATE_REG_FAILED ? "STATE_REG_FAILED" :                 \
+((S) == STATE_REG_UPDATE_PENDING ? "STATE_REG_UPDATE_PENDING" : \
+((S) == STATE_REG_UPDATE_NEEDED ? "STATE_REG_UPDATE_NEEDED" :   \
+((S) == STATE_REG_FULL_UPDATE_NEEDED ? "STATE_REG_FULL_UPDATE_NEEDED" :   \
+((S) == STATE_DEREG_PENDING ? "STATE_DEREG_PENDING" :           \
+((S) == STATE_BS_HOLD_OFF ? "STATE_BS_HOLD_OFF" :               \
+((S) == STATE_BS_INITIATED ? "STATE_BS_INITIATED" :             \
+((S) == STATE_BS_PENDING ? "STATE_BS_PENDING" :                 \
+((S) == STATE_BS_FINISHED ? "STATE_BS_FINISHED" :               \
+((S) == STATE_BS_FINISHING ? "STATE_BS_FINISHING" :             \
+((S) == STATE_BS_FAILING ? "STATE_BS_FAILING" :                 \
+((S) == STATE_BS_FAILED ? "STATE_BS_FAILED" :                   \
+"Unknown")))))))))))))))
+#define STR_MEDIA_TYPE(M)                                \
+((M) == LWM2M_CONTENT_TEXT ? "LWM2M_CONTENT_TEXT" :      \
+((M) == LWM2M_CONTENT_LINK ? "LWM2M_CONTENT_LINK" :      \
+((M) == LWM2M_CONTENT_OPAQUE ? "LWM2M_CONTENT_OPAQUE" :  \
+((M) == LWM2M_CONTENT_TLV ? "LWM2M_CONTENT_TLV" :        \
+((M) == LWM2M_CONTENT_JSON ? "LWM2M_CONTENT_JSON" :      \
+"Unknown")))))
+#define STR_STATE(S)                                \
+((S) == STATE_INITIAL ? "STATE_INITIAL" :      \
+((S) == STATE_BOOTSTRAP_REQUIRED ? "STATE_BOOTSTRAP_REQUIRED" :      \
+((S) == STATE_BOOTSTRAPPING ? "STATE_BOOTSTRAPPING" :  \
+((S) == STATE_REGISTER_REQUIRED ? "STATE_REGISTER_REQUIRED" :        \
+((S) == STATE_REGISTERING ? "STATE_REGISTERING" :      \
+((S) == STATE_READY ? "STATE_READY" :      \
+"Unknown"))))))
+#else
+#define LOG_ARG(FMT, ...)
+#define LOG(STR)
+#define LOG_URI(URI)
+#endif
+
+#define LWM2M_DEFAULT_LIFETIME  86400
+
+#ifdef LWM2M_SUPPORT_JSON
+#define REG_LWM2M_RESOURCE_TYPE     ">;rt=\"oma.lwm2m\";ct=11543,"
+#define REG_LWM2M_RESOURCE_TYPE_LEN 25
+#else
+#define REG_LWM2M_RESOURCE_TYPE     ">;rt=\"oma.lwm2m\","
+#define REG_LWM2M_RESOURCE_TYPE_LEN 17
+#endif
+#define REG_START           "<"
+#define REG_DEFAULT_PATH    "/"
+
+#define REG_OBJECT_MIN_LEN  5   // "</n>,"
+#define REG_PATH_END        ">,"
+#define REG_PATH_SEPARATOR  "/"
+
+#define REG_OBJECT_PATH             "<%s/%hu>,"
+#define REG_OBJECT_INSTANCE_PATH    "<%s/%hu/%hu>,"
+
+#define URI_REGISTRATION_SEGMENT        "rd"
+#define URI_REGISTRATION_SEGMENT_LEN    2
+#define URI_BOOTSTRAP_SEGMENT           "bs"
+#define URI_BOOTSTRAP_SEGMENT_LEN       2
+
+#define QUERY_STARTER       "?"
+#define QUERY_NAME          "ep="
+#define QUERY_NAME_LEN      3       // strlen("ep=")
+#define QUERY_SMS           "sms="
+#define QUERY_SMS_LEN       4
+#define QUERY_LIFETIME      "lt="
+#define QUERY_LIFETIME_LEN  3
+#define QUERY_VERSION       "lwm2m="
+#define QUERY_VERSION_LEN   6
+#define QUERY_BINDING       "b="
+#define QUERY_BINDING_LEN   2
+#define QUERY_DELIMITER     "&"
+
+#define LWM2M_VERSION      "1.0"
+#define LWM2M_VERSION_LEN  3
+
+#define QUERY_VERSION_FULL      QUERY_VERSION LWM2M_VERSION
+#define QUERY_VERSION_FULL_LEN  QUERY_VERSION_LEN+LWM2M_VERSION_LEN
+
+#define REG_URI_START       '<'
+#define REG_URI_END         '>'
+#define REG_DELIMITER       ','
+#define REG_ATTR_SEPARATOR  ';'
+#define REG_ATTR_EQUALS     '='
+#define REG_ATTR_TYPE_KEY           "rt"
+#define REG_ATTR_TYPE_KEY_LEN       2
+#define REG_ATTR_TYPE_VALUE         "\"oma.lwm2m\""
+#define REG_ATTR_TYPE_VALUE_LEN     11
+#define REG_ATTR_CONTENT_KEY        "ct"
+#define REG_ATTR_CONTENT_KEY_LEN    2
+#define REG_ATTR_CONTENT_JSON       "11543"   // Temporary value
+#define REG_ATTR_CONTENT_JSON_LEN   5
+
+#define ATTR_SERVER_ID_STR       "ep="
+#define ATTR_SERVER_ID_LEN       3
+#define ATTR_MIN_PERIOD_STR      "pmin="
+#define ATTR_MIN_PERIOD_LEN      5
+#define ATTR_MAX_PERIOD_STR      "pmax="
+#define ATTR_MAX_PERIOD_LEN      5
+#define ATTR_GREATER_THAN_STR    "gt="
+#define ATTR_GREATER_THAN_LEN    3
+#define ATTR_LESS_THAN_STR       "lt="
+#define ATTR_LESS_THAN_LEN       3
+#define ATTR_STEP_STR            "stp="
+#define ATTR_STEP_LEN            4
+#define ATTR_DIMENSION_STR       "dim="
+#define ATTR_DIMENSION_LEN       4
+
+#define URI_MAX_STRING_LEN    18      // /65535/65535/65535
+#define _PRV_64BIT_BUFFER_SIZE 8
+
+#define LINK_ITEM_START             "<"
+#define LINK_ITEM_START_SIZE        1
+#define LINK_ITEM_END               ">,"
+#define LINK_ITEM_END_SIZE          2
+#define LINK_ITEM_DIM_START         ">;dim="
+#define LINK_ITEM_DIM_START_SIZE    6
+#define LINK_ITEM_ATTR_END          ","
+#define LINK_ITEM_ATTR_END_SIZE     1
+#define LINK_URI_SEPARATOR          "/"
+#define LINK_URI_SEPARATOR_SIZE     1
+#define LINK_ATTR_SEPARATOR         ";"
+#define LINK_ATTR_SEPARATOR_SIZE    1
+
+#define ATTR_FLAG_NUMERIC (uint8_t)(LWM2M_ATTR_FLAG_LESS_THAN | LWM2M_ATTR_FLAG_GREATER_THAN | LWM2M_ATTR_FLAG_STEP)
+
+#define LWM2M_URI_FLAG_DM           (uint8_t)0x00
+#define LWM2M_URI_FLAG_DELETE_ALL   (uint8_t)0x10
+#define LWM2M_URI_FLAG_REGISTRATION (uint8_t)0x20
+#define LWM2M_URI_FLAG_BOOTSTRAP    (uint8_t)0x40
+
+#define LWM2M_URI_MASK_TYPE (uint8_t)0x70
+#define LWM2M_URI_MASK_ID   (uint8_t)0x07
+
+typedef struct
+{
+    uint16_t clientID;
+    lwm2m_uri_t uri;
+    lwm2m_result_callback_t callback;
+    void * userData;
+} dm_data_t;
+
+typedef enum
+{
+    URI_DEPTH_OBJECT,
+    URI_DEPTH_OBJECT_INSTANCE,
+    URI_DEPTH_RESOURCE,
+    URI_DEPTH_RESOURCE_INSTANCE
+} uri_depth_t;
+
+#ifdef LWM2M_BOOTSTRAP_SERVER_MODE
+typedef struct
+{
+    bool        isUri;
+    lwm2m_uri_t uri;
+    lwm2m_bootstrap_callback_t callback;
+    void *      userData;
+} bs_data_t;
+#endif
+
+// defined in uri.c
+lwm2m_uri_t * uri_decode(char * altPath, multi_option_t *uriPath);
+int uri_getNumber(uint8_t * uriString, size_t uriLength);
+int uri_toString(lwm2m_uri_t * uriP, uint8_t * buffer, size_t bufferLen, uri_depth_t * depthP);
+
+// defined in objects.c
+coap_status_t object_readData(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, int * sizeP, lwm2m_data_t ** dataP);
+coap_status_t object_read(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t * formatP, uint8_t ** bufferP, size_t * lengthP);
+coap_status_t object_write(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length);
+coap_status_t object_create(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length);
+coap_status_t object_execute(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, uint8_t * buffer, size_t length);
+coap_status_t object_delete(lwm2m_context_t * contextP, lwm2m_uri_t * uriP);
+coap_status_t object_discover(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, uint8_t ** bufferP, size_t * lengthP);
+uint8_t object_checkReadable(lwm2m_context_t * contextP, lwm2m_uri_t * uriP);
+uint8_t object_checkNumeric(lwm2m_context_t * contextP, lwm2m_uri_t * uriP);
+bool object_isInstanceNew(lwm2m_context_t * contextP, uint16_t objectId, uint16_t instanceId);
+int object_getRegisterPayload(lwm2m_context_t * contextP, uint8_t * buffer, size_t length);
+int object_getServers(lwm2m_context_t * contextP);
+coap_status_t object_createInstance(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_data_t * dataP);
+coap_status_t object_writeInstance(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_data_t * dataP);
+
+// defined in transaction.c
+lwm2m_transaction_t * transaction_new(void * sessionH, coap_method_t method, char * altPath, lwm2m_uri_t * uriP, uint16_t mID, uint8_t token_len, uint8_t* token);
+int transaction_send(lwm2m_context_t * contextP, lwm2m_transaction_t * transacP);
+void transaction_free(lwm2m_transaction_t * transacP);
+void transaction_remove(lwm2m_context_t * contextP, lwm2m_transaction_t * transacP);
+bool transaction_handleResponse(lwm2m_context_t * contextP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response);
+void transaction_step(lwm2m_context_t * contextP, time_t currentTime, time_t * timeoutP);
+
+// defined in management.c
+coap_status_t dm_handleRequest(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, coap_packet_t * message, coap_packet_t * response);
+
+// defined in observe.c
+coap_status_t observe_handleRequest(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, int size, lwm2m_data_t * dataP, coap_packet_t * message, coap_packet_t * response);
+void observe_cancel(lwm2m_context_t * contextP, uint16_t mid, void * fromSessionH);
+coap_status_t observe_setParameters(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, lwm2m_attributes_t * attrP);
+void observe_step(lwm2m_context_t * contextP, time_t currentTime, time_t * timeoutP);
+bool observe_handleNotify(lwm2m_context_t * contextP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response);
+void observe_remove(lwm2m_observation_t * observationP);
+lwm2m_observed_t * observe_findByUri(lwm2m_context_t * contextP, lwm2m_uri_t * uriP);
+
+// defined in registration.c
+coap_status_t registration_handleRequest(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response);
+void registration_deregister(lwm2m_context_t * contextP, lwm2m_server_t * serverP);
+void registration_freeClient(lwm2m_client_t * clientP);
+uint8_t registration_start(lwm2m_context_t * contextP);
+void registration_step(lwm2m_context_t * contextP, time_t currentTime, time_t * timeoutP);
+lwm2m_status_t registration_getStatus(lwm2m_context_t * contextP);
+
+// defined in packet.c
+coap_status_t message_send(lwm2m_context_t * contextP, coap_packet_t * message, void * sessionH);
+
+// defined in bootstrap.c
+void bootstrap_step(lwm2m_context_t * contextP, uint32_t currentTime, time_t* timeoutP);
+coap_status_t bootstrap_handleCommand(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, coap_packet_t * message, coap_packet_t * response);
+coap_status_t bootstrap_handleDeleteAll(lwm2m_context_t * context, void * fromSessionH);
+coap_status_t bootstrap_handleFinish(lwm2m_context_t * context, void * fromSessionH);
+uint8_t bootstrap_handleRequest(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, void * fromSessionH, coap_packet_t * message, coap_packet_t * response);
+void bootstrap_start(lwm2m_context_t * contextP);
+lwm2m_status_t bootstrap_getStatus(lwm2m_context_t * contextP);
+
+// defined in tlv.c
+int tlv_parse(uint8_t * buffer, size_t bufferLen, lwm2m_data_t ** dataP);
+int tlv_serialize(bool isResourceInstance, int size, lwm2m_data_t * dataP, uint8_t ** bufferP);
+
+// defined in json.c
+#ifdef LWM2M_SUPPORT_JSON
+int json_parse(lwm2m_uri_t * uriP, uint8_t * buffer, size_t bufferLen, lwm2m_data_t ** dataP);
+int json_serialize(lwm2m_uri_t * uriP, int size, lwm2m_data_t * tlvP, uint8_t ** bufferP);
+#endif
+
+// defined in discover.c
+int discover_serialize(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_server_t * serverP, int size, lwm2m_data_t * dataP, uint8_t ** bufferP);
+
+// defined in block1.c
+coap_status_t coap_block1_handler(lwm2m_block1_data_t ** block1Data, uint16_t mid, uint8_t * buffer, size_t length, uint16_t blockSize, uint32_t blockNum, bool blockMore, uint8_t ** outputBuffer, size_t * outputLength);
+void free_block1_buffer(lwm2m_block1_data_t * block1Data);
+
+// defined in utils.c
+lwm2m_data_type_t utils_depthToDatatype(uri_depth_t depth);
+lwm2m_binding_t utils_stringToBinding(uint8_t *buffer, size_t length);
+lwm2m_media_type_t utils_convertMediaType(coap_content_type_t type);
+int utils_isAltPathValid(const char * altPath);
+int utils_stringCopy(char * buffer, size_t length, const char * str);
+int utils_intCopy(char * buffer, size_t length, int32_t value);
+size_t utils_intToText(int64_t data, uint8_t * string, size_t length);
+size_t utils_floatToText(double data, uint8_t * string, size_t length);
+int utils_plainTextToInt64(uint8_t * buffer, int length, int64_t * dataP);
+int utils_plainTextToFloat64(uint8_t * buffer, int length, double * dataP);
+size_t utils_int64ToPlainText(int64_t data, uint8_t ** bufferP);
+size_t utils_float64ToPlainText(double data, uint8_t ** bufferP);
+size_t utils_boolToPlainText(bool data, uint8_t ** bufferP);
+void utils_copyValue(void * dst, const void * src, size_t len);
+int utils_opaqueToInt(const uint8_t * buffer, size_t buffer_len, int64_t * dataP);
+int utils_opaqueToFloat(const uint8_t * buffer, size_t buffer_len, double * dataP);
+size_t utils_encodeInt(int64_t data, uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE]);
+size_t utils_encodeFloat(double data, uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE]);
+size_t utils_base64ToOpaque(uint8_t * dataP, size_t dataLen, uint8_t ** bufferP);
+size_t utils_opaqueToBase64(uint8_t * dataP, size_t dataLen, uint8_t ** bufferP);
+size_t utils_base64Encode(uint8_t * dataP, size_t dataLen, uint8_t * bufferP, size_t bufferLen);
+#ifdef LWM2M_CLIENT_MODE
+lwm2m_server_t * utils_findServer(lwm2m_context_t * contextP, void * fromSessionH);
+lwm2m_server_t * utils_findBootstrapServer(lwm2m_context_t * contextP, void * fromSessionH);
+#endif
+
+#endif
diff -r 000000000000 -r 3f48af28ebcd core/json.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/json.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,1436 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2015 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
+ *
+ *******************************************************************************/
+
+
+#include "internals.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+
+#ifdef LWM2M_SUPPORT_JSON
+
+#define PRV_JSON_BUFFER_SIZE 1024
+
+#define JSON_MIN_ARRAY_LEN      21      // e":[{"n":"N","v":X}]}
+#define JSON_MIN_BASE_LEN        7      // n":"N",
+#define JSON_ITEM_MAX_SIZE      36      // with ten characters for value
+#define JSON_MIN_BX_LEN          5      // bt":1
+
+#define JSON_FALSE_STRING  "false"
+#define JSON_TRUE_STRING   "true"
+
+#define JSON_RES_ITEM_URI           "{\"n\":\""
+#define JSON_RES_ITEM_URI_SIZE      6
+#define JSON_ITEM_BOOL_TRUE         "\",\"bv\":true},"
+#define JSON_ITEM_BOOL_TRUE_SIZE    13
+#define JSON_ITEM_BOOL_FALSE        "\",\"bv\":false},"
+#define JSON_ITEM_BOOL_FALSE_SIZE   14
+#define JSON_ITEM_NUM               "\",\"v\":"
+#define JSON_ITEM_NUM_SIZE          6
+#define JSON_ITEM_NUM_END           "},"
+#define JSON_ITEM_NUM_END_SIZE      2
+#define JSON_ITEM_STRING_BEGIN      "\",\"sv\":\""
+#define JSON_ITEM_STRING_BEGIN_SIZE 8
+#define JSON_ITEM_STRING_END        "\"},"
+#define JSON_ITEM_STRING_END_SIZE   3
+
+#define JSON_BN_HEADER_1        "{\"bn\":\""
+#define JSON_BN_HEADER_1_SIZE   7
+#define JSON_BN_HEADER_2        "\",\"e\":["
+#define JSON_BN_HEADER_2_SIZE   7
+#define JSON_HEADER             "{\"e\":["
+#define JSON_HEADER_SIZE        6
+#define JSON_FOOTER             "]}"
+#define JSON_FOOTER_SIZE        2
+
+
+#define _GO_TO_NEXT_CHAR(I,B,L)         \
+    {                                   \
+        I++;                            \
+        I += prv_skipSpace(B+I, L-I);   \
+        if (I == L) goto error;         \
+    }
+
+typedef enum
+{
+    _STEP_START,
+    _STEP_TOKEN,
+    _STEP_ANY_SEPARATOR,
+    _STEP_SEPARATOR,
+    _STEP_QUOTED_VALUE,
+    _STEP_VALUE,
+    _STEP_DONE
+} _itemState;
+
+typedef enum
+{
+    _TYPE_UNSET,
+    _TYPE_FALSE,
+    _TYPE_TRUE,
+    _TYPE_FLOAT,
+    _TYPE_STRING
+} _type;
+
+typedef struct
+{
+    uint16_t    ids[4];
+    _type       type;
+    uint8_t *   value;
+    size_t      valueLen;
+} _record_t;
+
+static int prv_isReserved(char sign)
+{
+    if (sign == '['
+     || sign == '{'
+     || sign == ']'
+     || sign == '}'
+     || sign == ':'
+     || sign == ','
+     || sign == '"')
+    {
+        return 1;
+    }
+
+    return 0;
+}
+
+static int prv_isWhiteSpace(uint8_t sign)
+{
+    if (sign == 0x20
+     || sign == 0x09
+     || sign == 0x0A
+     || sign == 0x0D)
+    {
+        return 1;
+    }
+
+    return 0;
+}
+
+static size_t prv_skipSpace(uint8_t * buffer,
+                            size_t bufferLen)
+{
+    size_t i;
+
+    i = 0;
+    while ((i < bufferLen)
+        && prv_isWhiteSpace(buffer[i]))
+    {
+        i++;
+    }
+
+    return i;
+}
+
+static int prv_split(uint8_t * buffer,
+                     size_t bufferLen,
+                     int * tokenStartP,
+                     int * tokenLenP,
+                     int * valueStartP,
+                     int * valueLenP)
+{
+    size_t index;
+    _itemState step;
+
+    index = 0;
+    step = _STEP_START;
+
+    index = prv_skipSpace(buffer + index, bufferLen - index);
+    if (index == bufferLen) return -1;
+
+    while ((index < bufferLen)
+        && (buffer[index] != ','))
+    {
+        switch (step)
+        {
+        case _STEP_START:
+            if (buffer[index] != '"') return -1;
+            *tokenStartP = index+1;
+            step = _STEP_TOKEN;
+            break;
+
+        case _STEP_TOKEN:
+            if (buffer[index] == '"')
+            {
+                *tokenLenP = index - *tokenStartP;
+                step = _STEP_ANY_SEPARATOR;
+            }
+            break;
+
+        case _STEP_ANY_SEPARATOR:
+            if (buffer[index] != ':') return -1;
+            step = _STEP_SEPARATOR;
+            break;
+
+        case _STEP_SEPARATOR:
+            if (buffer[index] == '"')
+            {
+                *valueStartP = index;
+                step = _STEP_QUOTED_VALUE;
+            } else if (!prv_isReserved(buffer[index]))
+            {
+                *valueStartP = index;
+                step = _STEP_VALUE;
+            } else
+            {
+                return -1;
+            }
+            break;
+
+        case _STEP_QUOTED_VALUE:
+            if (buffer[index] == '"' && buffer[index-1] != '\\' )
+            {
+                *valueLenP = index - *valueStartP + 1;
+                step = _STEP_DONE;
+            }
+            break;
+
+        case _STEP_VALUE:
+            if (prv_isWhiteSpace(buffer[index]))
+            {
+                *valueLenP = index - *valueStartP;
+                step = _STEP_DONE;
+            }
+            break;
+
+        case _STEP_DONE:
+        default:
+            return -1;
+        }
+
+        index++;
+        if (step == _STEP_START
+         || step == _STEP_ANY_SEPARATOR
+         || step == _STEP_SEPARATOR
+         || step == _STEP_DONE)
+        {
+            index += prv_skipSpace(buffer + index, bufferLen - index);
+        }
+    }
+
+    if (step == _STEP_VALUE)
+    {
+        *valueLenP = index - *valueStartP;
+        step = _STEP_DONE;
+    }
+
+    if (step != _STEP_DONE) return -1;
+
+    return (int)index;
+}
+
+static int prv_countItems(uint8_t * buffer,
+                          size_t bufferLen)
+{
+    int count;
+    size_t index;
+    int in;
+
+    count = 0;
+    index = 0;
+    in = 0;
+
+    while (index < bufferLen)
+    {
+        if (in == 0)
+        {
+            if (buffer[index] != '{') return -1;
+            in = 1;
+            _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+        }
+        else
+        {
+            if (buffer[index] == '{') return -1;
+            if (buffer[index] == '}')
+            {
+                in = 0;
+                count++;
+                _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+                if (buffer[index] == ']')
+                {
+                    break;
+                }
+                if (buffer[index] != ',') return -1;
+                _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+            }
+            else
+            {
+                _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+            }
+        }
+    }
+    if (in == 1) return -1;
+
+    return count;
+
+error:
+    return -1;
+}
+
+static int prv_parseItem(uint8_t * buffer,
+                         size_t bufferLen,
+                         _record_t * recordP)
+{
+    size_t index;
+
+    memset(recordP->ids, 0xFF, 4*sizeof(uint16_t));
+    recordP->type = _TYPE_UNSET;
+    recordP->value = NULL;
+    recordP->valueLen = 0;
+
+    index = 0;
+    do
+    {
+        int tokenStart;
+        int tokenLen;
+        int valueStart;
+        int valueLen;
+        int next;
+
+        next = prv_split(buffer+index, bufferLen-index, &tokenStart, &tokenLen, &valueStart, &valueLen);
+        if (next < 0) return -1;
+
+        switch (tokenLen)
+        {
+        case 1:
+        {
+            switch (buffer[index+tokenStart])
+            {
+            case 'n':
+            {
+                int i;
+                int j;
+
+                if (recordP->ids[0] != LWM2M_MAX_ID) return -1;
+
+                // Check for " around URI
+                if (valueLen < 3
+                 || buffer[index+valueStart] != '"'
+                 || buffer[index+valueStart+valueLen-1] != '"')
+                {
+                    return -1;
+                }
+                // Ignore starting /
+                if (buffer[index + valueStart + 1] == '/')
+                {
+                    if (valueLen < 4)
+                    {
+                        return -1;
+                    }
+                    valueStart += 1;
+                    valueLen -= 1;
+                }
+                i = 0;
+                j = 0;
+                do {
+                    uint32_t readId;
+
+                    readId = 0;
+                    i++;
+                    while (i < valueLen-1 && buffer[index+valueStart+i] != '/')
+                    {
+                        if (buffer[index+valueStart+i] < '0'
+                         || buffer[index+valueStart+i] > '9')
+                        {
+                            return -1;
+                        }
+                        readId *= 10;
+                        readId += buffer[index+valueStart+i] - '0';
+                        if (readId > LWM2M_MAX_ID) return -1;
+                        i++;
+                    }
+                    recordP->ids[j] = readId;
+                    j++;
+                } while (i < valueLen-1 && j < 4 && buffer[index+valueStart+i] == '/');
+                if (i < valueLen-1 ) return -1;
+            }
+            break;
+
+            case 'v':
+                if (recordP->type != _TYPE_UNSET) return -1;
+                recordP->type = _TYPE_FLOAT;
+                recordP->value = buffer + index + valueStart;
+                recordP->valueLen = valueLen;
+                break;
+
+            case 't':
+                // TODO: support time
+                break;
+
+            default:
+                return -1;
+            }
+        }
+        break;
+
+        case 2:
+        {
+            // "bv", "ov", or "sv"
+            if (buffer[index+tokenStart+1] != 'v') return -1;
+            switch (buffer[index+tokenStart])
+            {
+            case 'b':
+                if (recordP->type != _TYPE_UNSET) return -1;
+                if (0 == lwm2m_strncmp(JSON_TRUE_STRING, (char *)buffer + index + valueStart, valueLen))
+                {
+                    recordP->type = _TYPE_TRUE;
+                }
+                else if (0 == lwm2m_strncmp(JSON_FALSE_STRING, (char *)buffer + index + valueStart, valueLen))
+                {
+                    recordP->type = _TYPE_FALSE;
+                }
+                else
+                {
+                    return -1;
+                }
+                break;
+
+            case 'o':
+                if (recordP->type != _TYPE_UNSET) return -1;
+                // TODO: support object link
+                break;
+
+            case 's':
+                if (recordP->type != _TYPE_UNSET) return -1;
+                // Check for " around value
+                if (valueLen < 2
+                 || buffer[index+valueStart] != '"'
+                 || buffer[index+valueStart+valueLen-1] != '"')
+                {
+                    return -1;
+                }
+                recordP->type = _TYPE_STRING;
+                recordP->value = buffer + index + valueStart + 1;
+                recordP->valueLen = valueLen - 2;
+                break;
+
+            default:
+                return -1;
+            }
+        }
+        break;
+
+        default:
+            return -1;
+        }
+
+        index += next + 1;
+    } while (index < bufferLen);
+
+    return 0;
+}
+
+static bool prv_convertValue(_record_t * recordP,
+                             lwm2m_data_t * targetP)
+{
+    switch (recordP->type)
+    {
+    case _TYPE_FALSE:
+        lwm2m_data_encode_bool(false, targetP);
+        break;
+    case _TYPE_TRUE:
+        lwm2m_data_encode_bool(true, targetP);
+        break;
+    case _TYPE_FLOAT:
+    {
+        size_t i;
+
+        i = 0;
+        while (i < recordP->valueLen
+            && recordP->value[i] != '.')
+        {
+            i++;
+        }
+        if (i == recordP->valueLen)
+        {
+            int64_t value;
+
+            if ( 1 != utils_plainTextToInt64(recordP->value,
+                                             recordP->valueLen,
+                                             &value))
+            {
+                return false;
+            }
+
+            lwm2m_data_encode_int(value, targetP);
+        }
+        else
+        {
+            double value;
+
+            if ( 1 != utils_plainTextToFloat64(recordP->value,
+                                               recordP->valueLen,
+                                               &value))
+            {
+                return false;
+            }
+
+            lwm2m_data_encode_float(value, targetP);
+        }
+    }
+    break;
+
+    case _TYPE_STRING:
+        lwm2m_data_encode_opaque(recordP->value, recordP->valueLen, targetP);
+        targetP->type = LWM2M_TYPE_STRING;
+        break;
+
+    case _TYPE_UNSET:
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+static lwm2m_data_t * prv_findDataItem(lwm2m_data_t * listP,
+                                       int count,
+                                       uint16_t id)
+{
+    int i;
+
+    i = 0;
+    while (i < count)
+    {
+        if (listP[i].type != LWM2M_TYPE_UNDEFINED && listP[i].id == id)
+        {
+            return listP + i;
+        }
+        i++;
+    }
+
+    return NULL;
+}
+
+static uri_depth_t prv_decreaseLevel(uri_depth_t level)
+{
+    switch(level)
+    {
+    case URI_DEPTH_OBJECT:
+        return URI_DEPTH_OBJECT_INSTANCE;
+    case URI_DEPTH_OBJECT_INSTANCE:
+        return URI_DEPTH_RESOURCE;
+    case URI_DEPTH_RESOURCE:
+        return URI_DEPTH_RESOURCE_INSTANCE;
+    case URI_DEPTH_RESOURCE_INSTANCE:
+        return URI_DEPTH_RESOURCE_INSTANCE;
+    default:
+        return URI_DEPTH_RESOURCE;
+    }
+}
+
+static lwm2m_data_t * prv_extendData(lwm2m_data_t * parentP)
+{
+    lwm2m_data_t * newP;
+
+    newP = lwm2m_data_new(parentP->value.asChildren.count + 1);
+    if (newP == NULL) return NULL;
+    if (parentP->value.asChildren.array != NULL)
+    {
+        memcpy(newP, parentP->value.asChildren.array, parentP->value.asChildren.count * sizeof(lwm2m_data_t));
+        lwm2m_free(parentP->value.asChildren.array);     // do not use lwm2m_data_free() to keep pointed values
+    }
+    parentP->value.asChildren.array = newP;
+    parentP->value.asChildren.count += 1;
+
+    return newP + parentP->value.asChildren.count - 1;
+}
+
+static int prv_convertRecord(lwm2m_uri_t * uriP,
+                             _record_t * recordArray,
+                             int count,
+                             lwm2m_data_t ** dataP)
+{
+    int index;
+    int freeIndex;
+    lwm2m_data_t * rootP;
+    int size;
+    uri_depth_t rootLevel;
+
+    if (uriP == NULL)
+    {
+        size = count;
+        *dataP = lwm2m_data_new(count);
+        if (NULL == *dataP) return -1;
+        rootLevel = URI_DEPTH_OBJECT;
+        rootP = *dataP;
+    }
+    else
+    {
+        lwm2m_data_t * parentP;
+        size = 1;
+
+        *dataP = lwm2m_data_new(1);
+        if (NULL == *dataP) return -1;
+        (*dataP)->type = LWM2M_TYPE_OBJECT;
+        (*dataP)->id = uriP->objectId;
+        rootLevel = URI_DEPTH_OBJECT_INSTANCE;
+        parentP = *dataP;
+        if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+        {
+            parentP->value.asChildren.count = 1;
+            parentP->value.asChildren.array = lwm2m_data_new(1);
+            if (NULL == parentP->value.asChildren.array) goto error;
+            parentP = parentP->value.asChildren.array;
+            parentP->type = LWM2M_TYPE_OBJECT_INSTANCE;
+            parentP->id = uriP->instanceId;
+            rootLevel = URI_DEPTH_RESOURCE;
+            if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+            {
+                parentP->value.asChildren.count = 1;
+                parentP->value.asChildren.array = lwm2m_data_new(1);
+                if (NULL == parentP->value.asChildren.array) goto error;
+                parentP = parentP->value.asChildren.array;
+                parentP->type = LWM2M_TYPE_UNDEFINED;
+                parentP->id = uriP->resourceId;
+                rootLevel = URI_DEPTH_RESOURCE_INSTANCE;
+            }
+        }
+        parentP->value.asChildren.count = count;
+        parentP->value.asChildren.array = lwm2m_data_new(count);
+        if (NULL == parentP->value.asChildren.array) goto error;
+        rootP = parentP->value.asChildren.array;
+    }
+
+    freeIndex = 0;
+    for (index = 0 ; index < count ; index++)
+    {
+        lwm2m_data_t * targetP;
+        int resSegmentIndex;
+        int i;
+
+        // check URI depth
+        // resSegmentIndex is set to the resource segment position
+        switch(rootLevel)
+        {
+        case URI_DEPTH_OBJECT:
+            resSegmentIndex = 2;
+            break;
+        case URI_DEPTH_OBJECT_INSTANCE:
+            resSegmentIndex = 1;
+            break;
+        case URI_DEPTH_RESOURCE:
+            resSegmentIndex = 0;
+            break;
+        case URI_DEPTH_RESOURCE_INSTANCE:
+            resSegmentIndex = -1;
+            break;
+        default:
+            goto error;
+        }
+        for (i = 0 ; i <= resSegmentIndex ; i++)
+        {
+            if (recordArray[index].ids[i] == LWM2M_MAX_ID) goto error;
+        }
+        if (resSegmentIndex < 2)
+        {
+            if (recordArray[index].ids[resSegmentIndex + 2] != LWM2M_MAX_ID) goto error;
+        }
+
+        targetP = prv_findDataItem(rootP, count, recordArray[index].ids[0]);
+        if (targetP == NULL)
+        {
+            targetP = rootP + freeIndex;
+            freeIndex++;
+            targetP->id = recordArray[index].ids[0];
+            targetP->type = utils_depthToDatatype(rootLevel);
+        }
+        if (recordArray[index].ids[1] != LWM2M_MAX_ID)
+        {
+            lwm2m_data_t * parentP;
+            uri_depth_t level;
+
+            parentP = targetP;
+            level = prv_decreaseLevel(rootLevel);
+            for (i = 1 ; i <= resSegmentIndex ; i++)
+            {
+                targetP = prv_findDataItem(parentP->value.asChildren.array, parentP->value.asChildren.count, recordArray[index].ids[i]);
+                if (targetP == NULL)
+                {
+                    targetP = prv_extendData(parentP);
+                    if (targetP == NULL) goto error;
+                    targetP->id = recordArray[index].ids[i];
+                    targetP->type = utils_depthToDatatype(level);
+                }
+                level = prv_decreaseLevel(level);
+                parentP = targetP;
+            }
+            if (recordArray[index].ids[resSegmentIndex + 1] != LWM2M_MAX_ID)
+            {
+                targetP->type = LWM2M_TYPE_MULTIPLE_RESOURCE;
+                targetP = prv_extendData(targetP);
+                if (targetP == NULL) goto error;
+                targetP->id = recordArray[index].ids[resSegmentIndex + 1];
+                targetP->type = LWM2M_TYPE_UNDEFINED;
+            }
+        }
+
+        if (true != prv_convertValue(recordArray + index, targetP)) goto error;
+    }
+
+    return size;
+
+error:
+    lwm2m_data_free(size, *dataP);
+    *dataP = NULL;
+
+    return -1;
+}
+
+static int prv_dataStrip(int size,
+                         lwm2m_data_t * dataP,
+                         lwm2m_data_t ** resultP)
+{
+    int i;
+    int j;
+    int realSize;
+
+    realSize = 0;
+    for (i = 0 ; i < size ; i++)
+    {
+        if (dataP[i].type != LWM2M_TYPE_UNDEFINED)
+        {
+            realSize++;
+        }
+    }
+
+    *resultP = lwm2m_data_new(realSize);
+    if (*resultP == NULL) return -1;
+
+    j = 0;
+    for (i = 0 ; i < size ; i++)
+    {
+        if (dataP[i].type != LWM2M_TYPE_UNDEFINED)
+        {
+            memcpy((*resultP) + j, dataP + i, sizeof(lwm2m_data_t));
+
+            if (dataP[i].type == LWM2M_TYPE_OBJECT
+             || dataP[i].type == LWM2M_TYPE_OBJECT_INSTANCE
+             || dataP[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE)
+            {
+                int childLen;
+
+                childLen = prv_dataStrip(dataP[i].value.asChildren.count, dataP[i].value.asChildren.array, &((*resultP)[j].value.asChildren.array));
+                if (childLen <= 0)
+                {
+                    // skip this one
+                    j--;
+                }
+                else
+                {
+                    (*resultP)[j].value.asChildren.count = childLen;
+                }
+            }
+            else
+            {
+                dataP[i].value.asBuffer.buffer = NULL;
+            }
+
+            j++;
+        }
+    }
+
+    return realSize;
+}
+
+int json_parse(lwm2m_uri_t * uriP,
+               uint8_t * buffer,
+               size_t bufferLen,
+               lwm2m_data_t ** dataP)
+{
+    size_t index;
+    int count = 0;
+    bool eFound = false;
+    bool bnFound = false;
+    bool btFound = false;
+    int bnStart;
+    int bnLen;
+    _record_t * recordArray;
+    lwm2m_data_t * parsedP;
+
+    LOG_ARG("bufferLen: %d, buffer: \"%s\"", bufferLen, (char *)buffer);
+    LOG_URI(uriP);
+    *dataP = NULL;
+    recordArray = NULL;
+    parsedP = NULL;
+
+    index = prv_skipSpace(buffer, bufferLen);
+    if (index == bufferLen) return -1;
+
+    if (buffer[index] != '{') return -1;
+    do
+    {
+        _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+        if (buffer[index] != '"') goto error;
+        if (index++ >= bufferLen) goto error;
+        switch (buffer[index])
+        {
+        case 'e':
+        {
+            int recordIndex;
+
+            if (bufferLen-index < JSON_MIN_ARRAY_LEN) goto error;
+            index++;
+            if (buffer[index] != '"') goto error;
+            if (eFound == true) goto error;
+            eFound = true;
+
+            _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+            if (buffer[index] != ':') goto error;
+            _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+            if (buffer[index] != '[') goto error;
+            _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+            count = prv_countItems(buffer + index, bufferLen - index);
+            if (count <= 0) goto error;
+            recordArray = (_record_t*)lwm2m_malloc(count * sizeof(_record_t));
+            if (recordArray == NULL) goto error;
+            // at this point we are sure buffer[index] is '{' and all { and } are matching
+            recordIndex = 0;
+            while (recordIndex < count)
+            {
+                int itemLen;
+
+                if (buffer[index] != '{') goto error;
+                itemLen = 0;
+                while (buffer[index + itemLen] != '}') itemLen++;
+                if (0 != prv_parseItem(buffer + index + 1, itemLen - 1, recordArray + recordIndex))
+                {
+                    goto error;
+                }
+                recordIndex++;
+                index += itemLen;
+                _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+                switch (buffer[index])
+                {
+                case ',':
+                    _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+                    break;
+                case ']':
+                    if (recordIndex == count) break;
+                    // else this is an error
+                default:
+                    goto error;
+                }
+            }
+            if (buffer[index] != ']') goto error;
+        }
+        break;
+
+        case 'b':
+            if (bufferLen-index < JSON_MIN_BX_LEN) goto error;
+            index++;
+            switch (buffer[index])
+            {
+            case 't':
+                index++;
+                if (buffer[index] != '"') goto error;
+                if (btFound == true) goto error;
+                btFound = true;
+
+                // TODO: handle timed values
+                // temp: skip this token
+                while(index < bufferLen && buffer[index] != ',' && buffer[index] != '}') index++;
+                if (index == bufferLen) goto error;
+                index--;
+                // end temp
+                break;
+            case 'n':
+                {
+                    int next;
+                    int tokenStart;
+                    int tokenLen;
+                    int itemLen;
+
+                    index++;
+                    if (buffer[index] != '"') goto error;
+                    if (bnFound == true) goto error;
+                    bnFound = true;
+                    index -= 3;
+                    itemLen = 0;
+                    while (buffer[index + itemLen] != '}'
+                        && buffer[index + itemLen] != ','
+                        && index + itemLen < bufferLen)
+                    {
+                        itemLen++;
+                    }
+                    if (index + itemLen == bufferLen) goto error;
+                    next = prv_split(buffer+index, itemLen, &tokenStart, &tokenLen, &bnStart, &bnLen);
+                    if (next < 0) goto error;
+                    bnStart += index;
+                    index += next - 1;
+                }
+                break;
+            default:
+                goto error;
+            }
+            break;
+
+        default:
+            goto error;
+        }
+
+        _GO_TO_NEXT_CHAR(index, buffer, bufferLen);
+    } while (buffer[index] == ',');
+
+    if (buffer[index] != '}') goto error;
+
+    if (eFound == true)
+    {
+        lwm2m_uri_t baseURI;
+        lwm2m_uri_t * baseUriP;
+        lwm2m_data_t * resultP;
+        int size;
+
+        memset(&baseURI, 0, sizeof(lwm2m_uri_t));
+        if (bnFound == false)
+        {
+            baseUriP = uriP;
+        }
+        else
+        {
+            int res;
+
+            // we ignore the request URI and use the bn one.
+
+            // Check for " around URI
+            if (bnLen < 3
+             || buffer[bnStart] != '"'
+             || buffer[bnStart+bnLen-1] != '"')
+            {
+                goto error;
+            }
+            bnStart += 1;
+            bnLen -= 2;
+
+            if (bnLen == 1)
+            {
+                if (buffer[bnStart] != '/') goto error;
+                baseUriP = NULL;
+            }
+            else
+            {
+                res = lwm2m_stringToUri((char *)buffer + bnStart, bnLen, &baseURI);
+                if (res < 0 || res != bnLen) goto error;
+                baseUriP = &baseURI;
+            }
+        }
+
+        count = prv_convertRecord(baseUriP, recordArray, count, &parsedP);
+        lwm2m_free(recordArray);
+        recordArray = NULL;
+
+        if (count > 0 && uriP != NULL)
+        {
+            if (parsedP->type != LWM2M_TYPE_OBJECT || parsedP->id != uriP->objectId) goto error;
+            if (!LWM2M_URI_IS_SET_INSTANCE(uriP))
+            {
+                size = parsedP->value.asChildren.count;
+                resultP = parsedP->value.asChildren.array;
+            }
+            else
+            {
+                int i;
+
+                resultP = NULL;
+                // be permissive and allow full object JSON when requesting for a single instance
+                for (i = 0 ; i < (int)parsedP->value.asChildren.count && resultP == NULL; i++)
+                {
+                    lwm2m_data_t * targetP;
+
+                    targetP = parsedP->value.asChildren.array + i;
+                    if (targetP->id == uriP->instanceId)
+                    {
+                        resultP = targetP->value.asChildren.array;
+                        size = targetP->value.asChildren.count;
+                    }
+                }
+                if (resultP == NULL) goto error;
+                if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+                {
+                    lwm2m_data_t * resP;
+
+                    resP = NULL;
+                    for (i = 0 ; i < size && resP == NULL; i++)
+                    {
+                        lwm2m_data_t * targetP;
+
+                        targetP = resultP + i;
+                        if (targetP->id == uriP->resourceId)
+                        {
+                            if (targetP->type == LWM2M_TYPE_MULTIPLE_RESOURCE)
+                            {
+                                resP = targetP->value.asChildren.array;
+                                size = targetP->value.asChildren.count;
+                            }
+                            else
+                            {
+                                size = prv_dataStrip(1, targetP, &resP);
+                                if (size <= 0) goto error;
+                                lwm2m_data_free(count, parsedP);
+                                parsedP = NULL;
+                            }
+                        }
+                    }
+                    if (resP == NULL) goto error;
+                    resultP = resP;
+                }
+            }
+        }
+        else
+        {
+            resultP = parsedP;
+            size = count;
+        }
+
+        if (parsedP != NULL)
+        {
+            lwm2m_data_t * tempP;
+
+            size = prv_dataStrip(size, resultP, &tempP);
+            if (size <= 0) goto error;
+            lwm2m_data_free(count, parsedP);
+            resultP = tempP;
+        }
+        count = size;
+        *dataP = resultP;
+    }
+
+    LOG_ARG("Parsing successful. count: %d", count);
+    return count;
+
+error:
+    LOG("Parsing failed");
+    if (parsedP != NULL)
+    {
+        lwm2m_data_free(count, parsedP);
+        parsedP = NULL;
+    }
+    if (recordArray != NULL)
+    {
+        lwm2m_free(recordArray);
+    }
+    return -1;
+}
+
+static int prv_serializeValue(lwm2m_data_t * tlvP,
+                              uint8_t * buffer,
+                              size_t bufferLen)
+{
+    int res;
+    int head;
+
+    switch (tlvP->type)
+    {
+    case LWM2M_TYPE_STRING:
+        if (bufferLen < JSON_ITEM_STRING_BEGIN_SIZE) return -1;
+        memcpy(buffer, JSON_ITEM_STRING_BEGIN, JSON_ITEM_STRING_BEGIN_SIZE);
+        head = JSON_ITEM_STRING_BEGIN_SIZE;
+
+        if (bufferLen - head < tlvP->value.asBuffer.length) return -1;
+        memcpy(buffer + head, tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length);
+        head += tlvP->value.asBuffer.length;
+
+        if (bufferLen - head < JSON_ITEM_STRING_END_SIZE) return -1;
+        memcpy(buffer + head, JSON_ITEM_STRING_END, JSON_ITEM_STRING_END_SIZE);
+        head += JSON_ITEM_STRING_END_SIZE;
+
+        break;
+
+    case LWM2M_TYPE_INTEGER:
+    {
+        int64_t value;
+
+        if (0 == lwm2m_data_decode_int(tlvP, &value)) return -1;
+
+        if (bufferLen < JSON_ITEM_NUM_SIZE) return -1;
+        memcpy(buffer, JSON_ITEM_NUM, JSON_ITEM_NUM_SIZE);
+        head = JSON_ITEM_NUM_SIZE;
+
+        res = utils_intToText(value, buffer + head, bufferLen - head);
+        if (res <= 0) return -1;
+        head += res;
+
+        if (bufferLen - head < JSON_ITEM_NUM_END_SIZE) return -1;
+        memcpy(buffer + head, JSON_ITEM_NUM_END, JSON_ITEM_NUM_END_SIZE);
+        head += JSON_ITEM_NUM_END_SIZE;
+    }
+    break;
+
+    case LWM2M_TYPE_FLOAT:
+    {
+        double value;
+
+        if (0 == lwm2m_data_decode_float(tlvP, &value)) return -1;
+
+        if (bufferLen < JSON_ITEM_NUM_SIZE) return -1;
+        memcpy(buffer, JSON_ITEM_NUM, JSON_ITEM_NUM_SIZE);
+        head = JSON_ITEM_NUM_SIZE;
+
+        res = utils_floatToText(value, buffer + head, bufferLen - head);
+        if (res <= 0) return -1;
+        head += res;
+
+        if (bufferLen - head < JSON_ITEM_NUM_END_SIZE) return -1;
+        memcpy(buffer + head, JSON_ITEM_NUM_END, JSON_ITEM_NUM_END_SIZE);
+        head += JSON_ITEM_NUM_END_SIZE;
+    }
+    break;
+
+    case LWM2M_TYPE_BOOLEAN:
+    {
+        bool value;
+
+        if (0 == lwm2m_data_decode_bool(tlvP, &value)) return -1;
+
+        if (value == true)
+        {
+            if (bufferLen < JSON_ITEM_BOOL_TRUE_SIZE) return -1;
+            memcpy(buffer, JSON_ITEM_BOOL_TRUE, JSON_ITEM_BOOL_TRUE_SIZE);
+            head = JSON_ITEM_BOOL_TRUE_SIZE;
+        }
+        else
+        {
+            if (bufferLen < JSON_ITEM_BOOL_FALSE_SIZE) return -1;
+            memcpy(buffer, JSON_ITEM_BOOL_FALSE, JSON_ITEM_BOOL_FALSE_SIZE);
+            head = JSON_ITEM_BOOL_FALSE_SIZE;
+        }
+    }
+    break;
+
+    case LWM2M_TYPE_OPAQUE:
+        if (bufferLen < JSON_ITEM_STRING_BEGIN_SIZE) return -1;
+        memcpy(buffer, JSON_ITEM_STRING_BEGIN, JSON_ITEM_STRING_BEGIN_SIZE);
+        head = JSON_ITEM_STRING_BEGIN_SIZE;
+
+        res = utils_base64Encode(tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length, buffer+head, bufferLen - head);
+        if (res == 0) return -1;
+        head += res;
+
+        if (bufferLen - head < JSON_ITEM_STRING_END_SIZE) return -1;
+        memcpy(buffer + head, JSON_ITEM_STRING_END, JSON_ITEM_STRING_END_SIZE);
+        head += JSON_ITEM_STRING_END_SIZE;
+        break;
+
+    case LWM2M_TYPE_OBJECT_LINK:
+        // TODO: implement
+        return -1;
+
+    default:
+        return -1;
+    }
+
+    return head;
+}
+
+int prv_serializeData(lwm2m_data_t * tlvP,
+                      uint8_t * parentUriStr,
+                      size_t parentUriLen,
+                      uint8_t * buffer,
+                      size_t bufferLen)
+{
+    int head;
+    int res;
+
+    head = 0;
+
+    switch (tlvP->type)
+    {
+    case LWM2M_TYPE_OBJECT:
+    case LWM2M_TYPE_OBJECT_INSTANCE:
+    case LWM2M_TYPE_MULTIPLE_RESOURCE:
+    {
+        uint8_t uriStr[URI_MAX_STRING_LEN];
+        size_t uriLen;
+        size_t index;
+
+        if (parentUriLen > 0)
+        {
+            if (URI_MAX_STRING_LEN < parentUriLen) return -1;
+            memcpy(uriStr, parentUriStr, parentUriLen);
+            uriLen = parentUriLen;
+        }
+        else
+        {
+            uriLen = 0;
+        }
+        res = utils_intToText(tlvP->id, uriStr + uriLen, URI_MAX_STRING_LEN - uriLen);
+        if (res <= 0) return -1;
+        uriLen += res;
+        uriStr[uriLen] = '/';
+        uriLen++;
+
+        head = 0;
+        for (index = 0 ; index < tlvP->value.asChildren.count; index++)
+        {
+            res = prv_serializeData(tlvP->value.asChildren.array + index, uriStr, uriLen, buffer + head, bufferLen - head);
+            if (res < 0) return -1;
+            head += res;
+        }
+    }
+    break;
+
+    default:
+        if (bufferLen < JSON_RES_ITEM_URI_SIZE) return -1;
+        memcpy(buffer, JSON_RES_ITEM_URI, JSON_RES_ITEM_URI_SIZE);
+        head = JSON_RES_ITEM_URI_SIZE;
+
+        if (parentUriLen > 0)
+        {
+            if (bufferLen - head < parentUriLen) return -1;
+            memcpy(buffer + head, parentUriStr, parentUriLen);
+            head += parentUriLen;
+        }
+
+        res = utils_intToText(tlvP->id, buffer + head, bufferLen - head);
+        if (res <= 0) return -1;
+        head += res;
+
+        res = prv_serializeValue(tlvP, buffer + head, bufferLen - head);
+        if (res < 0) return -1;
+        head += res;
+        break;
+    }
+
+    return head;
+}
+
+static int prv_findAndCheckData(lwm2m_uri_t * uriP,
+                                uri_depth_t level,
+                                size_t size,
+                                lwm2m_data_t * tlvP,
+                                lwm2m_data_t ** targetP)
+{
+    size_t index;
+    int result;
+
+    if (size == 0) return 0;
+
+    if (size > 1)
+    {
+        if (tlvP[0].type == LWM2M_TYPE_OBJECT || tlvP[0].type == LWM2M_TYPE_OBJECT_INSTANCE)
+        {
+            for (index = 0; index < size; index++)
+            {
+                if (tlvP[index].type != tlvP[0].type)
+                {
+                    *targetP = NULL;
+                    return -1;
+                }
+            }
+        }
+        else
+        {
+            for (index = 0; index < size; index++)
+            {
+                if (tlvP[index].type == LWM2M_TYPE_OBJECT || tlvP[index].type == LWM2M_TYPE_OBJECT_INSTANCE)
+                {
+                    *targetP = NULL;
+                    return -1;
+                }
+            }
+        }
+    }
+
+    *targetP = NULL;
+    result = -1;
+    switch (level)
+    {
+    case URI_DEPTH_OBJECT:
+        if (tlvP[0].type == LWM2M_TYPE_OBJECT)
+        {
+            *targetP = tlvP;
+            result = (int)size;
+        }
+        break;
+
+    case URI_DEPTH_OBJECT_INSTANCE:
+        switch (tlvP[0].type)
+        {
+        case LWM2M_TYPE_OBJECT:
+            for (index = 0; index < size; index++)
+            {
+                if (tlvP[index].id == uriP->objectId)
+                {
+                    return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP);
+                }
+            }
+            break;
+        case LWM2M_TYPE_OBJECT_INSTANCE:
+            *targetP = tlvP;
+            result = (int)size;
+            break;
+        default:
+            break;
+        }
+        break;
+
+    case URI_DEPTH_RESOURCE:
+        switch (tlvP[0].type)
+        {
+        case LWM2M_TYPE_OBJECT:
+            for (index = 0; index < size; index++)
+            {
+                if (tlvP[index].id == uriP->objectId)
+                {
+                    return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP);
+                }
+            }
+            break;
+        case LWM2M_TYPE_OBJECT_INSTANCE:
+            for (index = 0; index < size; index++)
+            {
+                if (tlvP[index].id == uriP->instanceId)
+                {
+                    return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP);
+                }
+            }
+            break;
+        default:
+            *targetP = tlvP;
+            result = (int)size;
+            break;
+        }
+        break;
+
+    case URI_DEPTH_RESOURCE_INSTANCE:
+        switch (tlvP[0].type)
+        {
+        case LWM2M_TYPE_OBJECT:
+            for (index = 0; index < size; index++)
+            {
+                if (tlvP[index].id == uriP->objectId)
+                {
+                    return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP);
+                }
+            }
+            break;
+        case LWM2M_TYPE_OBJECT_INSTANCE:
+            for (index = 0; index < size; index++)
+            {
+                if (tlvP[index].id == uriP->instanceId)
+                {
+                    return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP);
+                }
+            }
+            break;
+        case LWM2M_TYPE_MULTIPLE_RESOURCE:
+            for (index = 0; index < size; index++)
+            {
+                if (tlvP[index].id == uriP->resourceId)
+                {
+                    return prv_findAndCheckData(uriP, level, tlvP[index].value.asChildren.count, tlvP[index].value.asChildren.array, targetP);
+                }
+            }
+            break;
+        default:
+            *targetP = tlvP;
+            result = (int)size;
+            break;
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return result;
+}
+
+int json_serialize(lwm2m_uri_t * uriP,
+                   int size,
+                   lwm2m_data_t * tlvP,
+                   uint8_t ** bufferP)
+{
+    int index;
+    size_t head;
+    uint8_t bufferJSON[PRV_JSON_BUFFER_SIZE];
+    uint8_t baseUriStr[URI_MAX_STRING_LEN];
+    int baseUriLen;
+    uri_depth_t rootLevel;
+    int num;
+    lwm2m_data_t * targetP;
+
+    LOG_ARG("size: %d", size);
+    LOG_URI(uriP);
+    if (size != 0 && tlvP == NULL) return -1;
+
+    baseUriLen = uri_toString(uriP, baseUriStr, URI_MAX_STRING_LEN, &rootLevel);
+    if (baseUriLen < 0) return -1;
+
+    num = prv_findAndCheckData(uriP, rootLevel, size, tlvP, &targetP);
+    if (num < 0) return -1;
+
+    while (num == 1
+        && (targetP->type == LWM2M_TYPE_OBJECT
+         || targetP->type == LWM2M_TYPE_OBJECT_INSTANCE
+         || targetP->type == LWM2M_TYPE_MULTIPLE_RESOURCE))
+    {
+        int res;
+
+        res = utils_intToText(targetP->id, baseUriStr + baseUriLen, URI_MAX_STRING_LEN - baseUriLen);
+        if (res <= 0) return 0;
+        baseUriLen += res;
+        if (baseUriLen >= URI_MAX_STRING_LEN -1) return 0;
+        num = targetP->value.asChildren.count;
+        targetP = targetP->value.asChildren.array;
+        baseUriStr[baseUriLen] = '/';
+        baseUriLen++;
+    }
+
+    if (baseUriLen > 0)
+    {
+        memcpy(bufferJSON, JSON_BN_HEADER_1, JSON_BN_HEADER_1_SIZE);
+        head = JSON_BN_HEADER_1_SIZE;
+        memcpy(bufferJSON + head, baseUriStr, baseUriLen);
+        head += baseUriLen;
+        memcpy(bufferJSON + head, JSON_BN_HEADER_2, JSON_BN_HEADER_2_SIZE);
+        head += JSON_BN_HEADER_2_SIZE;
+    }
+    else
+    {
+        memcpy(bufferJSON, JSON_HEADER, JSON_HEADER_SIZE);
+        head = JSON_HEADER_SIZE;
+    }
+
+    for (index = 0 ; index < num && head < PRV_JSON_BUFFER_SIZE ; index++)
+    {
+        int res;
+
+        res = prv_serializeData(targetP + index, NULL, 0, bufferJSON + head, PRV_JSON_BUFFER_SIZE - head);
+        if (res < 0) return 0;
+        head += res;
+    }
+
+    if (head + JSON_FOOTER_SIZE - 1 > PRV_JSON_BUFFER_SIZE) return 0;
+
+    if (num > 0) head = head - 1;
+
+    memcpy(bufferJSON + head, JSON_FOOTER, JSON_FOOTER_SIZE);
+    head = head + JSON_FOOTER_SIZE;
+
+    *bufferP = (uint8_t *)lwm2m_malloc(head);
+    if (*bufferP == NULL) return 0;
+    memcpy(*bufferP, bufferJSON, head);
+
+    return head;
+}
+
+#endif
+
diff -r 000000000000 -r 3f48af28ebcd core/liblwm2m.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/liblwm2m.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,475 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Fabien Fleutot - Please refer to git log
+ *    Simon Bernard - Please refer to git log
+ *    Toby Jaffey - 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>
+
+
+lwm2m_context_t * lwm2m_init(void * userData)
+{
+    lwm2m_context_t * contextP;
+
+    LOG("Entering");
+    contextP = (lwm2m_context_t *)lwm2m_malloc(sizeof(lwm2m_context_t));
+    if (NULL != contextP)
+    {
+        memset(contextP, 0, sizeof(lwm2m_context_t));
+        contextP->userData = userData;
+        srand((int)lwm2m_gettime());
+        contextP->nextMID = rand();
+    }
+
+    return contextP;
+}
+
+#ifdef LWM2M_CLIENT_MODE
+void lwm2m_deregister(lwm2m_context_t * context)
+{
+    lwm2m_server_t * server = context->serverList;
+
+    LOG("Entering");
+    while (NULL != server)
+    {
+        registration_deregister(context, server);
+        server = server->next;
+    }
+}
+
+static void prv_deleteServer(lwm2m_server_t * serverP)
+{
+    // TODO parse transaction and observation to remove the ones related to this server
+    if (NULL != serverP->location)
+    {
+        lwm2m_free(serverP->location);
+    }
+    free_block1_buffer(serverP->block1Data);
+    lwm2m_free(serverP);
+}
+
+static void prv_deleteServerList(lwm2m_context_t * context)
+{
+    while (NULL != context->serverList)
+    {
+        lwm2m_server_t * server;
+        server = context->serverList;
+        context->serverList = server->next;
+        prv_deleteServer(server);
+    }
+}
+
+static void prv_deleteBootstrapServer(lwm2m_server_t * serverP)
+{
+    // TODO should we free location as in prv_deleteServer ?
+    // TODO should we parse transaction and observation to remove the ones related to this server ?
+    free_block1_buffer(serverP->block1Data);
+    lwm2m_free(serverP);
+}
+
+static void prv_deleteBootstrapServerList(lwm2m_context_t * context)
+{
+    while (NULL != context->bootstrapServerList)
+    {
+        lwm2m_server_t * server;
+        server = context->bootstrapServerList;
+        context->bootstrapServerList = server->next;
+        prv_deleteBootstrapServer(server);
+    }
+}
+
+static void prv_deleteObservedList(lwm2m_context_t * contextP)
+{
+    while (NULL != contextP->observedList)
+    {
+        lwm2m_observed_t * targetP;
+        lwm2m_watcher_t * watcherP;
+
+        targetP = contextP->observedList;
+        contextP->observedList = contextP->observedList->next;
+
+        for (watcherP = targetP->watcherList ; watcherP != NULL ; watcherP = watcherP->next)
+        {
+            if (watcherP->parameters != NULL) lwm2m_free(watcherP->parameters);
+        }
+        LWM2M_LIST_FREE(targetP->watcherList);
+
+        lwm2m_free(targetP);
+    }
+}
+#endif
+
+void prv_deleteTransactionList(lwm2m_context_t * context)
+{
+    while (NULL != context->transactionList)
+    {
+        lwm2m_transaction_t * transaction;
+
+        transaction = context->transactionList;
+        context->transactionList = context->transactionList->next;
+        transaction_free(transaction);
+    }
+}
+
+void lwm2m_close(lwm2m_context_t * contextP)
+{
+#ifdef LWM2M_CLIENT_MODE
+
+    LOG("Entering");
+    lwm2m_deregister(contextP);
+    prv_deleteServerList(contextP);
+    prv_deleteBootstrapServerList(contextP);
+    prv_deleteObservedList(contextP);
+    lwm2m_free(contextP->endpointName);
+    if (contextP->msisdn != NULL)
+    {
+        lwm2m_free(contextP->msisdn);
+    }
+    if (contextP->altPath != NULL)
+    {
+        lwm2m_free(contextP->altPath);
+    }
+
+#endif
+
+#ifdef LWM2M_SERVER_MODE
+    while (NULL != contextP->clientList)
+    {
+        lwm2m_client_t * clientP;
+
+        clientP = contextP->clientList;
+        contextP->clientList = contextP->clientList->next;
+
+        registration_freeClient(clientP);
+    }
+#endif
+
+    prv_deleteTransactionList(contextP);
+    lwm2m_free(contextP);
+}
+
+#ifdef LWM2M_CLIENT_MODE
+static int prv_refreshServerList(lwm2m_context_t * contextP)
+{
+    lwm2m_server_t * targetP;
+    lwm2m_server_t * nextP;
+
+    // Remove all servers marked as dirty
+    targetP = contextP->bootstrapServerList;
+    contextP->bootstrapServerList = NULL;
+    while (targetP != NULL)
+    {
+        nextP = targetP->next;
+        targetP->next = NULL;
+        if (!targetP->dirty)
+        {
+            targetP->status = STATE_DEREGISTERED;
+            contextP->bootstrapServerList = (lwm2m_server_t *)LWM2M_LIST_ADD(contextP->bootstrapServerList, targetP);
+        }
+        else
+        {
+            prv_deleteServer(targetP);
+        }
+        targetP = nextP;
+    }
+    targetP = contextP->serverList;
+    contextP->serverList = NULL;
+    while (targetP != NULL)
+    {
+        nextP = targetP->next;
+        targetP->next = NULL;
+        if (!targetP->dirty)
+        {
+            // TODO: Should we revert the status to STATE_DEREGISTERED ?
+            contextP->serverList = (lwm2m_server_t *)LWM2M_LIST_ADD(contextP->serverList, targetP);
+        }
+        else
+        {
+            prv_deleteServer(targetP);
+        }
+        targetP = nextP;
+    }
+
+    return object_getServers(contextP);
+}
+
+int lwm2m_configure(lwm2m_context_t * contextP,
+                    const char * endpointName,
+                    const char * msisdn,
+                    const char * altPath,
+                    uint16_t numObject,
+                    lwm2m_object_t * objectList[])
+{
+    int i;
+    uint8_t found;
+
+    LOG_ARG("endpointName: \"%s\", msisdn: \"%s\", altPath: \"%s\", numObject: %d", endpointName, msisdn, altPath, numObject);
+    // This API can be called only once for now
+    if (contextP->endpointName != NULL || contextP->objectList != NULL) return COAP_400_BAD_REQUEST;
+
+    if (endpointName == NULL) return COAP_400_BAD_REQUEST;
+    if (numObject < 3) return COAP_400_BAD_REQUEST;
+    // Check that mandatory objects are present
+    found = 0;
+    for (i = 0 ; i < numObject ; i++)
+    {
+        if (objectList[i]->objID == LWM2M_SECURITY_OBJECT_ID) found |= 0x01;
+        if (objectList[i]->objID == LWM2M_SERVER_OBJECT_ID) found |= 0x02;
+        if (objectList[i]->objID == LWM2M_DEVICE_OBJECT_ID) found |= 0x04;
+    }
+    if (found != 0x07) return COAP_400_BAD_REQUEST;
+    if (altPath != NULL)
+    {
+        if (0 == utils_isAltPathValid(altPath))
+        {
+            return COAP_400_BAD_REQUEST;
+        }
+        if (altPath[1] == 0)
+        {
+            altPath = NULL;
+        }
+    }
+    contextP->endpointName = lwm2m_strdup(endpointName);
+    if (contextP->endpointName == NULL)
+    {
+        return COAP_500_INTERNAL_SERVER_ERROR;
+    }
+
+    if (msisdn != NULL)
+    {
+        contextP->msisdn = lwm2m_strdup(msisdn);
+        if (contextP->msisdn == NULL)
+        {
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    if (altPath != NULL)
+    {
+        contextP->altPath = lwm2m_strdup(altPath);
+        if (contextP->altPath == NULL)
+        {
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    for (i = 0; i < numObject; i++)
+    {
+        objectList[i]->next = NULL;
+        contextP->objectList = (lwm2m_object_t *)LWM2M_LIST_ADD(contextP->objectList, objectList[i]);
+    }
+
+    return COAP_NO_ERROR;
+}
+
+int lwm2m_add_object(lwm2m_context_t * contextP,
+                     lwm2m_object_t * objectP)
+{
+    lwm2m_object_t * targetP;
+
+    LOG_ARG("ID: %d", objectP->objID);
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, objectP->objID);
+    if (targetP != NULL) return COAP_406_NOT_ACCEPTABLE;
+    objectP->next = NULL;
+
+    contextP->objectList = (lwm2m_object_t *)LWM2M_LIST_ADD(contextP->objectList, objectP);
+
+    if (contextP->state == STATE_READY)
+    {
+        return lwm2m_update_registration(contextP, 0, true);
+    }
+
+    return COAP_NO_ERROR;
+}
+
+int lwm2m_remove_object(lwm2m_context_t * contextP,
+                        uint16_t id)
+{
+    lwm2m_object_t * targetP;
+
+    LOG_ARG("ID: %d", id);
+    contextP->objectList = (lwm2m_object_t *)LWM2M_LIST_RM(contextP->objectList, id, &targetP);
+
+    if (targetP == NULL) return COAP_404_NOT_FOUND;
+
+    if (contextP->state == STATE_READY)
+    {
+        return lwm2m_update_registration(contextP, 0, true);
+    }
+
+    return 0;
+}
+
+#endif
+
+
+int lwm2m_step(lwm2m_context_t * contextP,
+               time_t * timeoutP)
+{
+    time_t tv_sec;
+    int result;
+
+    LOG_ARG("timeoutP: %" PRId64, *timeoutP);
+    tv_sec = lwm2m_gettime();
+    if (tv_sec < 0) return COAP_500_INTERNAL_SERVER_ERROR;
+
+#ifdef LWM2M_CLIENT_MODE
+    LOG_ARG("State: %s", STR_STATE(contextP->state));
+    // state can also be modified in bootstrap_handleCommand().
+
+next_step:
+    switch (contextP->state)
+    {
+    case STATE_INITIAL:
+        if (0 != prv_refreshServerList(contextP)) return COAP_503_SERVICE_UNAVAILABLE;
+        if (contextP->serverList != NULL)
+        {
+            contextP->state = STATE_REGISTER_REQUIRED;
+        }
+        else
+        {
+            // Bootstrapping
+            contextP->state = STATE_BOOTSTRAP_REQUIRED;
+        }
+        goto next_step;
+        break;
+
+    case STATE_BOOTSTRAP_REQUIRED:
+#ifdef LWM2M_BOOTSTRAP
+        if (contextP->bootstrapServerList != NULL)
+        {
+            bootstrap_start(contextP);
+            contextP->state = STATE_BOOTSTRAPPING;
+            bootstrap_step(contextP, tv_sec, timeoutP);
+        }
+        else
+#endif
+        {
+            return COAP_503_SERVICE_UNAVAILABLE;
+        }
+        break;
+
+#ifdef LWM2M_BOOTSTRAP
+    case STATE_BOOTSTRAPPING:
+        switch (bootstrap_getStatus(contextP))
+        {
+        case STATE_BS_FINISHED:
+            contextP->state = STATE_INITIAL;
+            goto next_step;
+            break;
+
+        case STATE_BS_FAILED:
+            return COAP_503_SERVICE_UNAVAILABLE;
+
+        default:
+            // keep on waiting
+            bootstrap_step(contextP, tv_sec, timeoutP);
+            break;
+        }
+        break;
+#endif
+    case STATE_REGISTER_REQUIRED:
+        result = registration_start(contextP);
+        if (COAP_NO_ERROR != result) return result;
+        contextP->state = STATE_REGISTERING;
+        break;
+
+    case STATE_REGISTERING:
+    {
+        switch (registration_getStatus(contextP))
+        {
+        case STATE_REGISTERED:
+            contextP->state = STATE_READY;
+            break;
+
+        case STATE_REG_FAILED:
+            // TODO avoid infinite loop by checking the bootstrap info is different
+            contextP->state = STATE_BOOTSTRAP_REQUIRED;
+            goto next_step;
+            break;
+
+        case STATE_REG_PENDING:
+        default:
+            // keep on waiting
+            break;
+        }
+    }
+    break;
+
+    case STATE_READY:
+        if (registration_getStatus(contextP) == STATE_REG_FAILED)
+        {
+            // TODO avoid infinite loop by checking the bootstrap info is different
+            contextP->state = STATE_BOOTSTRAP_REQUIRED;
+            goto next_step;
+            break;
+        }
+        break;
+
+    default:
+        // do nothing
+        break;
+    }
+
+    observe_step(contextP, tv_sec, timeoutP);
+#endif
+
+    registration_step(contextP, tv_sec, timeoutP);
+    transaction_step(contextP, tv_sec, timeoutP);
+
+    LOG_ARG("Final timeoutP: %" PRId64, *timeoutP);
+#ifdef LWM2M_CLIENT_MODE
+    LOG_ARG("Final state: %s", STR_STATE(contextP->state));
+#endif
+    return 0;
+}
diff -r 000000000000 -r 3f48af28ebcd core/liblwm2m.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/liblwm2m.h	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,740 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Fabien Fleutot - Please refer to git log
+ *    Simon Bernard - Please refer to git log
+ *    Toby Jaffey - 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>
+
+*/
+
+#ifndef _LWM2M_CLIENT_H_
+#define _LWM2M_CLIENT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <time.h>
+
+#ifdef LWM2M_SERVER_MODE
+#ifndef LWM2M_SUPPORT_JSON
+#define LWM2M_SUPPORT_JSON
+#endif
+#endif
+
+#if defined(LWM2M_BOOTSTRAP) && defined(LWM2M_BOOTSTRAP_SERVER_MODE)
+#error "LWM2M_BOOTSTRAP and LWM2M_BOOTSTRAP_SERVER_MODE cannot be defined at the same time!"
+#endif
+
+/*
+ * Platform abstraction functions to be implemented by the user
+ */
+
+#ifndef LWM2M_MEMORY_TRACE
+// Allocate a block of size bytes of memory, returning a pointer to the beginning of the block.
+void * lwm2m_malloc(size_t s);
+// Deallocate a block of memory previously allocated by lwm2m_malloc() or lwm2m_strdup()
+void lwm2m_free(void * p);
+// Allocate a memory block, duplicate the string str in it and return a pointer to this new block.
+char * lwm2m_strdup(const char * str);
+#else
+// same functions as above with caller location for debugging purposes
+char * lwm2m_trace_strdup(const char * str, const char * file, const char * function, int lineno);
+void * lwm2m_trace_malloc(size_t size, const char * file, const char * function, int lineno);
+void    lwm2m_trace_free(void * mem, const char * file, const char * function, int lineno);
+
+#define lwm2m_strdup(S) lwm2m_trace_strdup(S, __FILE__, __FUNCTION__, __LINE__)
+#define lwm2m_malloc(S) lwm2m_trace_malloc(S, __FILE__, __FUNCTION__, __LINE__)
+#define lwm2m_free(M)   lwm2m_trace_free(M, __FILE__, __FUNCTION__, __LINE__)
+#endif
+// Compare at most the n first bytes of s1 and s2, return 0 if they match
+int lwm2m_strncmp(const char * s1, const char * s2, size_t n);
+// This function must return the number of seconds elapsed since origin.
+// The origin (Epoch, system boot, etc...) does not matter as this
+// function is used only to determine the elapsed time since the last
+// call to it.
+// In case of error, this must return a negative value.
+// Per POSIX specifications, time_t is a signed integer.
+time_t lwm2m_gettime(void);
+
+#ifdef LWM2M_WITH_LOGS
+// Same usage as C89 printf()
+void lwm2m_printf(const char * format, ...);
+#endif
+
+// communication layer
+#ifdef LWM2M_CLIENT_MODE
+// Returns a session handle that MUST uniquely identify a peer.
+// secObjInstID: ID of the Securty Object instance to open a connection to
+// userData: parameter to lwm2m_init()
+void * lwm2m_connect_server(uint16_t secObjInstID, void * userData);
+// Close a session created by lwm2m_connect_server()
+// sessionH: session handle identifying the peer (opaque to the core)
+// userData: parameter to lwm2m_init()
+void lwm2m_close_connection(void * sessionH, void * userData);
+#endif
+// Send data to a peer
+// Returns COAP_NO_ERROR or a COAP_NNN error code
+// sessionH: session handle identifying the peer (opaque to the core)
+// buffer, length: data to send
+// userData: parameter to lwm2m_init()
+uint8_t lwm2m_buffer_send(void * sessionH, uint8_t * buffer, size_t length, void * userData);
+// Compare two session handles
+// Returns true if the two sessions identify the same peer. false otherwise.
+// userData: parameter to lwm2m_init()
+bool lwm2m_session_is_equal(void * session1, void * session2, void * userData);
+
+/*
+ * Error code
+ */
+
+#define COAP_NO_ERROR                   (uint8_t)0x00
+#define COAP_IGNORE                     (uint8_t)0x01
+
+#define COAP_201_CREATED                (uint8_t)0x41
+#define COAP_202_DELETED                (uint8_t)0x42
+#define COAP_204_CHANGED                (uint8_t)0x44
+#define COAP_205_CONTENT                (uint8_t)0x45
+#define COAP_231_CONTINUE               (uint8_t)0x5F
+#define COAP_400_BAD_REQUEST            (uint8_t)0x80
+#define COAP_401_UNAUTHORIZED           (uint8_t)0x81
+#define COAP_402_BAD_OPTION             (uint8_t)0x82
+#define COAP_404_NOT_FOUND              (uint8_t)0x84
+#define COAP_405_METHOD_NOT_ALLOWED     (uint8_t)0x85
+#define COAP_406_NOT_ACCEPTABLE         (uint8_t)0x86
+#define COAP_408_REQ_ENTITY_INCOMPLETE  (uint8_t)0x88
+#define COAP_412_PRECONDITION_FAILED    (uint8_t)0x8C
+#define COAP_413_ENTITY_TOO_LARGE       (uint8_t)0x8D
+#define COAP_500_INTERNAL_SERVER_ERROR  (uint8_t)0xA0
+#define COAP_501_NOT_IMPLEMENTED        (uint8_t)0xA1
+#define COAP_503_SERVICE_UNAVAILABLE    (uint8_t)0xA3
+
+/*
+ * Standard Object IDs
+ */
+#define LWM2M_SECURITY_OBJECT_ID            0
+#define LWM2M_SERVER_OBJECT_ID              1
+#define LWM2M_ACL_OBJECT_ID                 2
+#define LWM2M_DEVICE_OBJECT_ID              3
+#define LWM2M_CONN_MONITOR_OBJECT_ID        4
+#define LWM2M_FIRMWARE_UPDATE_OBJECT_ID     5
+#define LWM2M_LOCATION_OBJECT_ID            6
+#define LWM2M_CONN_STATS_OBJECT_ID          7
+
+/*
+ * Ressource IDs for the LWM2M Security Object
+ */
+#define LWM2M_SECURITY_URI_ID                 0
+#define LWM2M_SECURITY_BOOTSTRAP_ID           1
+#define LWM2M_SECURITY_SECURITY_ID            2
+#define LWM2M_SECURITY_PUBLIC_KEY_ID          3
+#define LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID   4
+#define LWM2M_SECURITY_SECRET_KEY_ID          5
+#define LWM2M_SECURITY_SMS_SECURITY_ID        6
+#define LWM2M_SECURITY_SMS_KEY_PARAM_ID       7
+#define LWM2M_SECURITY_SMS_SECRET_KEY_ID      8
+#define LWM2M_SECURITY_SMS_SERVER_NUMBER_ID   9
+#define LWM2M_SECURITY_SHORT_SERVER_ID        10
+#define LWM2M_SECURITY_HOLD_OFF_ID            11
+#define LWM2M_SECURITY_BOOTSTRAP_TIMEOUT_ID   12
+
+/*
+ * Ressource IDs for the LWM2M Server Object
+ */
+#define LWM2M_SERVER_SHORT_ID_ID    0
+#define LWM2M_SERVER_LIFETIME_ID    1
+#define LWM2M_SERVER_MIN_PERIOD_ID  2
+#define LWM2M_SERVER_MAX_PERIOD_ID  3
+#define LWM2M_SERVER_DISABLE_ID     4
+#define LWM2M_SERVER_TIMEOUT_ID     5
+#define LWM2M_SERVER_STORING_ID     6
+#define LWM2M_SERVER_BINDING_ID     7
+#define LWM2M_SERVER_UPDATE_ID      8
+
+#define LWM2M_SECURITY_MODE_PRE_SHARED_KEY  0
+#define LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY  1
+#define LWM2M_SECURITY_MODE_CERTIFICATE     2
+#define LWM2M_SECURITY_MODE_NONE            3
+
+
+/*
+ * Utility functions for sorted linked list
+ */
+
+typedef struct _lwm2m_list_t
+{
+    struct _lwm2m_list_t * next;
+    uint16_t    id;
+} lwm2m_list_t;
+
+// defined in list.c
+// Add 'node' to the list 'head' and return the new list
+lwm2m_list_t * lwm2m_list_add(lwm2m_list_t * head, lwm2m_list_t * node);
+// Return the node with ID 'id' from the list 'head' or NULL if not found
+lwm2m_list_t * lwm2m_list_find(lwm2m_list_t * head, uint16_t id);
+// Remove the node with ID 'id' from the list 'head' and return the new list
+lwm2m_list_t * lwm2m_list_remove(lwm2m_list_t * head, uint16_t id, lwm2m_list_t ** nodeP);
+// Return the lowest unused ID in the list 'head'
+uint16_t lwm2m_list_newId(lwm2m_list_t * head);
+// Free a list. Do not use if nodes contain allocated pointers as it calls lwm2m_free on nodes only.
+// If the nodes of the list need to do more than just "free()" their instances, don't use lwm2m_list_free().
+void lwm2m_list_free(lwm2m_list_t * head);
+
+#define LWM2M_LIST_ADD(H,N) lwm2m_list_add((lwm2m_list_t *)H, (lwm2m_list_t *)N);
+#define LWM2M_LIST_RM(H,I,N) lwm2m_list_remove((lwm2m_list_t *)H, I, (lwm2m_list_t **)N);
+#define LWM2M_LIST_FIND(H,I) lwm2m_list_find((lwm2m_list_t *)H, I)
+#define LWM2M_LIST_FREE(H) lwm2m_list_free((lwm2m_list_t *)H)
+
+/*
+ * URI
+ *
+ * objectId is always set
+ * instanceId or resourceId are set according to the flag bit-field
+ *
+ */
+
+#define LWM2M_MAX_ID   ((uint16_t)0xFFFF)
+
+#define LWM2M_URI_FLAG_OBJECT_ID    (uint8_t)0x04
+#define LWM2M_URI_FLAG_INSTANCE_ID  (uint8_t)0x02
+#define LWM2M_URI_FLAG_RESOURCE_ID  (uint8_t)0x01
+
+#define LWM2M_URI_IS_SET_INSTANCE(uri) (((uri)->flag & LWM2M_URI_FLAG_INSTANCE_ID) != 0)
+#define LWM2M_URI_IS_SET_RESOURCE(uri) (((uri)->flag & LWM2M_URI_FLAG_RESOURCE_ID) != 0)
+
+typedef struct
+{
+    uint8_t     flag;           // indicates which segments are set
+    uint16_t    objectId;
+    uint16_t    instanceId;
+    uint16_t    resourceId;
+} lwm2m_uri_t;
+
+
+#define LWM2M_STRING_ID_MAX_LEN 6
+
+// Parse an URI in LWM2M format and fill the lwm2m_uri_t.
+// Return the number of characters read from buffer or 0 in case of error.
+// Valid URIs: /1, /1/, /1/2, /1/2/, /1/2/3
+// Invalid URIs: /, //, //2, /1//, /1//3, /1/2/3/, /1/2/3/4
+int lwm2m_stringToUri(const char * buffer, size_t buffer_len, lwm2m_uri_t * uriP);
+
+/*
+ * The lwm2m_data_t is used to store LWM2M resource values in a hierarchical way.
+ * Depending on the type the value is different:
+ * - LWM2M_TYPE_OBJECT, LWM2M_TYPE_OBJECT_INSTANCE, LWM2M_TYPE_MULTIPLE_RESOURCE: value.asChildren
+ * - LWM2M_TYPE_STRING, LWM2M_TYPE_OPAQUE: value.asBuffer
+ * - LWM2M_TYPE_INTEGER, LWM2M_TYPE_TIME: value.asInteger
+ * - LWM2M_TYPE_FLOAT: value.asFloat
+ * - LWM2M_TYPE_BOOLEAN: value.asBoolean
+ *
+ * LWM2M_TYPE_STRING is also used when the data is in text format.
+ */
+
+typedef enum
+{
+    LWM2M_TYPE_UNDEFINED = 0,
+    LWM2M_TYPE_OBJECT,
+    LWM2M_TYPE_OBJECT_INSTANCE,
+    LWM2M_TYPE_MULTIPLE_RESOURCE,
+
+    LWM2M_TYPE_STRING,
+    LWM2M_TYPE_OPAQUE,
+    LWM2M_TYPE_INTEGER,
+    LWM2M_TYPE_FLOAT,
+    LWM2M_TYPE_BOOLEAN,
+
+    LWM2M_TYPE_OBJECT_LINK
+} lwm2m_data_type_t;
+
+typedef struct _lwm2m_data_t lwm2m_data_t;
+
+struct _lwm2m_data_t
+{
+    lwm2m_data_type_t type;
+    uint16_t    id;
+    union
+    {
+        bool        asBoolean;
+        int64_t     asInteger;
+        double      asFloat;
+        struct
+        {
+            size_t    length;
+            uint8_t * buffer;
+        } asBuffer;
+        struct
+        {
+            size_t         count;
+            lwm2m_data_t * array;
+        } asChildren;
+        struct
+        {
+            uint16_t objectId;
+            uint16_t objectInstanceId;
+        } asObjLink;
+    } value;
+};
+
+typedef enum
+{
+    LWM2M_CONTENT_TEXT      = 0,        // Also used as undefined
+    LWM2M_CONTENT_LINK      = 40,
+    LWM2M_CONTENT_OPAQUE    = 42,
+    LWM2M_CONTENT_TLV_OLD   = 1542,     // Keep old value for backward-compatibility
+    LWM2M_CONTENT_TLV       = 11542,
+    LWM2M_CONTENT_JSON_OLD  = 1543,     // Keep old value for backward-compatibility
+    LWM2M_CONTENT_JSON      = 11543
+} lwm2m_media_type_t;
+
+lwm2m_data_t * lwm2m_data_new(int size);
+int lwm2m_data_parse(lwm2m_uri_t * uriP, uint8_t * buffer, size_t bufferLen, lwm2m_media_type_t format, lwm2m_data_t ** dataP);
+int lwm2m_data_serialize(lwm2m_uri_t * uriP, int size, lwm2m_data_t * dataP, lwm2m_media_type_t * formatP, uint8_t ** bufferP);
+void lwm2m_data_free(int size, lwm2m_data_t * dataP);
+
+void lwm2m_data_encode_string(const char * string, lwm2m_data_t * dataP);
+void lwm2m_data_encode_nstring(const char * string, size_t length, lwm2m_data_t * dataP);
+void lwm2m_data_encode_opaque(uint8_t * buffer, size_t length, lwm2m_data_t * dataP);
+void lwm2m_data_encode_int(int64_t value, lwm2m_data_t * dataP);
+int lwm2m_data_decode_int(const lwm2m_data_t * dataP, int64_t * valueP);
+void lwm2m_data_encode_float(double value, lwm2m_data_t * dataP);
+int lwm2m_data_decode_float(const lwm2m_data_t * dataP, double * valueP);
+void lwm2m_data_encode_bool(bool value, lwm2m_data_t * dataP);
+int lwm2m_data_decode_bool(const lwm2m_data_t * dataP, bool * valueP);
+void lwm2m_data_encode_objlink(uint16_t objectId, uint16_t objectInstanceId, lwm2m_data_t * dataP);
+void lwm2m_data_encode_instances(lwm2m_data_t * subDataP, size_t count, lwm2m_data_t * dataP);
+void lwm2m_data_include(lwm2m_data_t * subDataP, size_t count, lwm2m_data_t * dataP);
+
+
+/*
+ * Utility function to parse TLV buffers directly
+ *
+ * Returned value: number of bytes parsed
+ * buffer: buffer to parse
+ * buffer_len: length in bytes of buffer
+ * oType: (OUT) type of the parsed TLV record. can be:
+ *          - LWM2M_TYPE_OBJECT
+ *          - LWM2M_TYPE_OBJECT_INSTANCE
+ *          - LWM2M_TYPE_MULTIPLE_RESOURCE
+ *          - LWM2M_TYPE_OPAQUE
+ * oID: (OUT) ID of the parsed TLV record
+ * oDataIndex: (OUT) index of the data of the parsed TLV record in the buffer
+ * oDataLen: (OUT) length of the data of the parsed TLV record
+ */
+
+#define LWM2M_TLV_HEADER_MAX_LENGTH 6
+
+int lwm2m_decode_TLV(const uint8_t * buffer, size_t buffer_len, lwm2m_data_type_t * oType, uint16_t * oID, size_t * oDataIndex, size_t * oDataLen);
+
+
+/*
+ * LWM2M Objects
+ *
+ * For the read callback, if *numDataP is not zero, *dataArrayP is pre-allocated
+ * and contains the list of resources to read.
+ *
+ */
+
+typedef struct _lwm2m_object_t lwm2m_object_t;
+
+typedef uint8_t (*lwm2m_read_callback_t) (uint16_t instanceId, int * numDataP, lwm2m_data_t ** dataArrayP, lwm2m_object_t * objectP);
+typedef uint8_t (*lwm2m_discover_callback_t) (uint16_t instanceId, int * numDataP, lwm2m_data_t ** dataArrayP, lwm2m_object_t * objectP);
+typedef uint8_t (*lwm2m_write_callback_t) (uint16_t instanceId, int numData, lwm2m_data_t * dataArray, lwm2m_object_t * objectP);
+typedef uint8_t (*lwm2m_execute_callback_t) (uint16_t instanceId, uint16_t resourceId, uint8_t * buffer, int length, lwm2m_object_t * objectP);
+typedef uint8_t (*lwm2m_create_callback_t) (uint16_t instanceId, int numData, lwm2m_data_t * dataArray, lwm2m_object_t * objectP);
+typedef uint8_t (*lwm2m_delete_callback_t) (uint16_t instanceId, lwm2m_object_t * objectP);
+
+struct _lwm2m_object_t
+{
+    struct _lwm2m_object_t * next;           // for internal use only.
+    uint16_t       objID;
+    lwm2m_list_t * instanceList;
+    lwm2m_read_callback_t     readFunc;
+    lwm2m_write_callback_t    writeFunc;
+    lwm2m_execute_callback_t  executeFunc;
+    lwm2m_create_callback_t   createFunc;
+    lwm2m_delete_callback_t   deleteFunc;
+    lwm2m_discover_callback_t discoverFunc;
+    void * userData;
+};
+
+/*
+ * LWM2M Servers
+ *
+ * Since LWM2M Server Object instances are not accessible to LWM2M servers,
+ * there is no need to store them as lwm2m_objects_t
+ */
+
+typedef enum
+{
+    STATE_DEREGISTERED = 0,        // not registered or boostrap not started
+    STATE_REG_PENDING,             // registration pending
+    STATE_REGISTERED,              // successfully registered
+    STATE_REG_FAILED,              // last registration failed
+    STATE_REG_UPDATE_PENDING,      // registration update pending
+    STATE_REG_UPDATE_NEEDED,       // registration update required
+    STATE_REG_FULL_UPDATE_NEEDED,  // registration update with objects required
+    STATE_DEREG_PENDING,           // deregistration pending
+    STATE_BS_HOLD_OFF,             // bootstrap hold off time
+    STATE_BS_INITIATED,            // bootstrap request sent
+    STATE_BS_PENDING,              // boostrap ongoing
+    STATE_BS_FINISHING,            // boostrap finish received
+    STATE_BS_FINISHED,             // bootstrap done
+    STATE_BS_FAILING,              // bootstrap error occurred
+    STATE_BS_FAILED,               // bootstrap failed
+} lwm2m_status_t;
+
+typedef enum
+{
+    BINDING_UNKNOWN = 0,
+    BINDING_U,   // UDP
+    BINDING_UQ,  // UDP queue mode
+    BINDING_S,   // SMS
+    BINDING_SQ,  // SMS queue mode
+    BINDING_US,  // UDP plus SMS
+    BINDING_UQS  // UDP queue mode plus SMS
+} lwm2m_binding_t;
+
+/*
+ * LWM2M block1 data
+ *
+ * Temporary data needed to handle block1 request.
+ * Currently support only one block1 request by server.
+ */
+typedef struct _lwm2m_block1_data_ lwm2m_block1_data_t;
+
+struct _lwm2m_block1_data_
+{
+    uint8_t *             block1buffer;     // data buffer
+    size_t                block1bufferSize; // buffer size
+    uint16_t              lastmid;          // mid of the last message received
+};
+
+typedef struct _lwm2m_server_
+{
+    struct _lwm2m_server_ * next;         // matches lwm2m_list_t::next
+    uint16_t                secObjInstID; // matches lwm2m_list_t::id
+    uint16_t                shortID;      // servers short ID, may be 0 for bootstrap server
+    time_t                  lifetime;     // lifetime of the registration in sec or 0 if default value (86400 sec), also used as hold off time for bootstrap servers
+    time_t                  registration; // date of the last registration in sec or end of client hold off time for bootstrap servers
+    lwm2m_binding_t         binding;      // client connection mode with this server
+    void *                  sessionH;
+    lwm2m_status_t          status;
+    char *                  location;
+    bool                    dirty;
+    lwm2m_block1_data_t *   block1Data;   // buffer to handle block1 data, should be replace by a list to support several block1 transfer by server.
+} lwm2m_server_t;
+
+
+/*
+ * LWM2M result callback
+ *
+ * When used with an observe, if 'data' is not nil, 'status' holds the observe counter.
+ */
+typedef void (*lwm2m_result_callback_t) (uint16_t clientID, lwm2m_uri_t * uriP, int status, lwm2m_media_type_t format, uint8_t * data, int dataLength, void * userData);
+
+/*
+ * LWM2M Observations
+ *
+ * Used to store observation of remote clients resources.
+ * status STATE_REG_PENDING means the observe request was sent to the client but not yet answered.
+ * status STATE_REGISTERED means the client acknowledged the observe request.
+ * status STATE_DEREG_PENDING means the user canceled the request before the client answered it.
+ */
+
+typedef struct _lwm2m_observation_
+{
+    struct _lwm2m_observation_ * next;  // matches lwm2m_list_t::next
+    uint16_t                     id;    // matches lwm2m_list_t::id
+    struct _lwm2m_client_ * clientP;
+    lwm2m_uri_t             uri;
+    lwm2m_status_t          status;
+    lwm2m_result_callback_t callback;
+    void *                  userData;
+} lwm2m_observation_t;
+
+/*
+ * LWM2M Link Attributes
+ *
+ * Used for observation parameters.
+ *
+ */
+
+#define LWM2M_ATTR_FLAG_MIN_PERIOD      (uint8_t)0x01
+#define LWM2M_ATTR_FLAG_MAX_PERIOD      (uint8_t)0x02
+#define LWM2M_ATTR_FLAG_GREATER_THAN    (uint8_t)0x04
+#define LWM2M_ATTR_FLAG_LESS_THAN       (uint8_t)0x08
+#define LWM2M_ATTR_FLAG_STEP            (uint8_t)0x10
+
+typedef struct
+{
+    uint8_t     toSet;
+    uint8_t     toClear;
+    uint32_t    minPeriod;
+    uint32_t    maxPeriod;
+    double      greaterThan;
+    double      lessThan;
+    double      step;
+} lwm2m_attributes_t;
+
+/*
+ * LWM2M Clients
+ *
+ * Be careful not to mix lwm2m_client_object_t used to store list of objects of remote clients
+ * and lwm2m_object_t describing objects exposed to remote servers.
+ *
+ */
+
+typedef struct _lwm2m_client_object_
+{
+    struct _lwm2m_client_object_ * next; // matches lwm2m_list_t::next
+    uint16_t                 id;         // matches lwm2m_list_t::id
+    lwm2m_list_t *           instanceList;
+} lwm2m_client_object_t;
+
+typedef struct _lwm2m_client_
+{
+    struct _lwm2m_client_ * next;       // matches lwm2m_list_t::next
+    uint16_t                internalID; // matches lwm2m_list_t::id
+    char *                  name;
+    lwm2m_binding_t         binding;
+    char *                  msisdn;
+    char *                  altPath;
+    bool                    supportJSON;
+    uint32_t                lifetime;
+    time_t                  endOfLife;
+    void *                  sessionH;
+    lwm2m_client_object_t * objectList;
+    lwm2m_observation_t *   observationList;
+} lwm2m_client_t;
+
+
+/*
+ * LWM2M transaction
+ *
+ * Adaptation of Erbium's coap_transaction_t
+ */
+
+typedef struct _lwm2m_transaction_ lwm2m_transaction_t;
+
+typedef void (*lwm2m_transaction_callback_t) (lwm2m_transaction_t * transacP, void * message);
+
+struct _lwm2m_transaction_
+{
+    lwm2m_transaction_t * next;  // matches lwm2m_list_t::next
+    uint16_t              mID;   // matches lwm2m_list_t::id
+    void *                peerH;
+    uint8_t               ack_received; // indicates, that the ACK was received
+    time_t                response_timeout; // timeout to wait for response, if token is used. When 0, use calculated acknowledge timeout.
+    uint8_t  retrans_counter;
+    time_t   retrans_time;
+    char objStringID[LWM2M_STRING_ID_MAX_LEN];
+    char instanceStringID[LWM2M_STRING_ID_MAX_LEN];
+    char resourceStringID[LWM2M_STRING_ID_MAX_LEN];
+    void * message;
+    uint16_t buffer_len;
+    uint8_t * buffer;
+    lwm2m_transaction_callback_t callback;
+    void * userData;
+};
+
+/*
+ * LWM2M observed resources
+ */
+typedef struct _lwm2m_watcher_
+{
+    struct _lwm2m_watcher_ * next;
+
+    bool active;
+    bool update;
+    lwm2m_server_t * server;
+    lwm2m_attributes_t * parameters;
+    uint8_t token[8];
+    size_t tokenLen;
+    time_t lastTime;
+    uint32_t counter;
+    uint16_t lastMid;
+    union
+    {
+        int64_t asInteger;
+        double  asFloat;
+    } lastValue;
+} lwm2m_watcher_t;
+
+typedef struct _lwm2m_observed_
+{
+    struct _lwm2m_observed_ * next;
+
+    lwm2m_uri_t uri;
+    lwm2m_watcher_t * watcherList;
+} lwm2m_observed_t;
+
+#ifdef LWM2M_CLIENT_MODE
+
+typedef enum
+{
+    STATE_INITIAL = 0,
+    STATE_BOOTSTRAP_REQUIRED,
+    STATE_BOOTSTRAPPING,
+    STATE_REGISTER_REQUIRED,
+    STATE_REGISTERING,
+    STATE_READY
+} lwm2m_client_state_t;
+
+#endif
+/*
+ * LWM2M Context
+ */
+
+#ifdef LWM2M_BOOTSTRAP_SERVER_MODE
+// In all the following APIs, the session handle MUST uniquely identify a peer.
+
+// LWM2M bootstrap callback
+// When a LWM2M client requests bootstrap information, the callback is called with status COAP_NO_ERROR, uriP is nil and
+// name is set. The callback must return a COAP_* error code. COAP_204_CHANGED for success.
+// After a lwm2m_bootstrap_delete() or a lwm2m_bootstrap_write(), the callback is called with the status returned by the
+// client, the URI of the operation (may be nil) and name is nil. The callback return value is ignored.
+typedef int (*lwm2m_bootstrap_callback_t) (void * sessionH, uint8_t status, lwm2m_uri_t * uriP, char * name, void * userData);
+#endif
+
+typedef struct
+{
+#ifdef LWM2M_CLIENT_MODE
+    lwm2m_client_state_t state;
+    char *               endpointName;
+    char *               msisdn;
+    char *               altPath;
+    lwm2m_server_t *     bootstrapServerList;
+    lwm2m_server_t *     serverList;
+    lwm2m_object_t *     objectList;
+    lwm2m_observed_t *   observedList;
+#endif
+#ifdef LWM2M_SERVER_MODE
+    lwm2m_client_t *        clientList;
+    lwm2m_result_callback_t monitorCallback;
+    void *                  monitorUserData;
+#endif
+#ifdef LWM2M_BOOTSTRAP_SERVER_MODE
+    lwm2m_bootstrap_callback_t bootstrapCallback;
+    void *                     bootstrapUserData;
+#endif
+    uint16_t                nextMID;
+    lwm2m_transaction_t *   transactionList;
+    void *                  userData;
+} lwm2m_context_t;
+
+
+// initialize a liblwm2m context.
+lwm2m_context_t * lwm2m_init(void * userData);
+// close a liblwm2m context.
+void lwm2m_close(lwm2m_context_t * contextP);
+
+// perform any required pending operation and adjust timeoutP to the maximal time interval to wait in seconds.
+int lwm2m_step(lwm2m_context_t * contextP, time_t * timeoutP);
+// dispatch received data to liblwm2m
+void lwm2m_handle_packet(lwm2m_context_t * contextP, uint8_t * buffer, int length, void * fromSessionH);
+
+#ifdef LWM2M_CLIENT_MODE
+// configure the client side with the Endpoint Name, binding, MSISDN (can be nil), alternative path
+// for objects (can be nil) and a list of objects.
+// LWM2M Security Object (ID 0) must be present with either a bootstrap server or a LWM2M server and
+// its matching LWM2M Server Object (ID 1) instance
+int lwm2m_configure(lwm2m_context_t * contextP, const char * endpointName, const char * msisdn, const char * altPath, uint16_t numObject, lwm2m_object_t * objectList[]);
+int lwm2m_add_object(lwm2m_context_t * contextP, lwm2m_object_t * objectP);
+int lwm2m_remove_object(lwm2m_context_t * contextP, uint16_t id);
+
+// send a registration update to the server specified by the server short identifier
+// or all if the ID is 0.
+// If withObjects is true, the registration update contains the object list.
+int lwm2m_update_registration(lwm2m_context_t * contextP, uint16_t shortServerID, bool withObjects);
+
+void lwm2m_resource_value_changed(lwm2m_context_t * contextP, lwm2m_uri_t * uriP);
+#endif
+
+#ifdef LWM2M_SERVER_MODE
+// Clients registration/deregistration monitoring API.
+// When a LWM2M client registers, the callback is called with status COAP_201_CREATED.
+// When a LWM2M client deregisters, the callback is called with status COAP_202_DELETED.
+// clientID is the internal ID of the LWM2M Client.
+// The callback's parameters uri, data, dataLength are always NULL.
+// The lwm2m_client_t is present in the lwm2m_context_t's clientList when the callback is called. On a deregistration, it deleted when the callback returns.
+void lwm2m_set_monitoring_callback(lwm2m_context_t * contextP, lwm2m_result_callback_t callback, void * userData);
+
+// Device Management APIs
+int lwm2m_dm_read(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_result_callback_t callback, void * userData);
+int lwm2m_dm_discover(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_result_callback_t callback, void * userData);
+int lwm2m_dm_write(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, int length, lwm2m_result_callback_t callback, void * userData);
+int lwm2m_dm_write_attributes(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_attributes_t * attrP, lwm2m_result_callback_t callback, void * userData);
+int lwm2m_dm_execute(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, int length, lwm2m_result_callback_t callback, void * userData);
+int lwm2m_dm_create(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, int length, lwm2m_result_callback_t callback, void * userData);
+int lwm2m_dm_delete(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_result_callback_t callback, void * userData);
+
+// Information Reporting APIs
+int lwm2m_observe(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_result_callback_t callback, void * userData);
+int lwm2m_observe_cancel(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_result_callback_t callback, void * userData);
+#endif
+
+#ifdef LWM2M_BOOTSTRAP_SERVER_MODE
+// Clients bootstrap request monitoring API.
+// When a LWM2M client sends a bootstrap request, the callback is called with the client's endpoint name.
+void lwm2m_set_bootstrap_callback(lwm2m_context_t * contextP, lwm2m_bootstrap_callback_t callback, void * userData);
+
+// Boostrap Interface APIs
+// if uriP is nil, a "Delete /" is sent to the client
+int lwm2m_bootstrap_delete(lwm2m_context_t * contextP, void * sessionH, lwm2m_uri_t * uriP);
+int lwm2m_bootstrap_write(lwm2m_context_t * contextP, void * sessionH, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length);
+int lwm2m_bootstrap_finish(lwm2m_context_t * contextP, void * sessionH);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r 3f48af28ebcd core/list.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/list.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ *
+ * Copyright (c) 2013 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
+ *    
+ *******************************************************************************/
+
+#include "internals.h"
+
+
+lwm2m_list_t * lwm2m_list_add(lwm2m_list_t * head,
+                              lwm2m_list_t * node)
+{
+    lwm2m_list_t * target;
+
+    if (NULL == head) return node;
+
+    if (head->id > node->id)
+    {
+        node->next = head;
+        return node;
+    }
+
+    target = head;
+    while (NULL != target->next && target->next->id < node->id)
+    {
+        target = target->next;
+    }
+
+    node->next = target->next;
+    target->next = node;
+
+    return head;
+}
+
+
+lwm2m_list_t * lwm2m_list_find(lwm2m_list_t * head,
+                               uint16_t id)
+{
+    while (NULL != head && head->id < id)
+    {
+        head = head->next;
+    }
+
+    if (NULL != head && head->id == id) return head;
+
+    return NULL;
+}
+
+
+lwm2m_list_t * lwm2m_list_remove(lwm2m_list_t * head,
+                                 uint16_t id,
+                                 lwm2m_list_t ** nodeP)
+{
+    lwm2m_list_t * target;
+
+    if (head == NULL)
+    {
+        if (nodeP) *nodeP = NULL;
+        return NULL;
+    }
+
+    if (head->id == id)
+    {
+        if (nodeP) *nodeP = head;
+        return head->next;
+    }
+
+    target = head;
+    while (NULL != target->next && target->next->id < id)
+    {
+        target = target->next;
+    }
+
+    if (NULL != target->next && target->next->id == id)
+    {
+        if (nodeP) *nodeP = target->next;
+        target->next = target->next->next;
+    }
+    else
+    {
+        if (nodeP) *nodeP = NULL;
+    }
+
+    return head;
+}
+
+uint16_t lwm2m_list_newId(lwm2m_list_t * head)
+{
+    uint16_t id;
+    lwm2m_list_t * target;
+
+    id = 0;
+    target = head;
+
+    while (target != NULL && id == target->id)
+    {
+        id = target->id + 1;
+        target = target->next;
+    }
+
+    return id;
+}
+
+void lwm2m_list_free(lwm2m_list_t * head)
+{
+    if (head != NULL)
+    {
+        lwm2m_list_t * nextP;
+
+        nextP = head->next;
+        lwm2m_free(head);
+        lwm2m_list_free(nextP);
+    }
+}
diff -r 000000000000 -r 3f48af28ebcd core/management.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/management.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,777 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Toby Jaffey - 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 <stdio.h>
+
+
+#ifdef LWM2M_CLIENT_MODE
+static int prv_readAttributes(multi_option_t * query,
+                              lwm2m_attributes_t * attrP)
+{
+    int64_t intValue;
+    double floatValue;
+
+    memset(attrP, 0, sizeof(lwm2m_attributes_t));
+
+    while (query != NULL)
+    {
+        if (lwm2m_strncmp((char *)query->data, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN) == 0)
+        {
+            if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_MIN_PERIOD)) return -1;
+            if (query->len == ATTR_MIN_PERIOD_LEN) return -1;
+
+            if (1 != utils_plainTextToInt64(query->data + ATTR_MIN_PERIOD_LEN, query->len - ATTR_MIN_PERIOD_LEN, &intValue)) return -1;
+            if (intValue < 0) return -1;
+
+            attrP->toSet |= LWM2M_ATTR_FLAG_MIN_PERIOD;
+            attrP->minPeriod = intValue;
+        }
+        else if (lwm2m_strncmp((char *)query->data, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN - 1) == 0)
+        {
+            if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_MIN_PERIOD)) return -1;
+            if (query->len != ATTR_MIN_PERIOD_LEN - 1) return -1;
+
+            attrP->toClear |= LWM2M_ATTR_FLAG_MIN_PERIOD;
+        }
+        else if (lwm2m_strncmp((char *)query->data, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN) == 0)
+        {
+            if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_MAX_PERIOD)) return -1;
+            if (query->len == ATTR_MAX_PERIOD_LEN) return -1;
+
+            if (1 != utils_plainTextToInt64(query->data + ATTR_MAX_PERIOD_LEN, query->len - ATTR_MAX_PERIOD_LEN, &intValue)) return -1;
+            if (intValue < 0) return -1;
+
+            attrP->toSet |= LWM2M_ATTR_FLAG_MAX_PERIOD;
+            attrP->maxPeriod = intValue;
+        }
+        else if (lwm2m_strncmp((char *)query->data, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN - 1) == 0)
+        {
+            if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_MAX_PERIOD)) return -1;
+            if (query->len != ATTR_MAX_PERIOD_LEN - 1) return -1;
+
+            attrP->toClear |= LWM2M_ATTR_FLAG_MAX_PERIOD;
+        }
+        else if (lwm2m_strncmp((char *)query->data, ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN) == 0)
+        {
+            if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_GREATER_THAN)) return -1;
+            if (query->len == ATTR_GREATER_THAN_LEN) return -1;
+
+            if (1 != utils_plainTextToFloat64(query->data + ATTR_GREATER_THAN_LEN, query->len - ATTR_GREATER_THAN_LEN, &floatValue)) return -1;
+
+            attrP->toSet |= LWM2M_ATTR_FLAG_GREATER_THAN;
+            attrP->greaterThan = floatValue;
+        }
+        else if (lwm2m_strncmp((char *)query->data, ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN - 1) == 0)
+        {
+            if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_GREATER_THAN)) return -1;
+            if (query->len != ATTR_GREATER_THAN_LEN - 1) return -1;
+
+            attrP->toClear |= LWM2M_ATTR_FLAG_GREATER_THAN;
+        }
+        else if (lwm2m_strncmp((char *)query->data, ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN) == 0)
+        {
+            if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_LESS_THAN)) return -1;
+            if (query->len == ATTR_LESS_THAN_LEN) return -1;
+
+            if (1 != utils_plainTextToFloat64(query->data + ATTR_LESS_THAN_LEN, query->len - ATTR_LESS_THAN_LEN, &floatValue)) return -1;
+
+            attrP->toSet |= LWM2M_ATTR_FLAG_LESS_THAN;
+            attrP->lessThan = floatValue;
+        }
+        else if (lwm2m_strncmp((char *)query->data, ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN - 1) == 0)
+        {
+            if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_LESS_THAN)) return -1;
+            if (query->len != ATTR_LESS_THAN_LEN - 1) return -1;
+
+            attrP->toClear |= LWM2M_ATTR_FLAG_LESS_THAN;
+        }
+        else if (lwm2m_strncmp((char *)query->data, ATTR_STEP_STR, ATTR_STEP_LEN) == 0)
+        {
+            if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_STEP)) return -1;
+            if (query->len == ATTR_STEP_LEN) return -1;
+
+            if (1 != utils_plainTextToFloat64(query->data + ATTR_STEP_LEN, query->len - ATTR_STEP_LEN, &floatValue)) return -1;
+            if (floatValue < 0) return -1;
+
+            attrP->toSet |= LWM2M_ATTR_FLAG_STEP;
+            attrP->step = floatValue;
+        }
+        else if (lwm2m_strncmp((char *)query->data, ATTR_STEP_STR, ATTR_STEP_LEN - 1) == 0)
+        {
+            if (0 != ((attrP->toSet | attrP->toClear) & LWM2M_ATTR_FLAG_STEP)) return -1;
+            if (query->len != ATTR_STEP_LEN - 1) return -1;
+
+            attrP->toClear |= LWM2M_ATTR_FLAG_STEP;
+        }
+        else return -1;
+
+        query = query->next;
+    }
+
+    return 0;
+}
+
+coap_status_t dm_handleRequest(lwm2m_context_t * contextP,
+                                lwm2m_uri_t * uriP,
+                                lwm2m_server_t * serverP,
+                                coap_packet_t * message,
+                                coap_packet_t * response)
+{
+    coap_status_t result;
+    lwm2m_media_type_t format;
+
+    LOG_ARG("Code: %02X, server status: %s", message->code, STR_STATUS(serverP->status));
+    LOG_URI(uriP);
+
+    if (IS_OPTION(message, COAP_OPTION_CONTENT_TYPE))
+    {
+        format = utils_convertMediaType(message->content_type);
+    }
+    else
+    {
+        format = LWM2M_CONTENT_TLV;
+    }
+
+    if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID)
+    {
+        return COAP_404_NOT_FOUND;
+    }
+
+    if (serverP->status != STATE_REGISTERED
+        && serverP->status != STATE_REG_UPDATE_NEEDED
+        && serverP->status != STATE_REG_FULL_UPDATE_NEEDED
+        && serverP->status != STATE_REG_UPDATE_PENDING)
+    {
+        return COAP_IGNORE;
+    }
+
+    // TODO: check ACL
+
+    switch (message->code)
+    {
+    case COAP_GET:
+        {
+            uint8_t * buffer = NULL;
+            size_t length = 0;
+            int res;
+
+            if (IS_OPTION(message, COAP_OPTION_OBSERVE))
+            {
+                lwm2m_data_t * dataP = NULL;
+                int size = 0;
+
+                result = object_readData(contextP, uriP, &size, &dataP);
+                if (COAP_205_CONTENT == result)
+                {
+                    result = observe_handleRequest(contextP, uriP, serverP, size, dataP, message, response);
+                    if (COAP_205_CONTENT == result)
+                    {
+                        res = lwm2m_data_serialize(uriP, size, dataP, &format, &buffer);
+                        if (res < 0)
+                        {
+                            result = COAP_500_INTERNAL_SERVER_ERROR;
+                        }
+                        else
+                        {
+                            length = (size_t)res;
+                            LOG_ARG("Observe Request[/%d/%d/%d]: %.*s\n", uriP->objectId, uriP->instanceId, uriP->resourceId, length, buffer);
+                        }
+                    }
+                    lwm2m_data_free(size, dataP);
+                }
+            }
+            else if (IS_OPTION(message, COAP_OPTION_ACCEPT)
+                  && message->accept_num == 1
+                  && message->accept[0] == APPLICATION_LINK_FORMAT)
+            {
+                format = LWM2M_CONTENT_LINK;
+                result = object_discover(contextP, uriP, serverP, &buffer, &length);
+            }
+            else
+            {
+                if (IS_OPTION(message, COAP_OPTION_ACCEPT))
+                {
+                    format = utils_convertMediaType(message->accept[0]);
+                }
+
+                result = object_read(contextP, uriP, &format, &buffer, &length);
+            }
+            if (COAP_205_CONTENT == result)
+            {
+                coap_set_header_content_type(response, format);
+                coap_set_payload(response, buffer, length);
+                // lwm2m_handle_packet will free buffer
+            }
+            else
+            {
+                lwm2m_free(buffer);
+            }
+        }
+        break;
+
+    case COAP_POST:
+        {
+            if (!LWM2M_URI_IS_SET_INSTANCE(uriP))
+            {
+                result = object_create(contextP, uriP, format, message->payload, message->payload_len);
+                if (result == COAP_201_CREATED)
+                {
+                    //longest uri is /65535/65535 = 12 + 1 (null) chars
+                    char location_path[13] = "";
+                    //instanceId expected
+                    if ((uriP->flag & LWM2M_URI_FLAG_INSTANCE_ID) == 0)
+                    {
+                        result = COAP_500_INTERNAL_SERVER_ERROR;
+                        break;
+                    }
+
+                    if (sprintf(location_path, "/%d/%d", uriP->objectId, uriP->instanceId) < 0)
+                    {
+                        result = COAP_500_INTERNAL_SERVER_ERROR;
+                        break;
+                    }
+                    coap_set_header_location_path(response, location_path);
+
+                    lwm2m_update_registration(contextP, 0, true);
+                }
+            }
+            else if (!LWM2M_URI_IS_SET_RESOURCE(uriP))
+            {
+                result = object_write(contextP, uriP, format, message->payload, message->payload_len);
+            }
+            else
+            {
+                result = object_execute(contextP, uriP, message->payload, message->payload_len);
+            }
+        }
+        break;
+
+    case COAP_PUT:
+        {
+            if (IS_OPTION(message, COAP_OPTION_URI_QUERY))
+            {
+                lwm2m_attributes_t attr;
+
+                if (0 != prv_readAttributes(message->uri_query, &attr))
+                {
+                    result = COAP_400_BAD_REQUEST;
+                }
+                else
+                {
+                    result = observe_setParameters(contextP, uriP, serverP, &attr);
+                }
+            }
+            else if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+            {
+                result = object_write(contextP, uriP, format, message->payload, message->payload_len);
+            }
+            else
+            {
+                result = COAP_400_BAD_REQUEST;
+            }
+        }
+        break;
+
+    case COAP_DELETE:
+        {
+            if (!LWM2M_URI_IS_SET_INSTANCE(uriP) || LWM2M_URI_IS_SET_RESOURCE(uriP))
+            {
+                result = COAP_400_BAD_REQUEST;
+            }
+            else
+            {
+                result = object_delete(contextP, uriP);
+                if (result == COAP_202_DELETED)
+                {
+                    lwm2m_update_registration(contextP, 0, true);
+                }
+            }
+        }
+        break;
+
+    default:
+        result = COAP_400_BAD_REQUEST;
+        break;
+    }
+
+    return result;
+}
+
+#endif
+
+#ifdef LWM2M_SERVER_MODE
+
+#define ID_AS_STRING_MAX_LEN 8
+
+static void prv_resultCallback(lwm2m_transaction_t * transacP,
+                               void * message)
+{
+    dm_data_t * dataP = (dm_data_t *)transacP->userData;
+
+    if (message == NULL)
+    {
+        dataP->callback(dataP->clientID,
+                        &dataP->uri,
+                        COAP_503_SERVICE_UNAVAILABLE,
+                        LWM2M_CONTENT_TEXT, NULL, 0,
+                        dataP->userData);
+    }
+    else
+    {
+        coap_packet_t * packet = (coap_packet_t *)message;
+
+        //if packet is a CREATE response and the instanceId was assigned by the client
+        if (packet->code == COAP_201_CREATED
+         && packet->location_path != NULL)
+        {
+            char * locationString = NULL;
+            int result = 0;
+            lwm2m_uri_t locationUri;
+
+            locationString = coap_get_multi_option_as_string(packet->location_path);
+            if (locationString == NULL)
+            {
+                LOG("Error: coap_get_multi_option_as_string() failed for Location_path option in prv_resultCallback()");
+                return;
+            }
+
+            result = lwm2m_stringToUri(locationString, strlen(locationString), &locationUri);
+            if (result == 0)
+            {
+                LOG("Error: lwm2m_stringToUri() failed for Location_path option in prv_resultCallback()");
+                lwm2m_free(locationString);
+                return;
+            }
+
+            ((dm_data_t*)transacP->userData)->uri.instanceId = locationUri.instanceId;
+            ((dm_data_t*)transacP->userData)->uri.flag = locationUri.flag;
+
+            lwm2m_free(locationString);
+        }
+
+        dataP->callback(dataP->clientID,
+                        &dataP->uri,
+                        packet->code,
+                        utils_convertMediaType(packet->content_type),
+                        packet->payload,
+                        packet->payload_len,
+                        dataP->userData);
+    }
+    lwm2m_free(dataP);
+}
+
+static int prv_makeOperation(lwm2m_context_t * contextP,
+                             uint16_t clientID,
+                             lwm2m_uri_t * uriP,
+                             coap_method_t method,
+                             lwm2m_media_type_t format,
+                             uint8_t * buffer,
+                             int length,
+                             lwm2m_result_callback_t callback,
+                             void * userData)
+{
+    lwm2m_client_t * clientP;
+    lwm2m_transaction_t * transaction;
+    dm_data_t * dataP;
+
+    clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
+    if (clientP == NULL) return COAP_404_NOT_FOUND;
+
+    transaction = transaction_new(clientP->sessionH, method, clientP->altPath, uriP, contextP->nextMID++, 4, NULL);
+    if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    if (method == COAP_GET)
+    {
+        coap_set_header_accept(transaction->message, format);
+    }
+    else if (buffer != NULL)
+    {
+        coap_set_header_content_type(transaction->message, format);
+        // TODO: Take care of fragmentation
+        coap_set_payload(transaction->message, buffer, length);
+    }
+
+    if (callback != NULL)
+    {
+        dataP = (dm_data_t *)lwm2m_malloc(sizeof(dm_data_t));
+        if (dataP == NULL)
+        {
+            transaction_free(transaction);
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t));
+        dataP->clientID = clientP->internalID;
+        dataP->callback = callback;
+        dataP->userData = userData;
+
+        transaction->callback = prv_resultCallback;
+        transaction->userData = (void *)dataP;
+    }
+
+    contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
+
+    return transaction_send(contextP, transaction);
+}
+
+int lwm2m_dm_read(lwm2m_context_t * contextP,
+                  uint16_t clientID,
+                  lwm2m_uri_t * uriP,
+                  lwm2m_result_callback_t callback,
+                  void * userData)
+{
+    lwm2m_client_t * clientP;
+    lwm2m_media_type_t format;
+
+    LOG_ARG("clientID: %d", clientID);
+    LOG_URI(uriP);
+
+    clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
+    if (clientP == NULL) return COAP_404_NOT_FOUND;
+
+    if (clientP->supportJSON == true)
+    {
+        format = LWM2M_CONTENT_JSON;
+    }
+    else
+    {
+        format = LWM2M_CONTENT_TLV;
+    }
+
+    return prv_makeOperation(contextP, clientID, uriP,
+                             COAP_GET,
+                             format,
+                             NULL, 0,
+                             callback, userData);
+}
+
+int lwm2m_dm_write(lwm2m_context_t * contextP,
+                   uint16_t clientID,
+                   lwm2m_uri_t * uriP,
+                   lwm2m_media_type_t format,
+                   uint8_t * buffer,
+                   int length,
+                   lwm2m_result_callback_t callback,
+                   void * userData)
+{
+    LOG_ARG("clientID: %d, format: %s, length: %d", clientID, STR_MEDIA_TYPE(format), length);
+    LOG_URI(uriP);
+    if (!LWM2M_URI_IS_SET_INSTANCE(uriP)
+     || length == 0)
+    {
+        return COAP_400_BAD_REQUEST;
+    }
+
+    if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+    {
+        return prv_makeOperation(contextP, clientID, uriP,
+                                  COAP_PUT,
+                                  format, buffer, length,
+                                  callback, userData);
+    }
+    else
+    {
+        return prv_makeOperation(contextP, clientID, uriP,
+                                  COAP_POST,
+                                  format, buffer, length,
+                                  callback, userData);
+    }
+}
+
+int lwm2m_dm_execute(lwm2m_context_t * contextP,
+                     uint16_t clientID,
+                     lwm2m_uri_t * uriP,
+                     lwm2m_media_type_t format,
+                     uint8_t * buffer,
+                     int length,
+                     lwm2m_result_callback_t callback,
+                     void * userData)
+{
+    LOG_ARG("clientID: %d, format: %s, length: %d", clientID, STR_MEDIA_TYPE(format), length);
+    LOG_URI(uriP);
+    if (!LWM2M_URI_IS_SET_RESOURCE(uriP))
+    {
+        return COAP_400_BAD_REQUEST;
+    }
+
+    return prv_makeOperation(contextP, clientID, uriP,
+                              COAP_POST,
+                              format, buffer, length,
+                              callback, userData);
+}
+
+int lwm2m_dm_create(lwm2m_context_t * contextP,
+                    uint16_t clientID,
+                    lwm2m_uri_t * uriP,
+                    lwm2m_media_type_t format,
+                    uint8_t * buffer,
+                    int length,
+                    lwm2m_result_callback_t callback,
+                    void * userData)
+{
+    LOG_ARG("clientID: %d, format: %s, length: %d", clientID, STR_MEDIA_TYPE(format), length);
+    LOG_URI(uriP);
+
+    if (LWM2M_URI_IS_SET_INSTANCE(uriP)
+     || length == 0)
+    {
+        return COAP_400_BAD_REQUEST;
+    }
+
+    return prv_makeOperation(contextP, clientID, uriP,
+                              COAP_POST,
+                              format, buffer, length,
+                              callback, userData);
+}
+
+int lwm2m_dm_delete(lwm2m_context_t * contextP,
+                    uint16_t clientID,
+                    lwm2m_uri_t * uriP,
+                    lwm2m_result_callback_t callback,
+                    void * userData)
+{
+    LOG_ARG("clientID: %d", clientID);
+    LOG_URI(uriP);
+    if (!LWM2M_URI_IS_SET_INSTANCE(uriP)
+     || LWM2M_URI_IS_SET_RESOURCE(uriP))
+    {
+        return COAP_400_BAD_REQUEST;
+    }
+
+    return prv_makeOperation(contextP, clientID, uriP,
+                              COAP_DELETE,
+                              LWM2M_CONTENT_TEXT, NULL, 0,
+                              callback, userData);
+}
+
+int lwm2m_dm_write_attributes(lwm2m_context_t * contextP,
+                              uint16_t clientID,
+                              lwm2m_uri_t * uriP,
+                              lwm2m_attributes_t * attrP,
+                              lwm2m_result_callback_t callback,
+                              void * userData)
+{
+#define _PRV_BUFFER_SIZE 32
+    lwm2m_client_t * clientP;
+    lwm2m_transaction_t * transaction;
+    coap_packet_t * coap_pkt;
+    uint8_t buffer[_PRV_BUFFER_SIZE];
+    size_t length;
+
+    LOG_ARG("clientID: %d", clientID);
+    LOG_URI(uriP);
+    if (attrP == NULL) return COAP_400_BAD_REQUEST;
+
+    if (0 != (attrP->toSet & attrP->toClear)) return COAP_400_BAD_REQUEST;
+    if (0 != (attrP->toSet & ATTR_FLAG_NUMERIC) && !LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST;
+    if (ATTR_FLAG_NUMERIC == (attrP->toSet & ATTR_FLAG_NUMERIC)
+     && (attrP->lessThan + 2 * attrP->step >= attrP->greaterThan)) return COAP_400_BAD_REQUEST;
+
+    clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
+    if (clientP == NULL) return COAP_404_NOT_FOUND;
+
+    transaction = transaction_new(clientP->sessionH, COAP_PUT, clientP->altPath, uriP, contextP->nextMID++, 4, NULL);
+    if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    if (callback != NULL)
+    {
+        dm_data_t * dataP;
+
+        dataP = (dm_data_t *)lwm2m_malloc(sizeof(dm_data_t));
+        if (dataP == NULL)
+        {
+            transaction_free(transaction);
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t));
+        dataP->clientID = clientP->internalID;
+        dataP->callback = callback;
+        dataP->userData = userData;
+
+        transaction->callback = prv_resultCallback;
+        transaction->userData = (void *)dataP;
+    }
+
+    coap_pkt = (coap_packet_t *)transaction->message;
+    free_multi_option(coap_pkt->uri_query);
+    if (attrP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD)
+    {
+        memcpy(buffer, ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN);
+        length = utils_intToText(attrP->minPeriod, buffer + ATTR_MIN_PERIOD_LEN, _PRV_BUFFER_SIZE - ATTR_MIN_PERIOD_LEN);
+        if (length == 0)
+        {
+            transaction_free(transaction);
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_MIN_PERIOD_LEN + length, 0);
+        SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    }
+    if (attrP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD)
+    {
+        memcpy(buffer, ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN);
+        length = utils_intToText(attrP->maxPeriod, buffer + ATTR_MAX_PERIOD_LEN, _PRV_BUFFER_SIZE - ATTR_MAX_PERIOD_LEN);
+        if (length == 0)
+        {
+            transaction_free(transaction);
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_MAX_PERIOD_LEN + length, 0);
+        SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    }
+    if (attrP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN)
+    {
+        memcpy(buffer, ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN);
+        length = utils_floatToText(attrP->greaterThan, buffer + ATTR_GREATER_THAN_LEN, _PRV_BUFFER_SIZE - ATTR_GREATER_THAN_LEN);
+        if (length == 0)
+        {
+            transaction_free(transaction);
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_GREATER_THAN_LEN + length, 0);
+        SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    }
+    if (attrP->toSet & LWM2M_ATTR_FLAG_LESS_THAN)
+    {
+        memcpy(buffer, ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN);
+        length = utils_floatToText(attrP->lessThan, buffer + ATTR_LESS_THAN_LEN, _PRV_BUFFER_SIZE - ATTR_LESS_THAN_LEN);
+        if (length == 0)
+        {
+            transaction_free(transaction);
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_LESS_THAN_LEN + length, 0);
+        SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    }
+    if (attrP->toSet & LWM2M_ATTR_FLAG_STEP)
+    {
+        memcpy(buffer, ATTR_STEP_STR, ATTR_STEP_LEN);
+        length = utils_floatToText(attrP->step, buffer + ATTR_STEP_LEN, _PRV_BUFFER_SIZE - ATTR_STEP_LEN);
+        if (length == 0)
+        {
+            transaction_free(transaction);
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        coap_add_multi_option(&(coap_pkt->uri_query), buffer, ATTR_STEP_LEN + length, 0);
+        SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    }
+    if (attrP->toClear & LWM2M_ATTR_FLAG_MIN_PERIOD)
+    {
+        coap_add_multi_option(&(coap_pkt->uri_query), ATTR_MIN_PERIOD_STR, ATTR_MIN_PERIOD_LEN -1, 0);
+        SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    }
+    if (attrP->toClear & LWM2M_ATTR_FLAG_MAX_PERIOD)
+    {
+        coap_add_multi_option(&(coap_pkt->uri_query), ATTR_MAX_PERIOD_STR, ATTR_MAX_PERIOD_LEN - 1, 0);
+        SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    }
+    if (attrP->toClear & LWM2M_ATTR_FLAG_GREATER_THAN)
+    {
+        coap_add_multi_option(&(coap_pkt->uri_query), ATTR_GREATER_THAN_STR, ATTR_GREATER_THAN_LEN - 1, 0);
+        SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    }
+    if (attrP->toClear & LWM2M_ATTR_FLAG_LESS_THAN)
+    {
+        coap_add_multi_option(&(coap_pkt->uri_query), ATTR_LESS_THAN_STR, ATTR_LESS_THAN_LEN - 1, 0);
+        SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    }
+    if (attrP->toClear & LWM2M_ATTR_FLAG_STEP)
+    {
+        coap_add_multi_option(&(coap_pkt->uri_query), ATTR_STEP_STR, ATTR_STEP_LEN - 1, 0);
+        SET_OPTION(coap_pkt, COAP_OPTION_URI_QUERY);
+    }
+
+    contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
+
+    return transaction_send(contextP, transaction);
+}
+
+int lwm2m_dm_discover(lwm2m_context_t * contextP,
+                      uint16_t clientID,
+                      lwm2m_uri_t * uriP,
+                      lwm2m_result_callback_t callback,
+                      void * userData)
+{
+    lwm2m_client_t * clientP;
+    lwm2m_transaction_t * transaction;
+    dm_data_t * dataP;
+
+    LOG_ARG("clientID: %d", clientID);
+    LOG_URI(uriP);
+    clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
+    if (clientP == NULL) return COAP_404_NOT_FOUND;
+
+    transaction = transaction_new(clientP->sessionH, COAP_GET, clientP->altPath, uriP, contextP->nextMID++, 4, NULL);
+    if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    coap_set_header_accept(transaction->message, LWM2M_CONTENT_LINK);
+
+    if (callback != NULL)
+    {
+        dataP = (dm_data_t *)lwm2m_malloc(sizeof(dm_data_t));
+        if (dataP == NULL)
+        {
+            transaction_free(transaction);
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        memcpy(&dataP->uri, uriP, sizeof(lwm2m_uri_t));
+        dataP->clientID = clientP->internalID;
+        dataP->callback = callback;
+        dataP->userData = userData;
+
+        transaction->callback = prv_resultCallback;
+        transaction->userData = (void *)dataP;
+    }
+
+    contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
+
+    return transaction_send(contextP, transaction);
+}
+
+#endif
diff -r 000000000000 -r 3f48af28ebcd core/objects.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/objects.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,830 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Fabien Fleutot - Please refer to git log
+ *    Toby Jaffey - Please refer to git log
+ *    Benjamin Cabé - 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"
+
+#ifdef LWM2M_CLIENT_MODE
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+
+uint8_t object_checkReadable(lwm2m_context_t * contextP,
+                             lwm2m_uri_t * uriP)
+{
+    coap_status_t result;
+    lwm2m_object_t * targetP;
+    lwm2m_data_t * dataP = NULL;
+    int size;
+
+    LOG_URI(uriP);
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId);
+    if (NULL == targetP) return COAP_404_NOT_FOUND;
+    if (NULL == targetP->readFunc) return COAP_405_METHOD_NOT_ALLOWED;
+
+    if (!LWM2M_URI_IS_SET_INSTANCE(uriP)) return COAP_205_CONTENT;
+
+    if (NULL == lwm2m_list_find(targetP->instanceList, uriP->instanceId)) return COAP_404_NOT_FOUND;
+
+    if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_205_CONTENT;
+
+    size = 1;
+    dataP = lwm2m_data_new(1);
+    if (dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    dataP->id = uriP->resourceId;
+
+    result = targetP->readFunc(uriP->instanceId, &size, &dataP, targetP);
+    lwm2m_data_free(1, dataP);
+
+    return result;
+}
+
+uint8_t object_checkNumeric(lwm2m_context_t * contextP,
+                            lwm2m_uri_t * uriP)
+{
+    coap_status_t result;
+    lwm2m_object_t * targetP;
+    lwm2m_data_t * dataP = NULL;
+    int size;
+
+    LOG_URI(uriP);
+    if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_405_METHOD_NOT_ALLOWED;
+
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId);
+    if (NULL == targetP) return COAP_404_NOT_FOUND;
+    if (NULL == targetP->readFunc) return COAP_405_METHOD_NOT_ALLOWED;
+
+    size = 1;
+    dataP = lwm2m_data_new(1);
+    if (dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    dataP->id = uriP->resourceId;
+
+    result = targetP->readFunc(uriP->instanceId, &size, &dataP, targetP);
+    if (result == COAP_205_CONTENT)
+    {
+        switch (dataP->type)
+        {
+        case LWM2M_TYPE_INTEGER:
+        case LWM2M_TYPE_FLOAT:
+            break;
+        default:
+            result = COAP_405_METHOD_NOT_ALLOWED;
+        }
+    }
+
+    lwm2m_data_free(1, dataP);
+
+    return result;
+}
+
+coap_status_t object_readData(lwm2m_context_t * contextP,
+                               lwm2m_uri_t * uriP,
+                               int * sizeP,
+                               lwm2m_data_t ** dataP)
+{
+    coap_status_t result;
+    lwm2m_object_t * targetP;
+
+    LOG_URI(uriP);
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId);
+    if (NULL == targetP) return COAP_404_NOT_FOUND;
+    if (NULL == targetP->readFunc) return COAP_405_METHOD_NOT_ALLOWED;
+
+    if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+    {
+        if (NULL == lwm2m_list_find(targetP->instanceList, uriP->instanceId)) return COAP_404_NOT_FOUND;
+
+        // single instance read
+        if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+        {
+            *sizeP = 1;
+            *dataP = lwm2m_data_new(*sizeP);
+            if (*dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+            (*dataP)->id = uriP->resourceId;
+        }
+
+        result = targetP->readFunc(uriP->instanceId, sizeP, dataP, targetP);
+    }
+    else
+    {
+        // multiple object instances read
+        lwm2m_list_t * instanceP;
+        int i;
+
+        result = COAP_205_CONTENT;
+
+        *sizeP = 0;
+        for (instanceP = targetP->instanceList; instanceP != NULL ; instanceP = instanceP->next)
+        {
+            (*sizeP)++;
+        }
+
+        if (*sizeP == 0)
+        {
+            *dataP = NULL;
+        }
+        else
+        {
+            *dataP = lwm2m_data_new(*sizeP);
+            if (*dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+            instanceP = targetP->instanceList;
+            i = 0;
+            while (instanceP != NULL && result == COAP_205_CONTENT)
+            {
+                result = targetP->readFunc(instanceP->id, (int*)&((*dataP)[i].value.asChildren.count), &((*dataP)[i].value.asChildren.array), targetP);
+                (*dataP)[i].type = LWM2M_TYPE_OBJECT_INSTANCE;
+                (*dataP)[i].id = instanceP->id;
+                i++;
+                instanceP = instanceP->next;
+            }
+        }
+    }
+
+    LOG_ARG("result: %u.%2u, size: %d", (result & 0xFF) >> 5, (result & 0x1F), *sizeP);
+    return result;
+}
+
+coap_status_t object_read(lwm2m_context_t * contextP,
+                          lwm2m_uri_t * uriP,
+                          lwm2m_media_type_t * formatP,
+                          uint8_t ** bufferP,
+                          size_t * lengthP)
+{
+    coap_status_t result;
+    lwm2m_data_t * dataP = NULL;
+    int size = 0;
+    int res;
+
+    LOG_URI(uriP);
+    result = object_readData(contextP, uriP, &size, &dataP);
+
+    if (result == COAP_205_CONTENT)
+    {
+        res = lwm2m_data_serialize(uriP, size, dataP, formatP, bufferP);
+        if (res < 0)
+        {
+            result = COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        else
+        {
+            *lengthP = (size_t)res;
+        }
+    }
+    lwm2m_data_free(size, dataP);
+
+    LOG_ARG("result: %u.%2u, length: %d", (result & 0xFF) >> 5, (result & 0x1F), *lengthP);
+
+    return result;
+}
+
+coap_status_t object_write(lwm2m_context_t * contextP,
+                           lwm2m_uri_t * uriP,
+                           lwm2m_media_type_t format,
+                           uint8_t * buffer,
+                           size_t length)
+{
+    coap_status_t result = NO_ERROR;
+    lwm2m_object_t * targetP;
+    lwm2m_data_t * dataP = NULL;
+    int size = 0;
+
+    LOG_URI(uriP);
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId);
+    if (NULL == targetP)
+    {
+        result = COAP_404_NOT_FOUND;
+    }
+    else if (NULL == targetP->writeFunc)
+    {
+        result = COAP_405_METHOD_NOT_ALLOWED;
+    }
+    else
+    {
+        size = lwm2m_data_parse(uriP, buffer, length, format, &dataP);
+        if (size == 0)
+        {
+            result = COAP_406_NOT_ACCEPTABLE;
+        }
+    }
+    if (result == NO_ERROR)
+    {
+        result = targetP->writeFunc(uriP->instanceId, size, dataP, targetP);
+        lwm2m_data_free(size, dataP);
+    }
+
+    LOG_ARG("result: %u.%2u", (result & 0xFF) >> 5, (result & 0x1F));
+
+    return result;
+}
+
+coap_status_t object_execute(lwm2m_context_t * contextP,
+                             lwm2m_uri_t * uriP,
+                             uint8_t * buffer,
+                             size_t length)
+{
+    lwm2m_object_t * targetP;
+
+    LOG_URI(uriP);
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId);
+    if (NULL == targetP) return COAP_404_NOT_FOUND;
+    if (NULL == targetP->executeFunc) return COAP_405_METHOD_NOT_ALLOWED;
+    if (NULL == lwm2m_list_find(targetP->instanceList, uriP->instanceId)) return COAP_404_NOT_FOUND;
+
+    return targetP->executeFunc(uriP->instanceId, uriP->resourceId, buffer, length, targetP);
+}
+
+coap_status_t object_create(lwm2m_context_t * contextP,
+                            lwm2m_uri_t * uriP,
+                            lwm2m_media_type_t format,
+                            uint8_t * buffer,
+                            size_t length)
+{
+    lwm2m_object_t * targetP;
+    lwm2m_data_t * dataP = NULL;
+    int size = 0;
+    uint8_t result;
+
+    LOG_URI(uriP);
+
+    if (length == 0 || buffer == 0)
+    {
+        return COAP_400_BAD_REQUEST;
+    }
+
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId);
+    if (NULL == targetP) return COAP_404_NOT_FOUND;
+    if (NULL == targetP->createFunc) return COAP_405_METHOD_NOT_ALLOWED;
+
+    size = lwm2m_data_parse(uriP, buffer, length, format, &dataP);
+    if (size <= 0) return COAP_400_BAD_REQUEST;
+
+    switch (dataP[0].type)
+    {
+    case LWM2M_TYPE_OBJECT:
+        result = COAP_400_BAD_REQUEST;
+        goto exit;
+
+    case LWM2M_TYPE_OBJECT_INSTANCE:
+        if (size != 1)
+        {
+            result = COAP_400_BAD_REQUEST;
+            goto exit;
+        }
+        if (NULL != lwm2m_list_find(targetP->instanceList, dataP[0].id))
+        {
+            // Instance already exists
+            result = COAP_406_NOT_ACCEPTABLE;
+            goto exit;
+        }
+        result = targetP->createFunc(dataP[0].id, dataP[0].value.asChildren.count, dataP[0].value.asChildren.array, targetP);
+        uriP->instanceId = dataP[0].id;
+        uriP->flag |= LWM2M_URI_FLAG_INSTANCE_ID;
+        break;
+
+    default:
+        if (!LWM2M_URI_IS_SET_INSTANCE(uriP))
+        {
+            uriP->instanceId = lwm2m_list_newId(targetP->instanceList);
+            uriP->flag |= LWM2M_URI_FLAG_INSTANCE_ID;
+        }
+        result = targetP->createFunc(uriP->instanceId, size, dataP, targetP);
+        break;
+    }
+
+exit:
+    lwm2m_data_free(size, dataP);
+
+    LOG_ARG("result: %u.%2u", (result & 0xFF) >> 5, (result & 0x1F));
+
+    return result;
+}
+
+coap_status_t object_delete(lwm2m_context_t * contextP,
+                            lwm2m_uri_t * uriP)
+{
+    lwm2m_object_t * objectP;
+    coap_status_t result;
+
+    LOG_URI(uriP);
+    objectP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId);
+    if (NULL == objectP) return COAP_404_NOT_FOUND;
+    if (NULL == objectP->deleteFunc) return COAP_405_METHOD_NOT_ALLOWED;
+
+    LOG("Entering");
+
+    if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+    {
+        result = objectP->deleteFunc(uriP->instanceId, objectP);
+    }
+    else
+    {
+        lwm2m_list_t * instanceP;
+
+        result = COAP_202_DELETED;
+        instanceP = objectP->instanceList;
+        while (NULL != instanceP
+            && result == COAP_202_DELETED)
+        {
+            result = objectP->deleteFunc(instanceP->id, objectP);
+            instanceP = objectP->instanceList;
+        }
+    }
+
+    LOG_ARG("result: %u.%2u", (result & 0xFF) >> 5, (result & 0x1F));
+
+    return result;
+}
+
+coap_status_t object_discover(lwm2m_context_t * contextP,
+                              lwm2m_uri_t * uriP,
+                              lwm2m_server_t * serverP,
+                              uint8_t ** bufferP,
+                              size_t * lengthP)
+{
+    coap_status_t result;
+    lwm2m_object_t * targetP;
+    lwm2m_data_t * dataP = NULL;
+    int size = 0;
+
+    LOG_URI(uriP);
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId);
+    if (NULL == targetP) return COAP_404_NOT_FOUND;
+    if (NULL == targetP->discoverFunc) return COAP_501_NOT_IMPLEMENTED;
+
+    if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+    {
+        if (NULL == lwm2m_list_find(targetP->instanceList, uriP->instanceId)) return COAP_404_NOT_FOUND;
+
+        // single instance read
+        if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+        {
+            size = 1;
+            dataP = lwm2m_data_new(size);
+            if (dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+            dataP->id = uriP->resourceId;
+        }
+
+        result = targetP->discoverFunc(uriP->instanceId, &size, &dataP, targetP);
+    }
+    else
+    {
+        // multiple object instances read
+        lwm2m_list_t * instanceP;
+        int i;
+
+        result = COAP_205_CONTENT;
+
+        size = 0;
+        for (instanceP = targetP->instanceList; instanceP != NULL ; instanceP = instanceP->next)
+        {
+            size++;
+        }
+
+        if (size != 0)
+        {
+            dataP = lwm2m_data_new(size);
+            if (dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+            instanceP = targetP->instanceList;
+            i = 0;
+            while (instanceP != NULL && result == COAP_205_CONTENT)
+            {
+                result = targetP->discoverFunc(instanceP->id, (int*)&(dataP[i].value.asChildren.count), &(dataP[i].value.asChildren.array), targetP);
+                dataP[i].type = LWM2M_TYPE_OBJECT_INSTANCE;
+                dataP[i].id = instanceP->id;
+                i++;
+                instanceP = instanceP->next;
+            }
+        }
+    }
+
+    if (result == COAP_205_CONTENT)
+    {
+        int len;
+
+        len = discover_serialize(contextP, uriP, serverP, size, dataP, bufferP);
+        if (len <= 0) result = COAP_500_INTERNAL_SERVER_ERROR;
+        else *lengthP = len;
+    }
+    lwm2m_data_free(size, dataP);
+
+    LOG_ARG("result: %u.%2u", (result & 0xFF) >> 5, (result & 0x1F));
+
+    return result;
+}
+
+bool object_isInstanceNew(lwm2m_context_t * contextP,
+                          uint16_t objectId,
+                          uint16_t instanceId)
+{
+    lwm2m_object_t * targetP;
+
+    LOG("Entering");
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, objectId);
+    if (targetP != NULL)
+    {
+        if (NULL != lwm2m_list_find(targetP->instanceList, instanceId))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static int prv_getObjectTemplate(uint8_t * buffer,
+                                 size_t length,
+                                 uint16_t id)
+{
+    int index;
+    int result;
+
+    if (length < REG_OBJECT_MIN_LEN) return -1;
+
+    buffer[0] = '<';
+    buffer[1] = '/';
+    index = 2;
+
+    result = utils_intCopy((char *)buffer + index, length - index, id);
+    if (result < 0) return -1;
+    index += result;
+
+    if (length - index < REG_OBJECT_MIN_LEN - 3) return -1;
+    buffer[index] = '/';
+    index++;
+
+    return index;
+}
+
+int object_getRegisterPayload(lwm2m_context_t * contextP,
+                           uint8_t * buffer,
+                           size_t bufferLen)
+{
+    size_t index;
+    int result;
+    lwm2m_object_t * objectP;
+
+    LOG("Entering");
+    // index can not be greater than bufferLen
+    index = 0;
+
+    result = utils_stringCopy((char *)buffer, bufferLen, REG_START);
+    if (result < 0) return 0;
+    index += result;
+
+    if ((contextP->altPath != NULL)
+     && (contextP->altPath[0] != 0))
+    {
+        result = utils_stringCopy((char *)buffer + index, bufferLen - index, contextP->altPath);
+    }
+    else
+    {
+        result = utils_stringCopy((char *)buffer + index, bufferLen - index, REG_DEFAULT_PATH);
+    }
+    if (result < 0) return 0;
+    index += result;
+
+    result = utils_stringCopy((char *)buffer + index, bufferLen - index, REG_LWM2M_RESOURCE_TYPE);
+    if (result < 0) return 0;
+    index += result;
+
+    for (objectP = contextP->objectList; objectP != NULL; objectP = objectP->next)
+    {
+        size_t start;
+        size_t length;
+
+        if (objectP->objID == LWM2M_SECURITY_OBJECT_ID) continue;
+
+        start = index;
+        result = prv_getObjectTemplate(buffer + index, bufferLen - index, objectP->objID);
+        if (result < 0) return 0;
+        length = result;
+        index += length;
+
+        if (objectP->instanceList == NULL)
+        {
+            index--;
+            result = utils_stringCopy((char *)buffer + index, bufferLen - index, REG_PATH_END);
+            if (result < 0) return 0;
+            index += result;
+        }
+        else
+        {
+            lwm2m_list_t * targetP;
+            for (targetP = objectP->instanceList ; targetP != NULL ; targetP = targetP->next)
+            {
+                if (bufferLen - index <= length) return 0;
+
+                if (index != start + length)
+                {
+                    memcpy(buffer + index, buffer + start, length);
+                    index += length;
+                }
+
+                result = utils_intCopy((char *)buffer + index, bufferLen - index, targetP->id);
+                if (result < 0) return 0;
+                index += result;
+
+                result = utils_stringCopy((char *)buffer + index, bufferLen - index, REG_PATH_END);
+                if (result < 0) return 0;
+                index += result;
+            }
+        }
+    }
+
+    if (index > 0)
+    {
+        index = index - 1;  // remove trailing ','
+    }
+
+    buffer[index] = 0;
+
+    return index;
+}
+
+static lwm2m_list_t * prv_findServerInstance(lwm2m_object_t * objectP,
+                                             uint16_t shortID)
+{
+    lwm2m_list_t * instanceP;
+
+    instanceP = objectP->instanceList;
+    while (NULL != instanceP)
+    {
+        int64_t value;
+        lwm2m_data_t * dataP;
+        int size;
+
+        size = 1;
+        dataP = lwm2m_data_new(size);
+        if (dataP == NULL) return NULL;
+        dataP->id = LWM2M_SERVER_SHORT_ID_ID;
+
+        if (objectP->readFunc(instanceP->id, &size, &dataP, objectP) != COAP_205_CONTENT)
+        {
+            lwm2m_data_free(size, dataP);
+            return NULL;
+        }
+
+        if (1 == lwm2m_data_decode_int(dataP, &value))
+        {
+            if (value == shortID)
+            {
+                lwm2m_data_free(size, dataP);
+                break;
+            }
+        }
+        lwm2m_data_free(size, dataP);
+        instanceP = instanceP->next;
+    }
+
+    return instanceP;
+}
+
+static int prv_getMandatoryInfo(lwm2m_object_t * objectP,
+                                uint16_t instanceID,
+                                lwm2m_server_t * targetP)
+{
+    lwm2m_data_t * dataP;
+    int size;
+    int64_t value;
+
+    size = 2;
+    dataP = lwm2m_data_new(size);
+    if (dataP == NULL) return -1;
+    dataP[0].id = LWM2M_SERVER_LIFETIME_ID;
+    dataP[1].id = LWM2M_SERVER_BINDING_ID;
+
+    if (objectP->readFunc(instanceID, &size, &dataP, objectP) != COAP_205_CONTENT)
+    {
+        lwm2m_data_free(size, dataP);
+        return -1;
+    }
+
+    if (0 == lwm2m_data_decode_int(dataP, &value)
+     || value < 0 || value >0xFFFFFFFF)             // This is an implementation limit
+    {
+        lwm2m_data_free(size, dataP);
+        return -1;
+    }
+    targetP->lifetime = value;
+
+    targetP->binding = utils_stringToBinding(dataP[1].value.asBuffer.buffer, dataP[1].value.asBuffer.length);
+
+    lwm2m_data_free(size, dataP);
+
+    if (targetP->binding == BINDING_UNKNOWN)
+    {
+        return -1;
+    }
+
+    return 0;
+}
+
+int object_getServers(lwm2m_context_t * contextP)
+{
+    lwm2m_object_t * objectP;
+    lwm2m_object_t * securityObjP = NULL;
+    lwm2m_object_t * serverObjP = NULL;
+    lwm2m_list_t * securityInstP;   // instanceID of the server in the LWM2M Security Object
+
+    LOG("Entering");
+
+    for (objectP = contextP->objectList; objectP != NULL; objectP = objectP->next)
+    {
+        if (objectP->objID == LWM2M_SECURITY_OBJECT_ID)
+        {
+            securityObjP = objectP;
+        }
+        else if (objectP->objID == LWM2M_SERVER_OBJECT_ID)
+        {
+            serverObjP = objectP;
+        }
+    }
+
+    if (NULL == securityObjP) return -1;
+
+    securityInstP = securityObjP->instanceList;
+    while (securityInstP != NULL)
+    {
+        if (LWM2M_LIST_FIND(contextP->bootstrapServerList, securityInstP->id) == NULL
+         && LWM2M_LIST_FIND(contextP->serverList, securityInstP->id) == NULL)
+        {
+            // This server is new. eg created by last bootstrap
+
+            lwm2m_data_t * dataP;
+            int size;
+            lwm2m_server_t * targetP;
+            bool isBootstrap;
+            int64_t value = 0;
+
+            size = 3;
+            dataP = lwm2m_data_new(size);
+            if (dataP == NULL) return -1;
+            dataP[0].id = LWM2M_SECURITY_BOOTSTRAP_ID;
+            dataP[1].id = LWM2M_SECURITY_SHORT_SERVER_ID;
+            dataP[2].id = LWM2M_SECURITY_HOLD_OFF_ID;
+
+            if (securityObjP->readFunc(securityInstP->id, &size, &dataP, securityObjP) != COAP_205_CONTENT)
+            {
+                lwm2m_data_free(size, dataP);
+                return -1;
+            }
+
+            targetP = (lwm2m_server_t *)lwm2m_malloc(sizeof(lwm2m_server_t));
+            if (targetP == NULL) {
+                lwm2m_data_free(size, dataP);
+                return -1;
+            }
+            memset(targetP, 0, sizeof(lwm2m_server_t));
+            targetP->secObjInstID = securityInstP->id;
+
+            if (0 == lwm2m_data_decode_bool(dataP + 0, &isBootstrap))
+            {
+                lwm2m_free(targetP);
+                lwm2m_data_free(size, dataP);
+                return -1;
+            }
+
+            if (0 == lwm2m_data_decode_int(dataP + 1, &value)
+             || value < (isBootstrap ? 0 : 1) || value > 0xFFFF)                // 0 is forbidden as a Short Server ID
+            {
+                lwm2m_free(targetP);
+                lwm2m_data_free(size, dataP);
+                return -1;
+            }
+            targetP->shortID = value;
+
+            if (isBootstrap == true)
+            {
+                if (0 == lwm2m_data_decode_int(dataP + 2, &value)
+                 || value < 0 || value > 0xFFFFFFFF)             // This is an implementation limit
+                {
+                    lwm2m_free(targetP);
+                    lwm2m_data_free(size, dataP);
+                    return -1;
+                }
+                // lifetime of a bootstrap server is set to ClientHoldOffTime
+                targetP->lifetime = value;
+
+                contextP->bootstrapServerList = (lwm2m_server_t*)LWM2M_LIST_ADD(contextP->bootstrapServerList, targetP);
+            }
+            else
+            {
+                lwm2m_list_t * serverInstP;     // instanceID of the server in the LWM2M Server Object
+
+                serverInstP = prv_findServerInstance(serverObjP, targetP->shortID);
+                if (serverInstP == NULL)
+                {
+                    lwm2m_free(targetP);
+                    lwm2m_data_free(size, dataP);
+                    return -1;
+                }
+                if (0 != prv_getMandatoryInfo(serverObjP, serverInstP->id, targetP))
+                {
+                    lwm2m_free(targetP);
+                    lwm2m_data_free(size, dataP);
+                    return -1;
+                }
+                targetP->status = STATE_DEREGISTERED;
+                contextP->serverList = (lwm2m_server_t*)LWM2M_LIST_ADD(contextP->serverList, targetP);
+            }
+            lwm2m_data_free(size, dataP);
+        }
+        securityInstP = securityInstP->next;
+    }
+
+    return 0;
+}
+
+coap_status_t object_createInstance(lwm2m_context_t * contextP,
+                                    lwm2m_uri_t * uriP,
+                                    lwm2m_data_t * dataP)
+{
+    lwm2m_object_t * targetP;
+
+    LOG_URI(uriP);
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId);
+    if (NULL == targetP) return COAP_404_NOT_FOUND;
+
+    if (NULL == targetP->createFunc) 
+    {
+        return COAP_405_METHOD_NOT_ALLOWED;
+    }
+
+    return targetP->createFunc(lwm2m_list_newId(targetP->instanceList), dataP->value.asChildren.count, dataP->value.asChildren.array, targetP);
+}
+
+coap_status_t object_writeInstance(lwm2m_context_t * contextP,
+                            lwm2m_uri_t * uriP,
+                            lwm2m_data_t * dataP)
+{
+    lwm2m_object_t * targetP;
+
+    LOG_URI(uriP);
+    targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId);
+    if (NULL == targetP) return COAP_404_NOT_FOUND;
+
+    if (NULL == targetP->writeFunc) 
+    {
+        return COAP_405_METHOD_NOT_ALLOWED;
+    }
+
+    return targetP->writeFunc(dataP->id, dataP->value.asChildren.count, dataP->value.asChildren.array, targetP);
+}
+
+#endif
diff -r 000000000000 -r 3f48af28ebcd core/observe.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/observe.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,995 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Toby Jaffey - Please refer to git log
+ *    Bosch Software Innovations GmbH - 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 <stdio.h>
+
+
+#ifdef LWM2M_CLIENT_MODE
+static lwm2m_observed_t * prv_findObserved(lwm2m_context_t * contextP,
+                                           lwm2m_uri_t * uriP)
+{
+    lwm2m_observed_t * targetP;
+
+    targetP = contextP->observedList;
+    while (targetP != NULL
+        && (targetP->uri.objectId != uriP->objectId
+         || targetP->uri.flag != uriP->flag
+         || (LWM2M_URI_IS_SET_INSTANCE(uriP) && targetP->uri.instanceId != uriP->instanceId)
+         || (LWM2M_URI_IS_SET_RESOURCE(uriP) && targetP->uri.resourceId != uriP->resourceId)))
+    {
+        targetP = targetP->next;
+    }
+
+    return targetP;
+}
+
+static void prv_unlinkObserved(lwm2m_context_t * contextP,
+                               lwm2m_observed_t * observedP)
+{
+    if (contextP->observedList == observedP)
+    {
+        contextP->observedList = contextP->observedList->next;
+    }
+    else
+    {
+        lwm2m_observed_t * parentP;
+
+        parentP = contextP->observedList;
+        while (parentP->next != NULL
+            && parentP->next != observedP)
+        {
+            parentP = parentP->next;
+        }
+        if (parentP->next != NULL)
+        {
+            parentP->next = parentP->next->next;
+        }
+    }
+}
+
+static lwm2m_watcher_t * prv_findWatcher(lwm2m_observed_t * observedP,
+                                         lwm2m_server_t * serverP)
+{
+    lwm2m_watcher_t * targetP;
+
+    targetP = observedP->watcherList;
+    while (targetP != NULL
+        && targetP->server != serverP)
+    {
+        targetP = targetP->next;
+    }
+
+    return targetP;
+}
+
+static lwm2m_watcher_t * prv_getWatcher(lwm2m_context_t * contextP,
+                                        lwm2m_uri_t * uriP,
+                                        lwm2m_server_t * serverP)
+{
+    lwm2m_observed_t * observedP;
+    bool allocatedObserver;
+    lwm2m_watcher_t * watcherP;
+
+    allocatedObserver = false;
+
+    observedP = prv_findObserved(contextP, uriP);
+    if (observedP == NULL)
+    {
+        observedP = (lwm2m_observed_t *)lwm2m_malloc(sizeof(lwm2m_observed_t));
+        if (observedP == NULL) return NULL;
+        allocatedObserver = true;
+        memset(observedP, 0, sizeof(lwm2m_observed_t));
+        memcpy(&(observedP->uri), uriP, sizeof(lwm2m_uri_t));
+        observedP->next = contextP->observedList;
+        contextP->observedList = observedP;
+    }
+
+    watcherP = prv_findWatcher(observedP, serverP);
+    if (watcherP == NULL)
+    {
+        watcherP = (lwm2m_watcher_t *)lwm2m_malloc(sizeof(lwm2m_watcher_t));
+        if (watcherP == NULL)
+        {
+            if (allocatedObserver == true)
+            {
+                lwm2m_free(observedP);
+            }
+            return NULL;
+        }
+        memset(watcherP, 0, sizeof(lwm2m_watcher_t));
+        watcherP->active = false;
+        watcherP->server = serverP;
+        watcherP->next = observedP->watcherList;
+        observedP->watcherList = watcherP;
+    }
+
+    return watcherP;
+}
+
+coap_status_t observe_handleRequest(lwm2m_context_t * contextP,
+                                    lwm2m_uri_t * uriP,
+                                    lwm2m_server_t * serverP,
+                                    int size,
+                                    lwm2m_data_t * dataP,
+                                    coap_packet_t * message,
+                                    coap_packet_t * response)
+{
+    lwm2m_watcher_t * watcherP;
+    uint32_t count;
+
+    LOG_ARG("Code: %02X, server status: %s", message->code, STR_STATUS(serverP->status));
+    LOG_URI(uriP);
+
+    coap_get_header_observe(message, &count);
+
+    switch (count)
+    {
+    case 0:
+        if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST;
+        if (message->token_len == 0) return COAP_400_BAD_REQUEST;
+
+        watcherP = prv_getWatcher(contextP, uriP, serverP);
+        if (watcherP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+        watcherP->tokenLen = message->token_len;
+        memcpy(watcherP->token, message->token, message->token_len);
+        watcherP->active = true;
+        watcherP->lastTime = lwm2m_gettime();
+
+        if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+        {
+            switch (dataP->type)
+            {
+            case LWM2M_TYPE_INTEGER:
+                if (1 != lwm2m_data_decode_int(dataP, &(watcherP->lastValue.asInteger))) return COAP_500_INTERNAL_SERVER_ERROR;
+                break;
+            case LWM2M_TYPE_FLOAT:
+                if (1 != lwm2m_data_decode_float(dataP, &(watcherP->lastValue.asFloat))) return COAP_500_INTERNAL_SERVER_ERROR;
+                break;
+            default:
+                break;
+            }
+        }
+
+        coap_set_header_observe(response, watcherP->counter++);
+
+        return COAP_205_CONTENT;
+
+    case 1:
+        // cancellation
+        observe_cancel(contextP, LWM2M_MAX_ID, serverP->sessionH);
+        return COAP_205_CONTENT;
+
+    default:
+        return COAP_400_BAD_REQUEST;
+    }
+}
+
+void observe_cancel(lwm2m_context_t * contextP,
+                    uint16_t mid,
+                    void * fromSessionH)
+{
+    lwm2m_observed_t * observedP;
+
+    LOG_ARG("mid: %d", mid);
+
+    for (observedP = contextP->observedList;
+         observedP != NULL;
+         observedP = observedP->next)
+    {
+        lwm2m_watcher_t * targetP = NULL;
+
+        if ((LWM2M_MAX_ID == mid || observedP->watcherList->lastMid == mid)
+         && lwm2m_session_is_equal(observedP->watcherList->server->sessionH, fromSessionH, contextP->userData))
+        {
+            targetP = observedP->watcherList;
+            observedP->watcherList = observedP->watcherList->next;
+        }
+        else
+        {
+            lwm2m_watcher_t * parentP;
+
+            parentP = observedP->watcherList;
+            while (parentP->next != NULL
+                && (parentP->next->lastMid != mid
+                 || lwm2m_session_is_equal(parentP->next->server->sessionH, fromSessionH, contextP->userData)))
+            {
+                parentP = parentP->next;
+            }
+            if (parentP->next != NULL)
+            {
+                targetP = parentP->next;
+                parentP->next = parentP->next->next;
+            }
+        }
+        if (targetP != NULL)
+        {
+            lwm2m_free(targetP);
+            if (observedP->watcherList == NULL)
+            {
+                prv_unlinkObserved(contextP, observedP);
+                lwm2m_free(observedP);
+            }
+            return;
+        }
+    }
+}
+
+coap_status_t observe_setParameters(lwm2m_context_t * contextP,
+                                    lwm2m_uri_t * uriP,
+                                    lwm2m_server_t * serverP,
+                                    lwm2m_attributes_t * attrP)
+{
+    uint8_t result;
+    lwm2m_watcher_t * watcherP;
+
+    LOG_URI(uriP);
+    LOG_ARG("toSet: %08X, toClear: %08X, minPeriod: %d, maxPeriod: %d, greaterThan: %f, lessThan: %f, step: %f",
+            attrP->toSet, attrP->toClear, attrP->minPeriod, attrP->maxPeriod, attrP->greaterThan, attrP->lessThan, attrP->step);
+
+    if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST;
+
+    result = object_checkReadable(contextP, uriP);
+    if (COAP_205_CONTENT != result) return result;
+
+    if (0 != (attrP->toSet & ATTR_FLAG_NUMERIC))
+    {
+        result = object_checkNumeric(contextP, uriP);
+        if (COAP_205_CONTENT != result) return result;
+    }
+
+    watcherP = prv_getWatcher(contextP, uriP, serverP);
+    if (watcherP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    // Check rule “lt” value + 2*”stp” values < “gt” value
+    if ((((attrP->toSet | (watcherP->parameters?watcherP->parameters->toSet:0)) & ~attrP->toClear) & ATTR_FLAG_NUMERIC) == ATTR_FLAG_NUMERIC)
+    {
+        float gt;
+        float lt;
+        float stp;
+
+        if (0 != (attrP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN))
+        {
+            gt = attrP->greaterThan;
+        }
+        else
+        {
+            gt = watcherP->parameters->greaterThan;
+        }
+        if (0 != (attrP->toSet & LWM2M_ATTR_FLAG_LESS_THAN))
+        {
+            lt = attrP->lessThan;
+        }
+        else
+        {
+            lt = watcherP->parameters->lessThan;
+        }
+        if (0 != (attrP->toSet & LWM2M_ATTR_FLAG_STEP))
+        {
+            stp = attrP->step;
+        }
+        else
+        {
+            stp = watcherP->parameters->step;
+        }
+
+        if (lt + (2 * stp) >= gt) return COAP_400_BAD_REQUEST;
+    }
+
+    if (watcherP->parameters == NULL)
+    {
+        if (attrP->toSet != 0)
+        {
+            watcherP->parameters = (lwm2m_attributes_t *)lwm2m_malloc(sizeof(lwm2m_attributes_t));
+            if (watcherP->parameters == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+            memcpy(watcherP->parameters, attrP, sizeof(lwm2m_attributes_t));
+        }
+    }
+    else
+    {
+        watcherP->parameters->toSet &= ~attrP->toClear;
+        if (attrP->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD)
+        {
+            watcherP->parameters->minPeriod = attrP->minPeriod;
+        }
+        if (attrP->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD)
+        {
+            watcherP->parameters->maxPeriod = attrP->maxPeriod;
+        }
+        if (attrP->toSet & LWM2M_ATTR_FLAG_GREATER_THAN)
+        {
+            watcherP->parameters->greaterThan = attrP->greaterThan;
+        }
+        if (attrP->toSet & LWM2M_ATTR_FLAG_LESS_THAN)
+        {
+            watcherP->parameters->lessThan = attrP->lessThan;
+        }
+        if (attrP->toSet & LWM2M_ATTR_FLAG_STEP)
+        {
+            watcherP->parameters->step = attrP->step;
+        }
+    }
+
+    LOG_ARG("Final toSet: %08X, minPeriod: %d, maxPeriod: %d, greaterThan: %f, lessThan: %f, step: %f",
+            watcherP->parameters->toSet, watcherP->parameters->minPeriod, watcherP->parameters->maxPeriod, watcherP->parameters->greaterThan, watcherP->parameters->lessThan, watcherP->parameters->step);
+
+    return COAP_204_CHANGED;
+}
+
+lwm2m_observed_t * observe_findByUri(lwm2m_context_t * contextP,
+                                     lwm2m_uri_t * uriP)
+{
+    lwm2m_observed_t * targetP;
+
+    LOG_URI(uriP);
+    targetP = contextP->observedList;
+    while (targetP != NULL)
+    {
+        if (targetP->uri.objectId == uriP->objectId)
+        {
+            if ((!LWM2M_URI_IS_SET_INSTANCE(uriP) && !LWM2M_URI_IS_SET_INSTANCE(&(targetP->uri)))
+             || (LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_INSTANCE(&(targetP->uri)) && (uriP->instanceId == targetP->uri.instanceId)))
+             {
+                 if ((!LWM2M_URI_IS_SET_RESOURCE(uriP) && !LWM2M_URI_IS_SET_RESOURCE(&(targetP->uri)))
+                     || (LWM2M_URI_IS_SET_RESOURCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(&(targetP->uri)) && (uriP->resourceId == targetP->uri.resourceId)))
+                 {
+                     LOG_ARG("Found one with%s observers.", targetP->watcherList ? "" : " no");
+                     LOG_URI(&(targetP->uri));
+                     return targetP;
+                 }
+             }
+        }
+        targetP = targetP->next;
+    }
+
+    LOG("Found nothing");
+    return NULL;
+}
+
+void lwm2m_resource_value_changed(lwm2m_context_t * contextP,
+                                  lwm2m_uri_t * uriP)
+{
+    lwm2m_observed_t * targetP;
+
+    LOG_URI(uriP);
+    targetP = contextP->observedList;
+    while (targetP != NULL)
+    {
+        if (targetP->uri.objectId == uriP->objectId)
+        {
+            if (!LWM2M_URI_IS_SET_INSTANCE(uriP)
+             || (targetP->uri.flag & LWM2M_URI_FLAG_INSTANCE_ID) == 0
+             || uriP->instanceId == targetP->uri.instanceId)
+            {
+                if (!LWM2M_URI_IS_SET_RESOURCE(uriP)
+                 || (targetP->uri.flag & LWM2M_URI_FLAG_RESOURCE_ID) == 0
+                 || uriP->resourceId == targetP->uri.resourceId)
+                {
+                    lwm2m_watcher_t * watcherP;
+
+                    LOG("Found an observation");
+                    LOG_URI(&(targetP->uri));
+
+                    for (watcherP = targetP->watcherList ; watcherP != NULL ; watcherP = watcherP->next)
+                    {
+                        if (watcherP->active == true)
+                        {
+                            LOG("Tagging a watcher");
+                            watcherP->update = true;
+                        }
+                    }
+                }
+            }
+        }
+        targetP = targetP->next;
+    }
+}
+
+void observe_step(lwm2m_context_t * contextP,
+                  time_t currentTime,
+                  time_t * timeoutP)
+{
+    lwm2m_observed_t * targetP;
+
+    LOG("Entering");
+    for (targetP = contextP->observedList ; targetP != NULL ; targetP = targetP->next)
+    {
+        lwm2m_watcher_t * watcherP;
+        uint8_t * buffer = NULL;
+        size_t length = 0;
+        lwm2m_data_t * dataP = NULL;
+        int size = 0;
+        double floatValue = 0;
+        int64_t integerValue = 0;
+        bool storeValue = false;
+        lwm2m_media_type_t format = LWM2M_CONTENT_TEXT;
+        coap_packet_t message[1];
+        time_t interval;
+
+        LOG_URI(&(targetP->uri));
+        if (LWM2M_URI_IS_SET_RESOURCE(&targetP->uri))
+        {
+            if (COAP_205_CONTENT != object_readData(contextP, &targetP->uri, &size, &dataP)) continue;
+            switch (dataP->type)
+            {
+            case LWM2M_TYPE_INTEGER:
+                if (1 != lwm2m_data_decode_int(dataP, &integerValue)) continue;
+                storeValue = true;
+                break;
+            case LWM2M_TYPE_FLOAT:
+                if (1 != lwm2m_data_decode_float(dataP, &floatValue)) continue;
+                storeValue = true;
+                break;
+            default:
+                break;
+            }
+        }
+        for (watcherP = targetP->watcherList ; watcherP != NULL ; watcherP = watcherP->next)
+        {
+            if (watcherP->active == true)
+            {
+                bool notify = false;
+
+                if (watcherP->update == true)
+                {
+                    // value changed, should we notify the server ?
+
+                    if (watcherP->parameters == NULL || watcherP->parameters->toSet == 0)
+                    {
+                        // no conditions
+                        notify = true;
+                        LOG("Notify with no conditions");
+                        LOG_URI(&(targetP->uri));
+                    }
+
+                    if (notify == false
+                     && watcherP->parameters != NULL
+                     && (watcherP->parameters->toSet & ATTR_FLAG_NUMERIC) != 0)
+                    {
+                        if ((watcherP->parameters->toSet & LWM2M_ATTR_FLAG_LESS_THAN) != 0)
+                        {
+                            LOG("Checking lower treshold");
+                            // Did we cross the lower treshold ?
+                            switch (dataP->type)
+                            {
+                            case LWM2M_TYPE_INTEGER:
+                                if ((integerValue <= watcherP->parameters->lessThan
+                                  && watcherP->lastValue.asInteger > watcherP->parameters->lessThan)
+                                 || (integerValue >= watcherP->parameters->lessThan
+                                  && watcherP->lastValue.asInteger < watcherP->parameters->lessThan))
+                                {
+                                    LOG("Notify on lower treshold crossing");
+                                    notify = true;
+                                }
+                                break;
+                            case LWM2M_TYPE_FLOAT:
+                                if ((floatValue <= watcherP->parameters->lessThan
+                                  && watcherP->lastValue.asFloat > watcherP->parameters->lessThan)
+                                 || (floatValue >= watcherP->parameters->lessThan
+                                  && watcherP->lastValue.asFloat < watcherP->parameters->lessThan))
+                                {
+                                    LOG("Notify on lower treshold crossing");
+                                    notify = true;
+                                }
+                                break;
+                            default:
+                                break;
+                            }
+                        }
+                        if ((watcherP->parameters->toSet & LWM2M_ATTR_FLAG_GREATER_THAN) != 0)
+                        {
+                            LOG("Checking upper treshold");
+                            // Did we cross the upper treshold ?
+                            switch (dataP->type)
+                            {
+                            case LWM2M_TYPE_INTEGER:
+                                if ((integerValue <= watcherP->parameters->greaterThan
+                                  && watcherP->lastValue.asInteger > watcherP->parameters->greaterThan)
+                                 || (integerValue >= watcherP->parameters->greaterThan
+                                  && watcherP->lastValue.asInteger < watcherP->parameters->greaterThan))
+                                {
+                                    LOG("Notify on lower upper crossing");
+                                    notify = true;
+                                }
+                                break;
+                            case LWM2M_TYPE_FLOAT:
+                                if ((floatValue <= watcherP->parameters->greaterThan
+                                  && watcherP->lastValue.asFloat > watcherP->parameters->greaterThan)
+                                 || (floatValue >= watcherP->parameters->greaterThan
+                                  && watcherP->lastValue.asFloat < watcherP->parameters->greaterThan))
+                                {
+                                    LOG("Notify on lower upper crossing");
+                                    notify = true;
+                                }
+                                break;
+                            default:
+                                break;
+                            }
+                        }
+                        if ((watcherP->parameters->toSet & LWM2M_ATTR_FLAG_STEP) != 0)
+                        {
+                            LOG("Checking step");
+
+                            switch (dataP->type)
+                            {
+                            case LWM2M_TYPE_INTEGER:
+                            {
+                                int64_t diff;
+
+                                diff = integerValue - watcherP->lastValue.asInteger;
+                                if ((diff < 0 && (0 - diff) >= watcherP->parameters->step)
+                                 || (diff >= 0 && diff >= watcherP->parameters->step))
+                                {
+                                    LOG("Notify on step condition");
+                                    notify = true;
+                                }
+                            }
+                                break;
+                            case LWM2M_TYPE_FLOAT:
+                            {
+                                double diff;
+
+                                diff = floatValue - watcherP->lastValue.asFloat;
+                                if ((diff < 0 && (0 - diff) >= watcherP->parameters->step)
+                                 || (diff >= 0 && diff >= watcherP->parameters->step))
+                                {
+                                    LOG("Notify on step condition");
+                                    notify = true;
+                                }
+                            }
+                                break;
+                            default:
+                                break;
+                            }
+                        }
+                    }
+
+                    if (watcherP->parameters != NULL
+                     && (watcherP->parameters->toSet & LWM2M_ATTR_FLAG_MIN_PERIOD) != 0)
+                    {
+                        LOG_ARG("Checking minimal period (%d s)", watcherP->parameters->minPeriod);
+
+                        if (watcherP->lastTime + watcherP->parameters->minPeriod > currentTime)
+                        {
+                            // Minimum Period did not elapse yet
+                            interval = watcherP->lastTime + watcherP->parameters->minPeriod - currentTime;
+                            if (*timeoutP > interval) *timeoutP = interval;
+                            notify = false;
+                        }
+                        else
+                        {
+                            LOG("Notify on minimal period");
+                            notify = true;
+                        }
+                    }
+                }
+
+                // Is the Maximum Period reached ?
+                if (notify == false
+                 && watcherP->parameters != NULL
+                 && (watcherP->parameters->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) != 0)
+                {
+                    LOG_ARG("Checking maximal period (%d s)", watcherP->parameters->minPeriod);
+
+                    if (watcherP->lastTime + watcherP->parameters->maxPeriod <= currentTime)
+                    {
+                        LOG("Notify on maximal period");
+                        notify = true;
+                    }
+                }
+
+                if (notify == true)
+                {
+                    if (buffer == NULL)
+                    {
+                        if (dataP != NULL)
+                        {
+                            int res;
+
+                            res = lwm2m_data_serialize(&targetP->uri, size, dataP, &format, &buffer);
+                            if (res < 0)
+                            {
+                                break;
+                            }
+                            else
+                            {
+                                length = (size_t)res;
+                            }
+
+                        }
+                        else
+                        {
+                            if (COAP_205_CONTENT != object_read(contextP, &targetP->uri, &format, &buffer, &length))
+                            {
+                                buffer = NULL;
+                                break;
+                            }
+                        }
+                        coap_init_message(message, COAP_TYPE_NON, COAP_205_CONTENT, 0);
+                        coap_set_header_content_type(message, format);
+                        coap_set_payload(message, buffer, length);
+                    }
+                    watcherP->lastTime = currentTime;
+                    watcherP->lastMid = contextP->nextMID++;
+                    message->mid = watcherP->lastMid;
+                    coap_set_header_token(message, watcherP->token, watcherP->tokenLen);
+                    coap_set_header_observe(message, watcherP->counter++);
+                    (void)message_send(contextP, message, watcherP->server->sessionH);
+                    watcherP->update = false;
+                }
+
+                // Store this value
+                if (notify == true && storeValue == true)
+                {
+                    switch (dataP->type)
+                    {
+                    case LWM2M_TYPE_INTEGER:
+                        watcherP->lastValue.asInteger = integerValue;
+                        break;
+                    case LWM2M_TYPE_FLOAT:
+                        watcherP->lastValue.asFloat = floatValue;
+                        break;
+                    default:
+                        break;
+                    }
+                }
+
+                if (watcherP->parameters != NULL && (watcherP->parameters->toSet & LWM2M_ATTR_FLAG_MAX_PERIOD) != 0)
+                {
+                    // update timers
+                    interval = watcherP->lastTime + watcherP->parameters->maxPeriod - currentTime;
+                    if (*timeoutP > interval) *timeoutP = interval;
+                }
+            }
+        }
+        if (dataP != NULL) lwm2m_data_free(size, dataP);
+        if (buffer != NULL) lwm2m_free(buffer);
+    }
+}
+
+#endif
+
+#ifdef LWM2M_SERVER_MODE
+
+typedef struct
+{
+    lwm2m_observation_t * observationP;
+    lwm2m_result_callback_t callbackP;
+    void * userDataP;
+} cancellation_data_t;
+
+static lwm2m_observation_t * prv_findObservationByURI(lwm2m_client_t * clientP,
+                                                      lwm2m_uri_t * uriP)
+{
+    lwm2m_observation_t * targetP;
+
+    targetP = clientP->observationList;
+    while (targetP != NULL)
+    {
+        if (targetP->uri.objectId == uriP->objectId
+         && targetP->uri.flag == uriP->flag
+         && targetP->uri.instanceId == uriP->instanceId
+         && targetP->uri.resourceId == uriP->resourceId)
+        {
+            return targetP;
+        }
+
+        targetP = targetP->next;
+    }
+
+    return targetP;
+}
+
+void observe_remove(lwm2m_observation_t * observationP)
+{
+    LOG("Entering");
+    observationP->clientP->observationList = (lwm2m_observation_t *) LWM2M_LIST_RM(observationP->clientP->observationList, observationP->id, NULL);
+    lwm2m_free(observationP);
+}
+
+static void prv_obsRequestCallback(lwm2m_transaction_t * transacP,
+                                   void * message)
+{
+    lwm2m_observation_t * observationP = (lwm2m_observation_t *)transacP->userData;
+    coap_packet_t * packet = (coap_packet_t *)message;
+    uint8_t code;
+
+    switch (observationP->status)
+    {
+    case STATE_DEREG_PENDING:
+        // Observation was canceled by the user.
+        observe_remove(observationP);
+        return;
+
+    case STATE_REG_PENDING:
+        observationP->status = STATE_REGISTERED;
+        break;
+
+    default:
+        break;
+    }
+
+    if (message == NULL)
+    {
+        code = COAP_503_SERVICE_UNAVAILABLE;
+    }
+    else if (packet->code == COAP_205_CONTENT
+         && !IS_OPTION(packet, COAP_OPTION_OBSERVE))
+    {
+        code = COAP_405_METHOD_NOT_ALLOWED;
+    }
+    else
+    {
+        code = packet->code;
+    }
+
+    if (code != COAP_205_CONTENT)
+    {
+        observationP->callback(observationP->clientP->internalID,
+                               &observationP->uri,
+                               code,
+                               LWM2M_CONTENT_TEXT, NULL, 0,
+                               observationP->userData);
+        observe_remove(observationP);
+    }
+    else
+    {
+        observationP->callback(observationP->clientP->internalID,
+                               &observationP->uri,
+                               0,
+                               packet->content_type, packet->payload, packet->payload_len,
+                               observationP->userData);
+    }
+}
+
+
+static void prv_obsCancelRequestCallback(lwm2m_transaction_t * transacP,
+                                         void * message)
+{
+    cancellation_data_t * cancelP = (cancellation_data_t *)transacP->userData;
+    coap_packet_t * packet = (coap_packet_t *)message;
+    uint8_t code;
+
+    if (message == NULL)
+    {
+        code = COAP_503_SERVICE_UNAVAILABLE;
+    }
+    else
+    {
+        code = packet->code;
+    }
+
+    if (code != COAP_205_CONTENT)
+    {
+        cancelP->callbackP(cancelP->observationP->clientP->internalID,
+                           &cancelP->observationP->uri,
+                           code,
+                           LWM2M_CONTENT_TEXT, NULL, 0,
+                           cancelP->userDataP);
+    }
+    else
+    {
+        cancelP->callbackP(cancelP->observationP->clientP->internalID,
+                           &cancelP->observationP->uri,
+                           0,
+                           packet->content_type, packet->payload, packet->payload_len,
+                           cancelP->userDataP);
+    }
+
+    observe_remove(cancelP->observationP);
+
+    lwm2m_free(cancelP);
+}
+
+
+int lwm2m_observe(lwm2m_context_t * contextP,
+                  uint16_t clientID,
+                  lwm2m_uri_t * uriP,
+                  lwm2m_result_callback_t callback,
+                  void * userData)
+{
+    lwm2m_client_t * clientP;
+    lwm2m_transaction_t * transactionP;
+    lwm2m_observation_t * observationP;
+    uint8_t token[4];
+
+    LOG_ARG("clientID: %d", clientID);
+    LOG_URI(uriP);
+
+    if (!LWM2M_URI_IS_SET_INSTANCE(uriP) && LWM2M_URI_IS_SET_RESOURCE(uriP)) return COAP_400_BAD_REQUEST;
+
+    clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
+    if (clientP == NULL) return COAP_404_NOT_FOUND;
+
+    observationP = (lwm2m_observation_t *)lwm2m_malloc(sizeof(lwm2m_observation_t));
+    if (observationP == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+    memset(observationP, 0, sizeof(lwm2m_observation_t));
+
+    observationP->id = lwm2m_list_newId((lwm2m_list_t *)clientP->observationList);
+    memcpy(&observationP->uri, uriP, sizeof(lwm2m_uri_t));
+    observationP->clientP = clientP;
+    observationP->status = STATE_REG_PENDING;
+    observationP->callback = callback;
+    observationP->userData = userData;
+
+    token[0] = clientP->internalID >> 8;
+    token[1] = clientP->internalID & 0xFF;
+    token[2] = observationP->id >> 8;
+    token[3] = observationP->id & 0xFF;
+
+    transactionP = transaction_new(clientP->sessionH, COAP_GET, clientP->altPath, uriP, contextP->nextMID++, 4, token);
+    if (transactionP == NULL)
+    {
+        lwm2m_free(observationP);
+        return COAP_500_INTERNAL_SERVER_ERROR;
+    }
+
+    observationP->clientP->observationList = (lwm2m_observation_t *)LWM2M_LIST_ADD(observationP->clientP->observationList, observationP);
+
+    coap_set_header_observe(transactionP->message, 0);
+    coap_set_header_token(transactionP->message, token, sizeof(token));
+
+    transactionP->callback = prv_obsRequestCallback;
+    transactionP->userData = (void *)observationP;
+
+    contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transactionP);
+
+    return transaction_send(contextP, transactionP);
+}
+
+int lwm2m_observe_cancel(lwm2m_context_t * contextP,
+                         uint16_t clientID,
+                         lwm2m_uri_t * uriP,
+                         lwm2m_result_callback_t callback,
+                         void * userData)
+{
+    lwm2m_client_t * clientP;
+    lwm2m_observation_t * observationP;
+
+    LOG_ARG("clientID: %d", clientID);
+    LOG_URI(uriP);
+
+    clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
+    if (clientP == NULL) return COAP_404_NOT_FOUND;
+
+    observationP = prv_findObservationByURI(clientP, uriP);
+    if (observationP == NULL) return COAP_404_NOT_FOUND;
+
+    switch (observationP->status)
+    {
+    case STATE_REGISTERED:
+    {
+        lwm2m_transaction_t * transactionP;
+        cancellation_data_t * cancelP;
+
+        transactionP = transaction_new(clientP->sessionH, COAP_GET, clientP->altPath, uriP, contextP->nextMID++, 0, NULL);
+        if (transactionP == NULL)
+        {
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        cancelP = (cancellation_data_t *)lwm2m_malloc(sizeof(cancellation_data_t));
+        if (cancelP == NULL)
+        {
+            lwm2m_free(transactionP);
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+
+        coap_set_header_observe(transactionP->message, 1);
+
+        cancelP->observationP = observationP;
+        cancelP->callbackP = callback;
+        cancelP->userDataP = userData;
+
+        transactionP->callback = prv_obsCancelRequestCallback;
+        transactionP->userData = (void *)cancelP;
+
+        contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transactionP);
+
+        return transaction_send(contextP, transactionP);
+    }
+
+    case STATE_REG_PENDING:
+        observationP->status = STATE_DEREG_PENDING;
+        break;
+
+    default:
+        // Should not happen
+        break;
+    }
+
+    return COAP_NO_ERROR;
+}
+
+bool observe_handleNotify(lwm2m_context_t * contextP,
+                           void * fromSessionH,
+                           coap_packet_t * message,
+        				   coap_packet_t * response)
+{
+    uint8_t * tokenP;
+    int token_len;
+    uint16_t clientID;
+    uint16_t obsID;
+    lwm2m_client_t * clientP;
+    lwm2m_observation_t * observationP;
+    uint32_t count;
+
+    LOG("Entering");
+    token_len = coap_get_header_token(message, (const uint8_t **)&tokenP);
+    if (token_len != sizeof(uint32_t)) return false;
+
+    if (1 != coap_get_header_observe(message, &count)) return false;
+
+    clientID = (tokenP[0] << 8) | tokenP[1];
+    obsID = (tokenP[2] << 8) | tokenP[3];
+
+    clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, clientID);
+    if (clientP == NULL) return false;
+
+    observationP = (lwm2m_observation_t *)lwm2m_list_find((lwm2m_list_t *)clientP->observationList, obsID);
+    if (observationP == NULL)
+    {
+        coap_init_message(response, COAP_TYPE_RST, 0, message->mid);
+        message_send(contextP, response, fromSessionH);
+    }
+    else
+    {
+        if (message->type == COAP_TYPE_CON ) {
+            coap_init_message(response, COAP_TYPE_ACK, 0, message->mid);
+            message_send(contextP, response, fromSessionH);
+        }
+        observationP->callback(clientID,
+                               &observationP->uri,
+                               (int)count,
+                               message->content_type, message->payload, message->payload_len,
+                               observationP->userData);
+    }
+    return true;
+}
+#endif
diff -r 000000000000 -r 3f48af28ebcd core/packet.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/packet.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,443 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Fabien Fleutot - Please refer to git log
+ *    Simon Bernard - Please refer to git log
+ *    Toby Jaffey - Please refer to git log
+ *    Pascal Rieux - Please refer to git log
+ *    Bosch Software Innovations GmbH - 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>
+
+*/
+
+/*
+Contains code snippets which are:
+
+ * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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.
+
+*/
+
+
+#include "internals.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <stdio.h>
+
+
+static void handle_reset(lwm2m_context_t * contextP,
+                         void * fromSessionH,
+                         coap_packet_t * message)
+{
+#ifdef LWM2M_CLIENT_MODE
+    LOG("Entering");
+    observe_cancel(contextP, message->mid, fromSessionH);
+#endif
+}
+
+static coap_status_t handle_request(lwm2m_context_t * contextP,
+                                    void * fromSessionH,
+                                    coap_packet_t * message,
+                                    coap_packet_t * response)
+{
+    lwm2m_uri_t * uriP;
+    coap_status_t result = COAP_IGNORE;
+
+    LOG("Entering");
+	
+#ifdef LWM2M_CLIENT_MODE
+    uriP = uri_decode(contextP->altPath, message->uri_path);
+#else
+    uriP = uri_decode(NULL, message->uri_path);
+#endif
+
+    if (uriP == NULL) return COAP_400_BAD_REQUEST;
+
+    switch(uriP->flag & LWM2M_URI_MASK_TYPE)
+    {
+#ifdef LWM2M_CLIENT_MODE
+    case LWM2M_URI_FLAG_DM:
+    {
+        lwm2m_server_t * serverP;
+
+        serverP = utils_findServer(contextP, fromSessionH);
+        if (serverP != NULL)
+        {
+            result = dm_handleRequest(contextP, uriP, serverP, message, response);
+        }
+#ifdef LWM2M_BOOTSTRAP
+        else
+        {
+            serverP = utils_findBootstrapServer(contextP, fromSessionH);
+            if (serverP != NULL)
+            {
+                result = bootstrap_handleCommand(contextP, uriP, serverP, message, response);
+            }
+        }
+#endif
+    }
+    break;
+
+#ifdef LWM2M_BOOTSTRAP
+    case LWM2M_URI_FLAG_DELETE_ALL:
+        if (COAP_DELETE != message->code)
+        {
+            result = COAP_400_BAD_REQUEST;
+        }
+        else
+        {
+            result = bootstrap_handleDeleteAll(contextP, fromSessionH);
+        }
+        break;
+
+    case LWM2M_URI_FLAG_BOOTSTRAP:
+        if (message->code == COAP_POST)
+        {
+            result = bootstrap_handleFinish(contextP, fromSessionH);
+        }
+        break;
+#endif
+#endif
+
+#ifdef LWM2M_SERVER_MODE
+    case LWM2M_URI_FLAG_REGISTRATION:
+        result = registration_handleRequest(contextP, uriP, fromSessionH, message, response);
+        break;
+#endif
+#ifdef LWM2M_BOOTSTRAP_SERVER_MODE
+    case LWM2M_URI_FLAG_BOOTSTRAP:
+        result = bootstrap_handleRequest(contextP, uriP, fromSessionH, message, response);
+        break;
+#endif
+    default:
+        result = COAP_IGNORE;
+        break;
+    }
+
+    coap_set_status_code(response, result);
+
+    if (COAP_IGNORE < result && result < COAP_400_BAD_REQUEST)
+    {
+        result = NO_ERROR;
+    }
+
+    lwm2m_free(uriP);
+    return result;
+}
+
+/* This function is an adaptation of function coap_receive() from Erbium's er-coap-13-engine.c.
+ * Erbium is Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
+ * All rights reserved.
+ */
+void lwm2m_handle_packet(lwm2m_context_t * contextP,
+                        uint8_t * buffer,
+                        int length,
+                        void * fromSessionH)
+{
+    coap_status_t coap_error_code = NO_ERROR;
+    static coap_packet_t message[1];
+    static coap_packet_t response[1];
+
+    LOG("Entering");
+    coap_error_code = coap_parse_message(message, buffer, (uint16_t)length);
+    if (coap_error_code == NO_ERROR)
+    {
+        LOG_ARG("Parsed: ver %u, type %u, tkl %u, code %u.%.2u, mid %u, Content type: %d",
+                message->version, message->type, message->token_len, message->code >> 5, message->code & 0x1F, message->mid, message->content_type);
+        LOG_ARG("Payload: %.*s", message->payload_len, message->payload);
+        if (message->code >= COAP_GET && message->code <= COAP_DELETE)
+        {
+            uint32_t block_num = 0;
+            uint16_t block_size = REST_MAX_CHUNK_SIZE;
+            uint32_t block_offset = 0;
+            int64_t new_offset = 0;
+
+            /* prepare response */
+            if (message->type == COAP_TYPE_CON)
+            {
+                /* Reliable CON requests are answered with an ACK. */
+                coap_init_message(response, COAP_TYPE_ACK, COAP_205_CONTENT, message->mid);
+            }
+            else
+            {
+                /* Unreliable NON requests are answered with a NON as well. */
+                coap_init_message(response, COAP_TYPE_NON, COAP_205_CONTENT, contextP->nextMID++);
+            }
+
+            /* mirror token */
+            if (message->token_len)
+            {
+                coap_set_header_token(response, message->token, message->token_len);
+            }
+
+            /* get offset for blockwise transfers */
+            if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset))
+            {
+                LOG_ARG("Blockwise: block request %u (%u/%u) @ %u bytes", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset);
+                block_size = MIN(block_size, REST_MAX_CHUNK_SIZE);
+                new_offset = block_offset;
+            }
+
+            /* handle block1 option */
+            if (IS_OPTION(message, COAP_OPTION_BLOCK1))
+            {
+#ifdef LWM2M_CLIENT_MODE
+                // get server
+                lwm2m_server_t * serverP;
+                serverP = utils_findServer(contextP, fromSessionH);
+#ifdef LWM2M_BOOTSTRAP
+                if (serverP == NULL)
+                {
+                    serverP = utils_findBootstrapServer(contextP, fromSessionH);
+                }
+#endif
+                if (serverP == NULL)
+                {
+                    coap_error_code = COAP_500_INTERNAL_SERVER_ERROR;
+                }
+                else
+                {
+                    uint32_t block1_num;
+                    uint8_t  block1_more;
+                    uint16_t block1_size;
+                    uint8_t * complete_buffer = NULL;
+                    size_t complete_buffer_size;
+
+                    // parse block1 header
+                    coap_get_header_block1(message, &block1_num, &block1_more, &block1_size, NULL);
+                    LOG_ARG("Blockwise: block1 request NUM %u (SZX %u/ SZX Max%u) MORE %u", block1_num, block1_size, REST_MAX_CHUNK_SIZE, block1_more);
+
+                    // handle block 1
+                    coap_error_code = coap_block1_handler(&serverP->block1Data, message->mid, message->payload, message->payload_len, block1_size, block1_num, block1_more, &complete_buffer, &complete_buffer_size);
+
+                    // if payload is complete, replace it in the coap message.
+                    if (coap_error_code == NO_ERROR)
+                    {
+                        message->payload = complete_buffer;
+                        message->payload_len = complete_buffer_size;
+                    }
+                    else if (coap_error_code == COAP_231_CONTINUE)
+                    {
+                        block1_size = MIN(block1_size, REST_MAX_CHUNK_SIZE);
+                        coap_set_header_block1(response,block1_num, block1_more,block1_size);
+                    }
+                }
+#else
+                coap_error_code = COAP_501_NOT_IMPLEMENTED;
+#endif
+            }
+            if (coap_error_code == NO_ERROR)
+            {
+                coap_error_code = handle_request(contextP, fromSessionH, message, response);
+            }
+            if (coap_error_code==NO_ERROR)
+            {
+                if ( IS_OPTION(message, COAP_OPTION_BLOCK2) )
+                {
+                    /* unchanged new_offset indicates that resource is unaware of blockwise transfer */
+                    if (new_offset==block_offset)
+                    {
+                        LOG_ARG("Blockwise: unaware resource with payload length %u/%u", response->payload_len, block_size);
+                        if (block_offset >= response->payload_len)
+                        {
+                            LOG("handle_incoming_data(): block_offset >= response->payload_len");
+
+                            response->code = COAP_402_BAD_OPTION;
+                            coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
+                        }
+                        else
+                        {
+                            coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size);
+                            coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size));
+                        } /* if (valid offset) */
+                    }
+                    else
+                    {
+                        /* resource provides chunk-wise data */
+                        LOG_ARG("Blockwise: blockwise resource, new offset %d", (int) new_offset);
+                        coap_set_header_block2(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size);
+                        if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size);
+                    } /* if (resource aware of blockwise) */
+                }
+                else if (new_offset!=0)
+                {
+                    LOG_ARG("Blockwise: no block option for blockwise resource, using block size %u", REST_MAX_CHUNK_SIZE);
+
+                    coap_set_header_block2(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE);
+                    coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE));
+                } /* if (blockwise request) */
+
+                coap_error_code = message_send(contextP, response, fromSessionH);
+
+                lwm2m_free(response->payload);
+                response->payload = NULL;
+                response->payload_len = 0;
+            }
+            else if (coap_error_code != COAP_IGNORE)
+            {
+                if (1 == coap_set_status_code(response, coap_error_code))
+                {
+                    coap_error_code = message_send(contextP, response, fromSessionH);
+                }
+            }
+        }
+        else
+        {
+            /* Responses */
+            switch (message->type)
+            {
+            case COAP_TYPE_NON:
+            case COAP_TYPE_CON:
+                {
+                    bool done = transaction_handleResponse(contextP, fromSessionH, message, response);
+
+#ifdef LWM2M_SERVER_MODE
+                    if (!done && IS_OPTION(message, COAP_OPTION_OBSERVE) &&
+                        ((message->code == COAP_204_CHANGED) || (message->code == COAP_205_CONTENT)))
+                    {
+                        done = observe_handleNotify(contextP, fromSessionH, message, response);
+                    }
+#endif
+                    if (!done && message->type == COAP_TYPE_CON )
+                    {
+                        coap_init_message(response, COAP_TYPE_ACK, 0, message->mid);
+                        coap_error_code = message_send(contextP, response, fromSessionH);
+                    }
+                }
+                break;
+
+            case COAP_TYPE_RST:
+                /* Cancel possible subscriptions. */
+                handle_reset(contextP, fromSessionH, message);
+                transaction_handleResponse(contextP, fromSessionH, message, NULL);
+                break;
+
+            case COAP_TYPE_ACK:
+                transaction_handleResponse(contextP, fromSessionH, message, NULL);
+                break;
+
+            default:
+                break;
+            }
+        } /* Request or Response */
+        coap_free_header(message);
+    } /* if (parsed correctly) */
+    else
+    {
+        LOG_ARG("Message parsing failed %u.%2u", coap_error_code >> 5, coap_error_code & 0x1F);
+    }
+
+    if (coap_error_code != NO_ERROR && coap_error_code != COAP_IGNORE)
+    {
+        LOG_ARG("ERROR %u: %s", coap_error_code, coap_error_message);
+
+        /* Set to sendable error code. */
+        if (coap_error_code >= 192)
+        {
+            coap_error_code = COAP_500_INTERNAL_SERVER_ERROR;
+        }
+        /* Reuse input buffer for error message. */
+        coap_init_message(message, COAP_TYPE_ACK, coap_error_code, message->mid);
+        coap_set_payload(message, coap_error_message, strlen(coap_error_message));
+        message_send(contextP, message, fromSessionH);
+    }
+}
+
+
+coap_status_t message_send(lwm2m_context_t * contextP,
+                           coap_packet_t * message,
+                           void * sessionH)
+{
+    coap_status_t result = COAP_500_INTERNAL_SERVER_ERROR;
+    uint8_t * pktBuffer;
+    size_t pktBufferLen = 0;
+    size_t allocLen;
+
+    LOG("Entering");
+    allocLen = coap_serialize_get_size(message);
+    LOG_ARG("Size to allocate: %d", allocLen);
+    if (allocLen == 0) return COAP_500_INTERNAL_SERVER_ERROR;
+
+    pktBuffer = (uint8_t *)lwm2m_malloc(allocLen);
+    if (pktBuffer != NULL)
+    {
+        pktBufferLen = coap_serialize_message(message, pktBuffer);
+        LOG_ARG("coap_serialize_message() returned %d", pktBufferLen);
+        if (0 != pktBufferLen)
+        {
+            result = lwm2m_buffer_send(sessionH, pktBuffer, pktBufferLen, contextP->userData);
+        }
+        lwm2m_free(pktBuffer);
+    }
+
+    return result;
+}
+
diff -r 000000000000 -r 3f48af28ebcd core/registration.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/registration.c	Wed Apr 19 11:28:00 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
+
+}
+
diff -r 000000000000 -r 3f48af28ebcd core/tlv.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/tlv.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,523 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Fabien Fleutot - Please refer to git log
+ *    Bosch Software Innovations GmbH - Please refer to git log
+ *    
+ *******************************************************************************/
+
+#include "internals.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <float.h>
+
+#ifndef LWM2M_BIG_ENDIAN
+#ifndef LWM2M_LITTLE_ENDIAN
+#error Please define LWM2M_BIG_ENDIAN or LWM2M_LITTLE_ENDIAN
+#endif
+#endif
+
+#define _PRV_TLV_TYPE_MASK 0xC0
+#define _PRV_TLV_HEADER_MAX_LENGTH 6
+
+#define _PRV_TLV_TYPE_UNKNOWN           (uint8_t)0xFF
+#define _PRV_TLV_TYPE_OBJECT            (uint8_t)0x10
+#define _PRV_TLV_TYPE_OBJECT_INSTANCE   (uint8_t)0x00
+#define _PRV_TLV_TYPE_RESOURCE          (uint8_t)0xC0
+#define _PRV_TLV_TYPE_MULTIPLE_RESOURCE (uint8_t)0x80
+#define _PRV_TLV_TYPE_RESOURCE_INSTANCE (uint8_t)0x40
+
+
+static uint8_t prv_getHeaderType(lwm2m_data_type_t type)
+{
+    switch (type)
+    {
+    case LWM2M_TYPE_OBJECT:
+        return _PRV_TLV_TYPE_OBJECT;
+
+    case LWM2M_TYPE_OBJECT_INSTANCE:
+        return _PRV_TLV_TYPE_OBJECT_INSTANCE;
+
+    case LWM2M_TYPE_MULTIPLE_RESOURCE:
+        return _PRV_TLV_TYPE_MULTIPLE_RESOURCE;
+
+
+    case LWM2M_TYPE_STRING:
+    case LWM2M_TYPE_INTEGER:
+    case LWM2M_TYPE_FLOAT:
+    case LWM2M_TYPE_BOOLEAN:
+    case LWM2M_TYPE_OPAQUE:
+    case LWM2M_TYPE_OBJECT_LINK:
+        return _PRV_TLV_TYPE_RESOURCE;
+
+    case LWM2M_TYPE_UNDEFINED:
+    default:
+        return _PRV_TLV_TYPE_UNKNOWN;
+    }
+}
+
+static lwm2m_data_type_t prv_getDataType(uint8_t type)
+{
+    switch (type)
+    {
+    case _PRV_TLV_TYPE_OBJECT:
+        return LWM2M_TYPE_OBJECT;
+
+    case _PRV_TLV_TYPE_OBJECT_INSTANCE:
+        return LWM2M_TYPE_OBJECT_INSTANCE;
+
+    case _PRV_TLV_TYPE_MULTIPLE_RESOURCE:
+        return LWM2M_TYPE_MULTIPLE_RESOURCE;
+
+    case _PRV_TLV_TYPE_RESOURCE:
+    case _PRV_TLV_TYPE_RESOURCE_INSTANCE:
+        return LWM2M_TYPE_OPAQUE;
+
+    default:
+        return LWM2M_TYPE_UNDEFINED;
+    }
+}
+
+static int prv_getHeaderLength(uint16_t id,
+                               size_t dataLen)
+{
+    int length;
+
+    length = 2;
+
+    if (id > 0xFF)
+    {
+        length += 1;
+    }
+
+    if (dataLen > 0xFFFF)
+    {
+        length += 3;
+    }
+    else if (dataLen > 0xFF)
+    {
+        length += 2;
+    }
+    else if (dataLen > 7)
+    {
+        length += 1;
+    }
+
+    return length;
+}
+
+static int prv_createHeader(uint8_t * header,
+                            bool isInstance,
+                            lwm2m_data_type_t type,
+                            uint16_t id,
+                            size_t data_len)
+{
+    int header_len;
+    int offset;
+    uint8_t hdrType;
+
+    header_len = prv_getHeaderLength(id, data_len);
+    if (isInstance == true)
+    {
+        hdrType = _PRV_TLV_TYPE_RESOURCE_INSTANCE;
+    }
+    else
+    {
+        hdrType = prv_getHeaderType(type);
+    }
+
+    header[0] = 0;
+    header[0] |= hdrType&_PRV_TLV_TYPE_MASK;
+
+    if (id > 0xFF)
+    {
+        header[0] |= 0x20;
+        header[1] = (id >> 8) & 0XFF;
+        header[2] = id & 0XFF;
+        offset = 3;
+    }
+    else
+    {
+        header[1] = id;
+        offset = 2;
+    }
+    if (data_len <= 7)
+    {
+        header[0] += data_len;
+    }
+    else if (data_len <= 0xFF)
+    {
+        header[0] |= 0x08;
+        header[offset] = data_len;
+    }
+    else if (data_len <= 0xFFFF)
+    {
+        header[0] |= 0x10;
+        header[offset] = (data_len >> 8) & 0XFF;
+        header[offset + 1] = data_len & 0XFF;
+    }
+    else if (data_len <= 0xFFFFFF)
+    {
+        header[0] |= 0x18;
+        header[offset] = (data_len >> 16) & 0XFF;
+        header[offset + 1] = (data_len >> 8) & 0XFF;
+        header[offset + 2] = data_len & 0XFF;
+    }
+
+    return header_len;
+}
+
+int lwm2m_decode_TLV(const uint8_t * buffer,
+                    size_t buffer_len,
+                    lwm2m_data_type_t * oType,
+                    uint16_t * oID,
+                    size_t * oDataIndex,
+                    size_t * oDataLen)
+{
+
+    LOG_ARG("buffer_len: %d", buffer_len);
+        ;
+    if (buffer_len < 2) return 0;
+
+    *oDataIndex = 2;
+
+    *oType = prv_getDataType(buffer[0]&_PRV_TLV_TYPE_MASK);
+
+    if ((buffer[0]&0x20) == 0x20)
+    {
+        // id is 16 bits long
+        if (buffer_len < 3) return 0;
+        *oDataIndex += 1;
+        *oID = (buffer[1]<<8) + buffer[2];
+    }
+    else
+    {
+        // id is 8 bits long
+        *oID = buffer[1];
+    }
+
+    switch (buffer[0]&0x18)
+    {
+    case 0x00:
+        // no length field
+        *oDataLen = buffer[0]&0x07;
+        break;
+    case 0x08:
+        // length field is 8 bits long
+        if (buffer_len < *oDataIndex + 1) return 0;
+        *oDataLen = buffer[*oDataIndex];
+        *oDataIndex += 1;
+        break;
+    case 0x10:
+        // length field is 16 bits long
+        if (buffer_len < *oDataIndex + 2) return 0;
+        *oDataLen = (buffer[*oDataIndex]<<8) + buffer[*oDataIndex+1];
+        *oDataIndex += 2;
+        break;
+    case 0x18:
+        // length field is 24 bits long
+        if (buffer_len < *oDataIndex + 3) return 0;
+        *oDataLen = (buffer[*oDataIndex]<<16) + (buffer[*oDataIndex+1]<<8) + buffer[*oDataIndex+2];
+        *oDataIndex += 3;
+        break;
+    default:
+        // can't happen
+        return 0;
+    }
+
+    if (*oDataIndex + *oDataLen > buffer_len) return 0;
+
+    return *oDataIndex + *oDataLen;
+}
+
+
+int tlv_parse(uint8_t * buffer,
+              size_t bufferLen,
+              lwm2m_data_t ** dataP)
+{
+    lwm2m_data_type_t type;
+    uint16_t id;
+    size_t dataIndex;
+    size_t dataLen;
+    int index = 0;
+    int result;
+    int size = 0;
+
+    LOG_ARG("bufferLen: %d", bufferLen);
+
+    *dataP = NULL;
+
+    while (0 != (result = lwm2m_decode_TLV((uint8_t*)buffer + index, bufferLen - index, &type, &id, &dataIndex, &dataLen)))
+    {
+        lwm2m_data_t * newTlvP;
+
+        newTlvP = lwm2m_data_new(size + 1);
+        if (size >= 1)
+        {
+            if (newTlvP == NULL)
+            {
+                lwm2m_data_free(size, *dataP);
+                return 0;
+            }
+            else
+            {
+                memcpy(newTlvP, *dataP, size * sizeof(lwm2m_data_t));
+                lwm2m_free(*dataP);
+            }
+        }
+        *dataP = newTlvP;
+
+        (*dataP)[size].type = type;
+        (*dataP)[size].id = id;
+        if (type == LWM2M_TYPE_OBJECT_INSTANCE || type == LWM2M_TYPE_MULTIPLE_RESOURCE)
+        {
+            (*dataP)[size].value.asChildren.count = tlv_parse(buffer + index + dataIndex,
+                                                          dataLen,
+                                                          &((*dataP)[size].value.asChildren.array));
+            if ((*dataP)[size].value.asChildren.count == 0)
+            {
+                lwm2m_data_free(size + 1, *dataP);
+                return 0;
+            }
+        }
+        else
+        {
+            lwm2m_data_encode_opaque(buffer + index + dataIndex, dataLen, (*dataP) + size);
+        }
+        size++;
+        index += result;
+    }
+
+    return size;
+}
+
+
+static int prv_getLength(int size,
+                         lwm2m_data_t * dataP)
+{
+    int length;
+    int i;
+
+    length = 0;
+
+    for (i = 0 ; i < size && length != -1 ; i++)
+    {
+        switch (dataP[i].type)
+        {
+        case LWM2M_TYPE_OBJECT_INSTANCE:
+        case LWM2M_TYPE_MULTIPLE_RESOURCE:
+            {
+                int subLength;
+
+                subLength = prv_getLength(dataP[i].value.asChildren.count, dataP[i].value.asChildren.array);
+                if (subLength == -1)
+                {
+                    length = -1;
+                }
+                else
+                {
+                    length += prv_getHeaderLength(dataP[i].id, subLength) + subLength;
+                }
+            }
+            break;
+
+        case LWM2M_TYPE_STRING:
+        case LWM2M_TYPE_OPAQUE:
+            length += prv_getHeaderLength(dataP[i].id, dataP[i].value.asBuffer.length) + dataP[i].value.asBuffer.length;
+            break;
+
+        case LWM2M_TYPE_INTEGER:
+            {
+                size_t data_len;
+                uint8_t unused_buffer[_PRV_64BIT_BUFFER_SIZE];
+
+                data_len = utils_encodeInt(dataP[i].value.asInteger, unused_buffer);
+                length += prv_getHeaderLength(dataP[i].id, data_len) + data_len;
+            }
+            break;
+
+        case LWM2M_TYPE_FLOAT:
+            {
+                size_t data_len;
+
+                if ((dataP[i].value.asFloat < 0.0 - (double)FLT_MAX)
+                    || (dataP[i].value.asFloat >(double)FLT_MAX))
+                {
+                    data_len = 8;
+                }
+                else
+                {
+                    data_len = 4;
+                }
+
+                length += prv_getHeaderLength(dataP[i].id, data_len) + data_len;
+            }
+            break;
+
+        case LWM2M_TYPE_BOOLEAN:
+            // Booleans are always encoded on one byte
+            length += prv_getHeaderLength(dataP[i].id, 1) + 1;
+            break;
+
+        case LWM2M_TYPE_OBJECT_LINK:
+            // Object Link are always encoded on four bytes
+            length += prv_getHeaderLength(dataP[i].id, 4) + 4;
+            break;
+
+        default:
+            length = -1;
+            break;
+        }
+    }
+
+    return length;
+}
+
+
+int tlv_serialize(bool isResourceInstance, 
+                  int size,
+                  lwm2m_data_t * dataP,
+                  uint8_t ** bufferP)
+{
+    int length;
+    int index;
+    int i;
+
+    LOG_ARG("isResourceInstance: %s, size: %d", isResourceInstance?"true":"false", size);
+
+    *bufferP = NULL;
+    length = prv_getLength(size, dataP);
+    if (length <= 0) return length;
+
+    *bufferP = (uint8_t *)lwm2m_malloc(length);
+    if (*bufferP == NULL) return 0;
+
+    index = 0;
+    for (i = 0 ; i < size && length != 0 ; i++)
+    {
+        int headerLen;
+        bool isInstance;
+
+        isInstance = isResourceInstance;
+        switch (dataP[i].type)
+        {
+        case LWM2M_TYPE_MULTIPLE_RESOURCE:
+            isInstance = true;
+            // fall through
+        case LWM2M_TYPE_OBJECT_INSTANCE:
+            {
+                uint8_t * tmpBuffer;
+                int res;
+
+                res = tlv_serialize(isInstance, dataP[i].value.asChildren.count, dataP[i].value.asChildren.array, &tmpBuffer);
+                if (res < 0)
+                {
+                    length = -1;
+                }
+                else
+                {
+                    size_t tmpLength;
+
+                    tmpLength = (size_t)res;
+                    headerLen = prv_createHeader(*bufferP + index, false, dataP[i].type, dataP[i].id, tmpLength);
+                    index += headerLen;
+                    if (tmpLength > 0)
+                    {
+                        memcpy(*bufferP + index, tmpBuffer, tmpLength);
+                        index += tmpLength;
+                        lwm2m_free(tmpBuffer);
+                    }
+                }
+            }
+            break;
+
+        case LWM2M_TYPE_OBJECT_LINK:
+            {
+                int k;
+                uint8_t buf[4];
+                uint32_t v = dataP[i].value.asObjLink.objectId;
+                v <<= 16;
+                v |= dataP[i].value.asObjLink.objectInstanceId;
+                for (k = 3; k >= 0; --k) {
+                    buf[k] = (uint8_t)(v & 0xFF);
+                    v >>= 8;
+                }
+                // keep encoding as buffer
+                headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, 4);
+                index += headerLen;
+                memcpy(*bufferP + index, buf, 4);
+                index += 4;
+            }
+            break;
+
+        case LWM2M_TYPE_STRING:
+        case LWM2M_TYPE_OPAQUE:
+            headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, dataP[i].value.asBuffer.length);
+            index += headerLen;
+            memcpy(*bufferP + index, dataP[i].value.asBuffer.buffer, dataP[i].value.asBuffer.length);
+            index += dataP[i].value.asBuffer.length;
+            break;
+
+        case LWM2M_TYPE_INTEGER:
+            {
+                size_t data_len;
+                uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE];
+
+                data_len = utils_encodeInt(dataP[i].value.asInteger, data_buffer);
+                headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, data_len);
+                index += headerLen;
+                memcpy(*bufferP + index, data_buffer, data_len);
+                index += data_len;
+            }
+            break;
+
+        case LWM2M_TYPE_FLOAT:
+            {
+                size_t data_len;
+                uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE];
+
+                data_len = utils_encodeFloat(dataP[i].value.asFloat, data_buffer);
+                headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, data_len);
+                index += headerLen;
+                memcpy(*bufferP + index, data_buffer, data_len);
+                index += data_len;
+            }
+            break;
+
+        case LWM2M_TYPE_BOOLEAN:
+            headerLen = prv_createHeader(*bufferP + index, isInstance, dataP[i].type, dataP[i].id, 1);
+            index += headerLen;
+            (*bufferP)[index] = dataP[i].value.asBoolean ? 1 : 0;
+            index += 1;
+            break;
+
+        default:
+            length = -1;
+            break;
+        }
+    }
+
+    if (length < 0)
+    {
+        lwm2m_free(*bufferP);
+        *bufferP = NULL;
+    }
+
+    LOG_ARG("returning %u", length);
+
+    return length;
+}
+
diff -r 000000000000 -r 3f48af28ebcd core/transaction.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/transaction.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,454 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Simon Bernard - Please refer to git log
+ *    Toby Jaffey - Please refer to git log
+ *    Pascal Rieux - Please refer to git log
+ *    Bosch Software Innovations GmbH - 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>
+
+*/
+
+/*
+Contains code snippets which are:
+
+ * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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.
+
+*/
+
+/************************************************************************
+ *  Function for communications transactions.
+ *
+ *  Basic specification: rfc7252
+ *
+ *  Transaction implements processing of piggybacked and separate response communication
+ *  dialogs specified in section 2.2 of the above specification.
+ *  The caller registers a callback function, which is called, when either the result is
+ *  received or a timeout occurs.
+ *
+ *  Supported dialogs:
+ *  Requests (GET - DELETE):
+ *  - CON with mid, without token => regular finished with corresponding ACK.MID
+ *  - CON with mid, with token => regular finished with corresponding ACK.MID and response containing
+ *                  the token. Supports both versions, with piggybacked ACK and separate ACK/response.
+ *                  Though the ACK.MID may be lost in the separate version, a matching response may
+ *                  finish the transaction even without the ACK.MID.
+ *  - NON without token => no transaction, no result expected!
+ *  - NON with token => regular finished with response containing the token.
+ *  Responses (COAP_201_CREATED - ?):
+ *  - CON with mid => regular finished with corresponding ACK.MID
+ */
+
+#include "internals.h"
+
+
+/*
+ * Modulo mask (+1 and +0.5 for rounding) for a random number to get the tick number for the random
+ * retransmission time between COAP_RESPONSE_TIMEOUT and COAP_RESPONSE_TIMEOUT*COAP_RESPONSE_RANDOM_FACTOR.
+ */
+#define COAP_RESPONSE_TIMEOUT_TICKS         (CLOCK_SECOND * COAP_RESPONSE_TIMEOUT)
+#define COAP_RESPONSE_TIMEOUT_BACKOFF_MASK  ((CLOCK_SECOND * COAP_RESPONSE_TIMEOUT * (COAP_RESPONSE_RANDOM_FACTOR - 1)) + 1.5)
+
+static int prv_checkFinished(lwm2m_transaction_t * transacP,
+                             coap_packet_t * receivedMessage)
+{
+    int len;
+    const uint8_t* token;
+    coap_packet_t * transactionMessage = transacP->message;
+
+    if (COAP_DELETE < transactionMessage->code)
+    {
+        // response
+        return transacP->ack_received ? 1 : 0;
+    }
+    if (!IS_OPTION(transactionMessage, COAP_OPTION_TOKEN))
+    {
+        // request without token
+        return transacP->ack_received ? 1 : 0;
+    }
+
+    len = coap_get_header_token(receivedMessage, &token);
+    if (transactionMessage->token_len == len)
+    {
+        if (memcmp(transactionMessage->token, token, len)==0) return 1;
+    }
+
+    return 0;
+}
+
+lwm2m_transaction_t * transaction_new(void * sessionH,
+                                      coap_method_t method,
+                                      char * altPath,
+                                      lwm2m_uri_t * uriP,
+                                      uint16_t mID,
+                                      uint8_t token_len,
+                                      uint8_t* token)
+{
+    lwm2m_transaction_t * transacP;
+    int result;
+
+    LOG_ARG("method: %d, altPath: \"%s\", mID: %d, token_len: %d",
+            method, altPath, mID, token_len);
+    LOG_URI(uriP);
+
+    // no transactions without peer
+    if (NULL == sessionH) return NULL;
+
+    transacP = (lwm2m_transaction_t *)lwm2m_malloc(sizeof(lwm2m_transaction_t));
+
+    if (NULL == transacP) return NULL;
+    memset(transacP, 0, sizeof(lwm2m_transaction_t));
+
+    transacP->message = lwm2m_malloc(sizeof(coap_packet_t));
+    if (NULL == transacP->message) goto error;
+
+    coap_init_message(transacP->message, COAP_TYPE_CON, method, mID);
+
+    transacP->peerH = sessionH;
+
+    transacP->mID = mID;
+
+    if (altPath != NULL)
+    {
+        // TODO: Support multi-segment alternative path
+        coap_set_header_uri_path_segment(transacP->message, altPath + 1);
+    }
+    if (NULL != uriP)
+    {
+        result = utils_intCopy(transacP->objStringID, LWM2M_STRING_ID_MAX_LEN, uriP->objectId);
+        if (result < 0) goto error;
+
+        coap_set_header_uri_path_segment(transacP->message, transacP->objStringID);
+
+        if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+        {
+            result = utils_intCopy(transacP->instanceStringID, LWM2M_STRING_ID_MAX_LEN, uriP->instanceId);
+            if (result < 0) goto error;
+            coap_set_header_uri_path_segment(transacP->message, transacP->instanceStringID);
+        }
+        else
+        {
+            if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+            {
+                coap_set_header_uri_path_segment(transacP->message, NULL);
+            }
+        }
+        if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+        {
+            result = utils_intCopy(transacP->resourceStringID, LWM2M_STRING_ID_MAX_LEN, uriP->resourceId);
+            if (result < 0) goto error;
+            coap_set_header_uri_path_segment(transacP->message, transacP->resourceStringID);
+        }
+    }
+    if (0 < token_len)
+    {
+        if (NULL != token)
+        {
+            coap_set_header_token(transacP->message, token, token_len);
+        }
+        else {
+            // generate a token
+            uint8_t temp_token[COAP_TOKEN_LEN];
+            time_t tv_sec = lwm2m_gettime();
+
+            // initialize first 6 bytes, leave the last 2 random
+            temp_token[0] = mID;
+            temp_token[1] = mID >> 8;
+            temp_token[2] = tv_sec;
+            temp_token[3] = tv_sec >> 8;
+            temp_token[4] = tv_sec >> 16;
+            temp_token[5] = tv_sec >> 24;
+            // use just the provided amount of bytes
+            coap_set_header_token(transacP->message, temp_token, token_len);
+        }
+    }
+
+    LOG("Exiting on success");
+    return transacP;
+
+error:
+    LOG("Exiting on failure");
+    lwm2m_free(transacP);
+    return NULL;
+}
+
+void transaction_free(lwm2m_transaction_t * transacP)
+{
+    LOG("Entering");
+    if (transacP->message) lwm2m_free(transacP->message);
+    if (transacP->buffer) lwm2m_free(transacP->buffer);
+    lwm2m_free(transacP);
+}
+
+void transaction_remove(lwm2m_context_t * contextP,
+                        lwm2m_transaction_t * transacP)
+{
+    LOG("Entering");
+    contextP->transactionList = (lwm2m_transaction_t *) LWM2M_LIST_RM(contextP->transactionList, transacP->mID, NULL);
+    transaction_free(transacP);
+}
+
+bool transaction_handleResponse(lwm2m_context_t * contextP,
+                                 void * fromSessionH,
+                                 coap_packet_t * message,
+                                 coap_packet_t * response)
+{
+    bool found = false;
+    bool reset = false;
+    lwm2m_transaction_t * transacP;
+
+    LOG("Entering");
+    transacP = contextP->transactionList;
+
+    while (NULL != transacP)
+    {
+        if (lwm2m_session_is_equal(fromSessionH, transacP->peerH, contextP->userData) == true)
+        {
+            if (!transacP->ack_received)
+            {
+                if ((COAP_TYPE_ACK == message->type) || (COAP_TYPE_RST == message->type))
+                {
+                    if (transacP->mID == message->mid)
+	                {
+    	                found = true;
+        	            transacP->ack_received = true;
+            	        reset = COAP_TYPE_RST == message->type;
+            	    }
+                }
+            }
+
+            if (reset || prv_checkFinished(transacP, message))
+            {
+                // HACK: If a message is sent from the monitor callback,
+                // it will arrive before the registration ACK.
+                // So we resend transaction that were denied for authentication reason.
+                if (!reset)
+                {
+                    if (COAP_TYPE_CON == message->type && NULL != response)
+                    {
+                        coap_init_message(response, COAP_TYPE_ACK, 0, message->mid);
+                        message_send(contextP, response, fromSessionH);
+                    }
+                
+	                if ((COAP_401_UNAUTHORIZED == message->code) && (COAP_MAX_RETRANSMIT > transacP->retrans_counter))
+    	            {
+        	            transacP->ack_received = false;
+            	        transacP->retrans_time += COAP_RESPONSE_TIMEOUT;
+                	    return true;
+                	}
+				}       
+                if (transacP->callback != NULL)
+                {
+                    transacP->callback(transacP, message);
+                }
+                transaction_remove(contextP, transacP);
+                return true;
+            }
+            // if we found our guy, exit
+            if (found)
+            {
+                time_t tv_sec = lwm2m_gettime();
+                if (0 <= tv_sec)
+                {
+                    transacP->retrans_time = tv_sec;
+                }
+                if (transacP->response_timeout)
+                {
+                    transacP->retrans_time += transacP->response_timeout;
+                }
+                else
+                {
+                    transacP->retrans_time += COAP_RESPONSE_TIMEOUT * transacP->retrans_counter;
+                }
+                return true;
+            }
+        }
+
+        transacP = transacP->next;
+    }
+    return false;
+}
+
+int transaction_send(lwm2m_context_t * contextP,
+                     lwm2m_transaction_t * transacP)
+{
+    bool maxRetriesReached = false;
+
+    LOG("Entering");
+    if (transacP->buffer == NULL)
+    {
+        transacP->buffer_len = coap_serialize_get_size(transacP->message);
+        if (transacP->buffer_len == 0) return COAP_500_INTERNAL_SERVER_ERROR;
+
+        transacP->buffer = (uint8_t*)lwm2m_malloc(transacP->buffer_len);
+        if (transacP->buffer == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
+
+        transacP->buffer_len = coap_serialize_message(transacP->message, transacP->buffer);
+        if (transacP->buffer_len == 0)
+        {
+            lwm2m_free(transacP->buffer);
+            transacP->buffer = NULL;
+            transaction_remove(contextP, transacP);
+            return COAP_500_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    if (!transacP->ack_received)
+    {
+        long unsigned timeout;
+
+        if (0 == transacP->retrans_counter)
+        {
+            time_t tv_sec = lwm2m_gettime();
+            if (0 <= tv_sec)
+            {
+                transacP->retrans_time = tv_sec + COAP_RESPONSE_TIMEOUT;
+                transacP->retrans_counter = 1;
+                timeout = 0;
+            }
+            else
+            {
+                maxRetriesReached = true;
+            }
+        }
+        else
+        {
+            timeout = COAP_RESPONSE_TIMEOUT << (transacP->retrans_counter - 1);
+        }
+
+        if (COAP_MAX_RETRANSMIT + 1 >= transacP->retrans_counter)
+        {
+            (void)lwm2m_buffer_send(transacP->peerH, transacP->buffer, transacP->buffer_len, contextP->userData);
+
+            transacP->retrans_time += timeout;
+            transacP->retrans_counter += 1;
+        }
+        else
+        {
+            maxRetriesReached = true;
+        }
+    }
+
+    if (transacP->ack_received || maxRetriesReached)
+    {
+        if (transacP->callback)
+        {
+            transacP->callback(transacP, NULL);
+        }
+        transaction_remove(contextP, transacP);
+        return -1;
+    }
+
+    return 0;
+}
+
+void transaction_step(lwm2m_context_t * contextP,
+                      time_t currentTime,
+                      time_t * timeoutP)
+{
+    lwm2m_transaction_t * transacP;
+
+    LOG("Entering");
+    transacP = contextP->transactionList;
+    while (transacP != NULL)
+    {
+        // transaction_send() may remove transaction from the linked list
+        lwm2m_transaction_t * nextP = transacP->next;
+        int removed = 0;
+
+        if (transacP->retrans_time <= currentTime)
+        {
+            removed = transaction_send(contextP, transacP);
+        }
+
+        if (0 == removed)
+        {
+            time_t interval;
+
+            if (transacP->retrans_time > currentTime)
+            {
+                interval = transacP->retrans_time - currentTime;
+            }
+            else
+            {
+                interval = 1;
+            }
+
+            if (*timeoutP > interval)
+            {
+                *timeoutP = interval;
+            }
+        }
+        else
+        {
+            *timeoutP = 1;
+        }
+
+        transacP = nextP;
+    }
+}
diff -r 000000000000 -r 3f48af28ebcd core/uri.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/uri.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,329 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Fabien Fleutot - Please refer to git log
+ *    Toby Jaffey - 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 <ctype.h>
+
+
+static int prv_parseNumber(uint8_t * uriString,
+                            size_t uriLength,
+                            size_t * headP)
+{
+    int result = 0;
+
+    if (uriString[*headP] == '/')
+    {
+        // empty Object Instance ID with resource ID is not allowed
+        return -1;
+    }
+    while (*headP < uriLength && uriString[*headP] != '/')
+    {
+        if ('0' <= uriString[*headP] && uriString[*headP] <= '9')
+        {
+            result += uriString[*headP] - '0';
+            result *= 10;
+        }
+        else
+        {
+            return -1;
+        }
+        *headP += 1;
+    }
+
+    result /= 10;
+    return result;
+}
+
+
+int uri_getNumber(uint8_t * uriString,
+                   size_t uriLength)
+{
+    size_t index = 0;
+
+    return prv_parseNumber(uriString, uriLength, &index);
+}
+
+
+lwm2m_uri_t * uri_decode(char * altPath,
+                         multi_option_t *uriPath)
+{
+    lwm2m_uri_t * uriP;
+    int readNum;
+
+    LOG_ARG("altPath: \"%s\"", altPath);
+
+    uriP = (lwm2m_uri_t *)lwm2m_malloc(sizeof(lwm2m_uri_t));
+    if (NULL == uriP) return NULL;
+
+    memset(uriP, 0, sizeof(lwm2m_uri_t));
+
+    // Read object ID
+    if (NULL != uriPath
+     && URI_REGISTRATION_SEGMENT_LEN == uriPath->len
+     && 0 == strncmp(URI_REGISTRATION_SEGMENT, (char *)uriPath->data, uriPath->len))
+    {
+        uriP->flag |= LWM2M_URI_FLAG_REGISTRATION;
+        uriPath = uriPath->next;
+        if (uriPath == NULL) return uriP;
+    }
+    else if (NULL != uriPath
+     && URI_BOOTSTRAP_SEGMENT_LEN == uriPath->len
+     && 0 == strncmp(URI_BOOTSTRAP_SEGMENT, (char *)uriPath->data, uriPath->len))
+    {
+        uriP->flag |= LWM2M_URI_FLAG_BOOTSTRAP;
+        uriPath = uriPath->next;
+        if (uriPath != NULL) goto error;
+        return uriP;
+    }
+
+    if ((uriP->flag & LWM2M_URI_MASK_TYPE) != LWM2M_URI_FLAG_REGISTRATION)
+    {
+        // Read altPath if any
+        if (altPath != NULL)
+        {
+            int i;
+            if (NULL == uriPath)
+            {
+                lwm2m_free(uriP);
+                return NULL;
+            }
+            for (i = 0 ; i < uriPath->len ; i++)
+            {
+                if (uriPath->data[i] != altPath[i+1])
+                {
+                    lwm2m_free(uriP);
+                    return NULL;
+                }
+            }
+            uriPath = uriPath->next;
+        }
+        if (NULL == uriPath || uriPath->len == 0)
+        {
+            uriP->flag |= LWM2M_URI_FLAG_DELETE_ALL;
+            return uriP;
+        }
+    }
+
+    readNum = uri_getNumber(uriPath->data, uriPath->len);
+    if (readNum < 0 || readNum > LWM2M_MAX_ID) goto error;
+    uriP->objectId = (uint16_t)readNum;
+    uriP->flag |= LWM2M_URI_FLAG_OBJECT_ID;
+    uriPath = uriPath->next;
+
+    if ((uriP->flag & LWM2M_URI_MASK_TYPE) == LWM2M_URI_FLAG_REGISTRATION)
+    {
+        if (uriPath != NULL) goto error;
+        return uriP;
+    }
+    uriP->flag |= LWM2M_URI_FLAG_DM;
+
+    if (uriPath == NULL) return uriP;
+
+    // Read object instance
+    if (uriPath->len != 0)
+    {
+        readNum = uri_getNumber(uriPath->data, uriPath->len);
+        if (readNum < 0 || readNum >= LWM2M_MAX_ID) goto error;
+        uriP->instanceId = (uint16_t)readNum;
+        uriP->flag |= LWM2M_URI_FLAG_INSTANCE_ID;
+    }
+    uriPath = uriPath->next;
+
+    if (uriPath == NULL) return uriP;
+
+    // Read resource ID
+    if (uriPath->len != 0)
+    {
+        // resource ID without an instance ID is not allowed
+        if ((uriP->flag & LWM2M_URI_FLAG_INSTANCE_ID) == 0) goto error;
+
+        readNum = uri_getNumber(uriPath->data, uriPath->len);
+        if (readNum < 0 || readNum > LWM2M_MAX_ID) goto error;
+        uriP->resourceId = (uint16_t)readNum;
+        uriP->flag |= LWM2M_URI_FLAG_RESOURCE_ID;
+    }
+
+    // must be the last segment
+    if (NULL == uriPath->next)
+    {
+        LOG_URI(uriP);
+        return uriP;
+    }
+
+error:
+    LOG("Exiting on error");
+    lwm2m_free(uriP);
+    return NULL;
+}
+
+int lwm2m_stringToUri(const char * buffer,
+                      size_t buffer_len,
+                      lwm2m_uri_t * uriP)
+{
+    size_t head;
+    int readNum;
+
+    LOG_ARG("buffer_len: %u, buffer: \"%.*s\"", buffer_len, buffer_len, buffer);
+
+    if (buffer == NULL || buffer_len == 0 || uriP == NULL) return 0;
+
+    memset(uriP, 0, sizeof(lwm2m_uri_t));
+
+    // Skip any white space
+    head = 0;
+    while (head < buffer_len && isspace(buffer[head]&0xFF))
+    {
+        head++;
+    }
+    if (head == buffer_len) return 0;
+
+    // Check the URI start with a '/'
+    if (buffer[head] != '/') return 0;
+    head++;
+    if (head == buffer_len) return 0;
+
+    // Read object ID
+    readNum = prv_parseNumber((uint8_t *)buffer, buffer_len, &head);
+    if (readNum < 0 || readNum > LWM2M_MAX_ID) return 0;
+    uriP->objectId = (uint16_t)readNum;
+    uriP->flag |= LWM2M_URI_FLAG_OBJECT_ID;
+
+    if (buffer[head] == '/') head += 1;
+    if (head >= buffer_len)
+    {
+        LOG_ARG("Parsed characters: %u", head);
+        LOG_URI(uriP);
+        return head;
+    }
+
+    readNum = prv_parseNumber((uint8_t *)buffer, buffer_len, &head);
+    if (readNum < 0 || readNum >= LWM2M_MAX_ID) return 0;
+    uriP->instanceId = (uint16_t)readNum;
+    uriP->flag |= LWM2M_URI_FLAG_INSTANCE_ID;
+
+    if (buffer[head] == '/') head += 1;
+    if (head >= buffer_len)
+    {
+        LOG_ARG("Parsed characters: %u", head);
+        LOG_URI(uriP);
+        return head;
+    }
+
+    readNum = prv_parseNumber((uint8_t *)buffer, buffer_len, &head);
+    if (readNum < 0 || readNum >= LWM2M_MAX_ID) return 0;
+    uriP->resourceId = (uint16_t)readNum;
+    uriP->flag |= LWM2M_URI_FLAG_RESOURCE_ID;
+
+    if (head != buffer_len) return 0;
+
+    LOG_ARG("Parsed characters: %u", head);
+    LOG_URI(uriP);
+
+    return head;
+}
+
+int uri_toString(lwm2m_uri_t * uriP,
+                 uint8_t * buffer,
+                 size_t bufferLen,
+                 uri_depth_t * depthP)
+{
+    size_t head;
+    int res;
+
+    LOG_ARG("bufferLen: %u", bufferLen);
+    LOG_URI(uriP);
+
+    buffer[0] = '/';
+
+    if (uriP == NULL)
+    {
+        if (depthP) *depthP = URI_DEPTH_OBJECT;
+        return 1;
+    }
+
+    head = 1;
+
+    res = utils_intToText(uriP->objectId, buffer + head, bufferLen - head);
+    if (res <= 0) return -1;
+    head += res;
+    if (head >= bufferLen - 1) return -1;
+    if (depthP) *depthP = URI_DEPTH_OBJECT_INSTANCE;
+
+    if (LWM2M_URI_IS_SET_INSTANCE(uriP))
+    {
+        buffer[head] = '/';
+        head++;
+        res = utils_intToText(uriP->instanceId, buffer + head, bufferLen - head);
+        if (res <= 0) return -1;
+        head += res;
+        if (head >= bufferLen - 1) return -1;
+        if (depthP) *depthP = URI_DEPTH_RESOURCE;
+        if (LWM2M_URI_IS_SET_RESOURCE(uriP))
+        {
+            buffer[head] = '/';
+            head++;
+            res = utils_intToText(uriP->resourceId, buffer + head, bufferLen - head);
+            if (res <= 0) return -1;
+            head += res;
+            if (head >= bufferLen - 1) return -1;
+            if (depthP) *depthP = URI_DEPTH_RESOURCE_INSTANCE;
+        }
+    }
+
+    buffer[head] = '/';
+    head++;
+
+    LOG_ARG("length: %u, buffer: \"%.*s\"", head, head, buffer);
+
+    return head;
+}
diff -r 000000000000 -r 3f48af28ebcd core/utils.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/utils.c	Wed Apr 19 11:28:00 2017 +0000
@@ -0,0 +1,893 @@
+/*******************************************************************************
+ *
+ * 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
+ *    Toby Jaffey - 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>
+#include <float.h>
+
+
+int utils_plainTextToInt64(uint8_t * buffer,
+                           int length,
+                           int64_t * dataP)
+{
+    uint64_t result = 0;
+    int sign = 1;
+    int i = 0;
+
+    if (0 == length) return 0;
+
+    if (buffer[0] == '-')
+    {
+        sign = -1;
+        i = 1;
+    }
+
+    while (i < length)
+    {
+        if ('0' <= buffer[i] && buffer[i] <= '9')
+        {
+            if (result > (UINT64_MAX / 10)) return 0;
+            result *= 10;
+            result += buffer[i] - '0';
+        }
+        else
+        {
+            return 0;
+        }
+        i++;
+    }
+
+    if (result > INT64_MAX) return 0;
+
+    if (sign == -1)
+    {
+        *dataP = 0 - result;
+    }
+    else
+    {
+        *dataP = result;
+    }
+
+    return 1;
+}
+
+int utils_plainTextToFloat64(uint8_t * buffer,
+                             int length,
+                             double * dataP)
+{
+    double result;
+    int sign;
+    int i;
+
+    if (0 == length) return 0;
+
+    if (buffer[0] == '-')
+    {
+        sign = -1;
+        i = 1;
+    }
+    else
+    {
+        sign = 1;
+        i = 0;
+    }
+
+    result = 0;
+    while (i < length && buffer[i] != '.')
+    {
+        if ('0' <= buffer[i] && buffer[i] <= '9')
+        {
+            if (result > (DBL_MAX / 10)) return 0;
+            result *= 10;
+            result += (buffer[i] - '0');
+        }
+        else
+        {
+            return 0;
+        }
+        i++;
+    }
+    if (buffer[i] == '.')
+    {
+        double dec;
+
+        i++;
+        if (i == length) return 0;
+
+        dec = 0.1;
+        while (i < length)
+        {
+            if ('0' <= buffer[i] && buffer[i] <= '9')
+            {
+                if (result > (DBL_MAX - 1)) return 0;
+                result += (buffer[i] - '0') * dec;
+                dec /= 10;
+            }
+            else
+            {
+                return 0;
+            }
+            i++;
+        }
+    }
+
+    *dataP = result * sign;
+    return 1;
+}
+
+size_t utils_intToText(int64_t data,
+                       uint8_t * string,
+                       size_t length)
+{
+    int index;
+    bool minus;
+    size_t result;
+
+    if (data < 0)
+    {
+        minus = true;
+        data = 0 - data;
+    }
+    else
+    {
+        minus = false;
+    }
+
+    index = length - 1;
+    do
+    {
+        string[index] = '0' + data%10;
+        data /= 10;
+        index --;
+    } while (index >= 0 && data > 0);
+
+    if (data > 0) return 0;
+
+    if (minus == true)
+    {
+        if (index == 0) return 0;
+        string[index] = '-';
+    }
+    else
+    {
+        index++;
+    }
+
+    result = length - index;
+
+    if (result < length)
+    {
+        memmove(string, string + index, result);
+    }
+
+    return result;
+}
+
+size_t utils_floatToText(double data,
+                         uint8_t * string,
+                         size_t length)
+{
+    size_t intLength;
+    size_t decLength;
+    int64_t intPart;
+    double decPart;
+
+    if (data <= (double)INT64_MIN || data >= (double)INT64_MAX) return 0;
+
+    intPart = (int64_t)data;
+    decPart = data - intPart;
+    if (decPart < 0)
+    {
+        decPart = 1 - decPart;
+    }
+    else
+    {
+        decPart = 1 + decPart;
+    }
+
+    if (decPart <= 1 + FLT_EPSILON)
+    {
+        decPart = 0;
+    }
+
+    if (intPart == 0 && data < 0)
+    {
+        // deal with numbers between -1 and 0
+        if (length < 4) return 0;   // "-0.n"
+        string[0] = '-';
+        string[1] = '0';
+        intLength = 2;
+    }
+    else
+    {
+        intLength = utils_intToText(intPart, string, length);
+        if (intLength == 0) return 0;
+    }
+    decLength = 0;
+    if (decPart >= FLT_EPSILON)
+    {
+        double noiseFloor;
+
+        if (intLength >= length - 1) return 0;
+
+        noiseFloor = FLT_EPSILON;
+        do
+        {
+            decPart *= 10;
+            noiseFloor *= 10;
+        } while (decPart - (int64_t)decPart > noiseFloor);
+
+        decLength = utils_intToText(decPart, string + intLength, length - intLength);
+        if (decLength <= 1) return 0;
+
+        // replace the leading 1 with a dot
+        string[intLength] = '.';
+    }
+
+    return intLength + decLength;
+}
+
+size_t utils_int64ToPlainText(int64_t data,
+                              uint8_t ** bufferP)
+{
+#define _PRV_STR_LENGTH 32
+    uint8_t string[_PRV_STR_LENGTH];
+    size_t length;
+
+    length = utils_intToText(data, string, _PRV_STR_LENGTH);
+    if (length == 0) return 0;
+
+    *bufferP = (uint8_t *)lwm2m_malloc(length);
+    if (NULL == *bufferP) return 0;
+
+    memcpy(*bufferP, string, length);
+
+    return length;
+}
+
+
+size_t utils_float64ToPlainText(double data,
+                                uint8_t ** bufferP)
+{
+    uint8_t string[_PRV_STR_LENGTH * 2];
+    size_t length;
+
+    length = utils_floatToText(data, string, _PRV_STR_LENGTH * 2);
+    if (length == 0) return 0;
+
+    *bufferP = (uint8_t *)lwm2m_malloc(length);
+    if (NULL == *bufferP) return 0;
+
+    memcpy(*bufferP, string, length);
+
+    return length;
+}
+
+
+size_t utils_boolToPlainText(bool data,
+                             uint8_t ** bufferP)
+{
+    return utils_int64ToPlainText((int64_t)(data?1:0), bufferP);
+}
+
+lwm2m_binding_t utils_stringToBinding(uint8_t * buffer,
+                                      size_t length)
+{
+    if (length == 0) return BINDING_UNKNOWN;
+
+    switch (buffer[0])
+    {
+    case 'U':
+        switch (length)
+        {
+        case 1:
+            return BINDING_U;
+        case 2:
+            switch (buffer[1])
+            {
+            case 'Q':
+                 return BINDING_UQ;
+            case 'S':
+                 return BINDING_US;
+            default:
+                break;
+            }
+            break;
+        case 3:
+            if (buffer[1] == 'Q' && buffer[2] == 'S')
+            {
+                return BINDING_UQS;
+            }
+            break;
+        default:
+            break;
+        }
+        break;
+
+        case 'S':
+            switch (length)
+            {
+            case 1:
+                return BINDING_S;
+            case 2:
+                if (buffer[1] == 'Q')
+                {
+                    return BINDING_SQ;
+                }
+                break;
+            default:
+                break;
+            }
+            break;
+
+        default:
+            break;
+    }
+
+    return BINDING_UNKNOWN;
+}
+
+lwm2m_media_type_t utils_convertMediaType(coap_content_type_t type)
+{
+    // Here we just check the content type is a valid value for LWM2M
+    switch((uint16_t)type)
+    {
+    case TEXT_PLAIN:
+        return LWM2M_CONTENT_TEXT;
+    case APPLICATION_OCTET_STREAM:
+        return LWM2M_CONTENT_OPAQUE;
+    case LWM2M_CONTENT_TLV_OLD:
+        return LWM2M_CONTENT_TLV_OLD;
+    case LWM2M_CONTENT_TLV:
+        return LWM2M_CONTENT_TLV;
+    case LWM2M_CONTENT_JSON_OLD:
+        return LWM2M_CONTENT_JSON_OLD;
+    case LWM2M_CONTENT_JSON:
+        return LWM2M_CONTENT_JSON;
+    case APPLICATION_LINK_FORMAT:
+        return LWM2M_CONTENT_LINK;
+
+    default:
+        return LWM2M_CONTENT_TEXT;
+    }
+}
+
+#ifdef LWM2M_CLIENT_MODE
+lwm2m_server_t * utils_findServer(lwm2m_context_t * contextP,
+                                  void * fromSessionH)
+{
+    lwm2m_server_t * targetP;
+
+    targetP = contextP->serverList;
+    while (targetP != NULL
+        && false == lwm2m_session_is_equal(targetP->sessionH, fromSessionH, contextP->userData))
+    {
+        targetP = targetP->next;
+    }
+
+    return targetP;
+}
+#endif
+
+lwm2m_server_t * utils_findBootstrapServer(lwm2m_context_t * contextP,
+                                           void * fromSessionH)
+{
+#ifdef LWM2M_CLIENT_MODE
+
+    lwm2m_server_t * targetP;
+
+    targetP = contextP->bootstrapServerList;
+    while (targetP != NULL
+        && false == lwm2m_session_is_equal(targetP->sessionH, fromSessionH, contextP->userData))
+    {
+        targetP = targetP->next;
+    }
+
+    return targetP;
+
+#else
+
+    return NULL;
+
+#endif
+}
+
+int utils_isAltPathValid(const char * altPath)
+{
+    int i;
+
+    if (altPath == NULL) return 0;
+
+    if (altPath[0] != '/') return 0;
+
+    for (i = 1 ; altPath[i] != 0 ; i++)
+    {
+        // TODO: Support multi-segment alternative path
+        if (altPath[i] == '/') return 0;
+        // TODO: Check needs for sub-delims, ':' and '@'
+        if ((altPath[i] < 'A' || altPath[i] > 'Z')      // ALPHA
+         && (altPath[i] < 'a' || altPath[i] > 'z')
+         && (altPath[i] < '0' || altPath[i] > '9')      // DIGIT
+         && (altPath[i] != '-')                         // Other unreserved
+         && (altPath[i] != '.')
+         && (altPath[i] != '_')
+         && (altPath[i] != '~')
+         && (altPath[i] != '%'))                        // pct_encoded
+        {
+            return 0;
+        }
+
+    }
+    return 1;
+}
+
+// copy a string in a buffer.
+// return the number of copied bytes or -1 if the buffer is not large enough
+int utils_stringCopy(char * buffer,
+                     size_t length,
+                     const char * str)
+{
+    size_t i;
+
+    for (i = 0 ; i < length && str[i] != 0 ; i++)
+    {
+        buffer[i] = str[i];
+    }
+
+    if (i == length) return -1;
+
+    buffer[i] = 0;
+
+    return (int)i;
+}
+
+int utils_intCopy(char * buffer,
+                  size_t length,
+                  int32_t value)
+{
+#define _PRV_INT32_MAX_STR_LEN 11
+    uint8_t str[_PRV_INT32_MAX_STR_LEN];
+    size_t len;
+
+    len = utils_intToText(value, str, _PRV_INT32_MAX_STR_LEN);
+    if (len == 0) return -1;
+    if (len > length + 1) return -1;
+
+    memcpy(buffer, str, len);
+    buffer[len] = 0;
+
+    return len;
+}
+
+void utils_copyValue(void * dst,
+                     const void * src,
+                     size_t len)
+{		
+#ifdef LWM2M_BIG_ENDIAN
+    memcpy(dst, src, len);
+#else
+#ifdef LWM2M_LITTLE_ENDIAN
+    size_t i;
+
+    for (i = 0; i < len; i++)
+    {
+        ((uint8_t *)dst)[i] = ((uint8_t *)src)[len - 1 - i];
+    }
+#endif
+#endif
+}
+
+int utils_opaqueToInt(const uint8_t * buffer,
+                      size_t buffer_len,
+                      int64_t * dataP)
+{
+    *dataP = 0;
+
+    switch (buffer_len)
+    {
+    case 1:
+    {
+        *dataP = (int8_t)buffer[0];
+
+        break;
+    }
+
+    case 2:
+    {
+        int16_t value;
+
+        utils_copyValue(&value, buffer, buffer_len);
+
+        *dataP = value;
+        break;
+    }
+
+    case 4:
+    {
+        int32_t value;
+
+        utils_copyValue(&value, buffer, buffer_len);
+
+        *dataP = value;
+        break;
+    }
+
+    case 8:
+        utils_copyValue(dataP, buffer, buffer_len);
+        return buffer_len;
+
+    default:
+        return 0;
+    }
+
+    return buffer_len;
+}
+
+int utils_opaqueToFloat(const uint8_t * buffer,
+                        size_t buffer_len,
+                        double * dataP)
+{
+    switch (buffer_len)
+    {
+    case 4:
+    {
+        float temp;
+
+        utils_copyValue(&temp, buffer, buffer_len);
+
+        *dataP = temp;
+    }
+    return 4;
+
+    case 8:
+        utils_copyValue(dataP, buffer, buffer_len);
+        return 8;
+
+    default:
+        return 0;
+    }
+}
+
+/**
+* Encode an integer value to a byte representation.
+* Returns the length of the result. For values < 0xff length is 1,
+* for values < 0xffff length is 2 and so on.
+* @param data        Input value
+* @param data_buffer Result in data_buffer is in big endian encoding
+*                    Negative values are represented in two's complement as of
+*                    OMA-TS-LightweightM2M-V1_0-20160308-D, Appendix C
+*/
+size_t utils_encodeInt(int64_t data,
+                       uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE])
+{
+    size_t length = 0;
+
+    memset(data_buffer, 0, _PRV_64BIT_BUFFER_SIZE);
+
+    if (data >= INT8_MIN && data <= INT8_MAX)
+    {
+        length = 1;
+        data_buffer[0] = data;
+    }
+    else if (data >= INT16_MIN && data <= INT16_MAX)
+    {
+        int16_t value;
+
+        value = data;
+        length = 2;
+        data_buffer[0] = (value >> 8) & 0xFF;
+        data_buffer[1] = value & 0xFF;
+    }
+    else if (data >= INT32_MIN && data <= INT32_MAX)
+    {
+        int32_t value;
+
+        value = data;
+        length = 4;
+        utils_copyValue(data_buffer, &value, length);
+    }
+    else if (data >= INT64_MIN && data <= INT64_MAX)
+    {
+        length = 8;
+        utils_copyValue(data_buffer, &data, length);
+    }
+
+    return length;
+}
+
+size_t utils_encodeFloat(double data,
+                         uint8_t data_buffer[_PRV_64BIT_BUFFER_SIZE])
+{
+    size_t length = 0;
+
+    memset(data_buffer, 0, _PRV_64BIT_BUFFER_SIZE);
+
+    if ((data < 0.0 - (double)FLT_MAX) || (data >(double)FLT_MAX))
+    {
+        length = 8;
+        utils_copyValue(data_buffer, &data, 8);
+    }
+    else
+    {
+        float value;
+
+        length = 4;
+        value = (float)data;
+        utils_copyValue(data_buffer, &value, 4);
+    }
+
+    return length;
+}
+
+#define PRV_B64_PADDING '='
+
+static char b64Alphabet[64] =
+{
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+    'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+static void prv_encodeBlock(uint8_t input[3],
+                            uint8_t output[4])
+{
+    output[0] = b64Alphabet[input[0] >> 2];
+    output[1] = b64Alphabet[((input[0] & 0x03) << 4) | (input[1] >> 4)];
+    output[2] = b64Alphabet[((input[1] & 0x0F) << 2) | (input[2] >> 6)];
+    output[3] = b64Alphabet[input[2] & 0x3F];
+}
+
+static uint8_t prv_b64Revert(uint8_t value)
+{
+    if (value >= 'A' && value <= 'Z')
+    {
+        return (value - 'A');
+    }
+    if (value >= 'a' && value <= 'z')
+    {
+        return (26 + value - 'a');
+    }
+    if (value >= '0' && value <= '9')
+    {
+        return (52 + value - '0');
+    }
+    switch (value)
+    {
+    case '+':
+        return 62;
+    case '/':
+        return 63;
+    default:
+        return 0;
+    }
+}
+
+static void prv_decodeBlock(uint8_t input[4],
+                            uint8_t output[3])
+{
+    uint8_t tmp[4];
+    int i;
+
+    memset(output, 0, 3);
+
+    for (i = 0; i < 4; i++)
+    {
+        tmp[i] = prv_b64Revert(input[i]);
+    }
+
+    output[0] = (tmp[0] << 2) | (tmp[1] >> 4);
+    output[1] = (tmp[1] << 4) | (tmp[2] >> 2);
+    output[2] = (tmp[2] << 6) | tmp[3];
+}
+
+static size_t prv_getBase64Size(size_t dataLen)
+{
+    size_t result_len;
+
+    result_len = 4 * (dataLen / 3);
+    if (dataLen % 3) result_len += 4;
+
+    return result_len;
+}
+
+size_t utils_base64Encode(uint8_t * dataP,
+                          size_t dataLen, 
+                          uint8_t * bufferP,
+                          size_t bufferLen)
+{
+    unsigned int data_index;
+    unsigned int result_index;
+    size_t result_len;
+
+    result_len = prv_getBase64Size(dataLen);
+
+    if (result_len > bufferLen) return 0;
+
+    data_index = 0;
+    result_index = 0;
+    while (data_index < dataLen)
+    {
+        switch (dataLen - data_index)
+        {
+        case 0:
+            // should never happen
+            break;
+        case 1:
+            bufferP[result_index] = b64Alphabet[dataP[data_index] >> 2];
+            bufferP[result_index + 1] = b64Alphabet[(dataP[data_index] & 0x03) << 4];
+            bufferP[result_index + 2] = PRV_B64_PADDING;
+            bufferP[result_index + 3] = PRV_B64_PADDING;
+            break;
+        case 2:
+            bufferP[result_index] = b64Alphabet[dataP[data_index] >> 2];
+            bufferP[result_index + 1] = b64Alphabet[(dataP[data_index] & 0x03) << 4 | (dataP[data_index + 1] >> 4)];
+            bufferP[result_index + 2] = b64Alphabet[(dataP[data_index + 1] & 0x0F) << 2];
+            bufferP[result_index + 3] = PRV_B64_PADDING;
+            break;
+        default:
+            prv_encodeBlock(dataP + data_index, bufferP + result_index);
+            break;
+        }
+        data_index += 3;
+        result_index += 4;
+    }
+
+    return result_len;
+}
+
+size_t utils_opaqueToBase64(uint8_t * dataP,
+                            size_t dataLen,
+                            uint8_t ** bufferP)
+{
+    size_t buffer_len;
+    size_t result_len;
+
+    buffer_len = prv_getBase64Size(dataLen);
+
+    *bufferP = (uint8_t *)lwm2m_malloc(buffer_len);
+    if (!*bufferP) return 0;
+    memset(*bufferP, 0, buffer_len);
+
+    result_len = utils_base64Encode(dataP, dataLen, *bufferP, buffer_len);
+
+    if (result_len == 0)
+    {
+        lwm2m_free(*bufferP);
+        *bufferP = NULL;
+    }
+ 
+    return result_len;
+}
+
+size_t utils_base64ToOpaque(uint8_t * dataP,
+                            size_t dataLen,
+                            uint8_t ** bufferP)
+{
+    size_t data_index;
+    size_t result_index;
+    size_t result_len;
+
+    if (dataLen % 4) return 0;
+
+    result_len = (dataLen >> 2) * 3;
+    *bufferP = (uint8_t *)lwm2m_malloc(result_len);
+    if (NULL == *bufferP) return 0;
+    memset(*bufferP, 0, result_len);
+
+    // remove padding
+    while (dataP[dataLen - 1] == PRV_B64_PADDING)
+    {
+        dataLen--;
+    }
+
+    data_index = 0;
+    result_index = 0;
+    while (data_index < dataLen)
+    {
+        prv_decodeBlock(dataP + data_index, *bufferP + result_index);
+        data_index += 4;
+        result_index += 3;
+    }
+    switch (data_index - dataLen)
+    {
+    case 0:
+        break;
+    case 2:
+    {
+        uint8_t tmp[2];
+
+        tmp[0] = prv_b64Revert(dataP[dataLen - 2]);
+        tmp[1] = prv_b64Revert(dataP[dataLen - 1]);
+
+        *bufferP[result_index - 3] = (tmp[0] << 2) | (tmp[1] >> 4);
+        *bufferP[result_index - 2] = (tmp[1] << 4);
+        result_len -= 2;
+    }
+    break;
+    case 3:
+    {
+        uint8_t tmp[3];
+
+        tmp[0] = prv_b64Revert(dataP[dataLen - 3]);
+        tmp[1] = prv_b64Revert(dataP[dataLen - 2]);
+        tmp[2] = prv_b64Revert(dataP[dataLen - 1]);
+
+        *bufferP[result_index - 3] = (tmp[0] << 2) | (tmp[1] >> 4);
+        *bufferP[result_index - 2] = (tmp[1] << 4) | (tmp[2] >> 2);
+        *bufferP[result_index - 1] = (tmp[2] << 6);
+        result_len -= 1;
+    }
+    break;
+    default:
+        // error
+        lwm2m_free(*bufferP);
+        *bufferP = NULL;
+        result_len = 0;
+        break;
+    }
+
+    return result_len;
+}
+
+lwm2m_data_type_t utils_depthToDatatype(uri_depth_t depth)
+{
+    switch (depth)
+    {
+    case URI_DEPTH_OBJECT:
+        return LWM2M_TYPE_OBJECT;
+    case URI_DEPTH_OBJECT_INSTANCE:
+        return LWM2M_TYPE_OBJECT_INSTANCE;
+    default:
+        break;
+    }
+
+    return LWM2M_TYPE_UNDEFINED;
+}