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

Revision:
0:1fa43ab66921
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/management.c	Wed Apr 19 11:30:02 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