From c21509790f447877a4656ab6cedf50a84d7391cf Mon Sep 17 00:00:00 2001 From: Scott Bertin Date: Tue, 5 Mar 2019 11:16:50 -0500 Subject: [PATCH 1/8] Fix some memory leaks in tests. Signed-off-by: Scott Bertin --- tests/senml_json_tests.c | 4 ++++ tests/tlv_json_lwm2m_data_test.c | 7 ++++++- tests/tlvtests.c | 9 +++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/senml_json_tests.c b/tests/senml_json_tests.c index a94ac1ceb..5b184105f 100644 --- a/tests/senml_json_tests.c +++ b/tests/senml_json_tests.c @@ -195,6 +195,7 @@ static void senml_json_test_raw(const char * uriStr, { senml_json_test_data(uriStr, LWM2M_CONTENT_TLV, tlvP, size, id); } + lwm2m_data_free(size, tlvP); } static void senml_json_test_raw_expected(const char * uriStr, @@ -227,6 +228,7 @@ static void senml_json_test_raw_expected(const char * uriStr, senml_json_test_data(uriStr, LWM2M_CONTENT_TLV, tlvP, size, id); else if (format == LWM2M_CONTENT_SENML_JSON) senml_json_test_data(uriStr, LWM2M_CONTENT_TLV, tlvP, size, id); + lwm2m_data_free(size, tlvP); } static void senml_json_test_raw_error(const char * uriStr, @@ -454,6 +456,8 @@ static void senml_json_test_13(void) lwm2m_data_encode_bool(false, data1 + 16); senml_json_test_data("/12/0", LWM2M_CONTENT_SENML_JSON, data1, 17, "13"); + + lwm2m_data_free(1, data1); } static void senml_json_test_14(void) diff --git a/tests/tlv_json_lwm2m_data_test.c b/tests/tlv_json_lwm2m_data_test.c index 8c4b2d414..b44ac75ad 100644 --- a/tests/tlv_json_lwm2m_data_test.c +++ b/tests/tlv_json_lwm2m_data_test.c @@ -144,6 +144,7 @@ static void test_raw(const char * uriStr, { test_data(uriStr, LWM2M_CONTENT_TLV, tlvP, size, id); } + lwm2m_data_free(size, tlvP); } static void test_raw_expected(const char * uriStr, @@ -174,6 +175,8 @@ static void test_raw_expected(const char * uriStr, test_data(uriStr, LWM2M_CONTENT_JSON, tlvP, size, id); else if (format == LWM2M_CONTENT_JSON) test_data(uriStr, LWM2M_CONTENT_TLV, tlvP, size, id); + + lwm2m_data_free(size, tlvP); } static void test_1(void) @@ -186,7 +189,7 @@ static void test_1(void) CU_ASSERT_TRUE_FATAL(size>0); // Serialize to the same format and compare to the input buffer test_data_and_compare(NULL, format, tlvP, size, "1", (uint8_t*)buffer, testLen); - + lwm2m_data_free(size, tlvP); } static void test_2(void) @@ -358,6 +361,8 @@ static void test_10(void) test_data(NULL, LWM2M_CONTENT_TLV, data1, 17, "10a"); test_data("/12/0", LWM2M_CONTENT_JSON, data1, 17, "10b"); + + lwm2m_data_free(17, data1); } static void test_11(void) diff --git a/tests/tlvtests.c b/tests/tlvtests.c index 240b74f17..30ae2076f 100644 --- a/tests/tlvtests.c +++ b/tests/tlvtests.c @@ -29,6 +29,7 @@ static void test_tlv_new(void) lwm2m_data_t *dataP = lwm2m_data_new(10); CU_ASSERT_PTR_NOT_NULL(dataP); MEMORY_TRACE_AFTER(<); + lwm2m_data_free(10, dataP); } static void test_tlv_free(void) @@ -266,6 +267,7 @@ static void test_tlv_int(void) result = lwm2m_data_decode_int(dataP, &value); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(value, 18); + lwm2m_free(dataP->value.asBuffer.buffer); lwm2m_data_encode_string("-14678", dataP); CU_ASSERT_EQUAL(dataP->type, LWM2M_TYPE_STRING); @@ -274,6 +276,7 @@ static void test_tlv_int(void) result = lwm2m_data_decode_int(dataP, &value); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(value, -14678); + lwm2m_free(dataP->value.asBuffer.buffer); uint8_t data1[] = { 0xed, 0xcc }; lwm2m_data_encode_opaque(data1, sizeof(data1), dataP); @@ -338,6 +341,7 @@ static void test_tlv_uint(void) result = lwm2m_data_decode_uint(dataP, &value); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(value, 18); + lwm2m_free(dataP->value.asBuffer.buffer); lwm2m_data_encode_string("-14678", dataP); CU_ASSERT_EQUAL(dataP->type, LWM2M_TYPE_STRING); @@ -345,6 +349,7 @@ static void test_tlv_uint(void) CU_ASSERT(0 == memcmp(dataP->value.asBuffer.buffer, "-14678", 6)); result = lwm2m_data_decode_uint(dataP, &value); CU_ASSERT_EQUAL(result, 0); + lwm2m_free(dataP->value.asBuffer.buffer); uint8_t data1[] = { 0xed, 0xcc }; lwm2m_data_encode_opaque(data1, sizeof(data1), dataP); @@ -366,6 +371,7 @@ static void test_tlv_uint(void) result = lwm2m_data_decode_uint(dataP, &value); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(value, 0x7f34567891223344); + lwm2m_free(dataP->value.asBuffer.buffer); uint8_t data3[] = { 0x8f, 0x34, 0x56, 0x78, 0x91, 0x22, 0x33, 0x44 }; lwm2m_data_encode_opaque(data3, sizeof(data3), dataP); @@ -413,6 +419,7 @@ static void test_tlv_bool(void) result = lwm2m_data_decode_bool(dataP, &value); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(value, true); + lwm2m_free(dataP->value.asBuffer.buffer); lwm2m_data_encode_string("0", dataP); CU_ASSERT_EQUAL(dataP->type, LWM2M_TYPE_STRING); @@ -421,6 +428,7 @@ static void test_tlv_bool(void) result = lwm2m_data_decode_bool(dataP, &value); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(value, false); + lwm2m_free(dataP->value.asBuffer.buffer); uint8_t data1[] = { 0x00 }; lwm2m_data_encode_opaque(data1, sizeof(data1), dataP); @@ -472,6 +480,7 @@ static void test_tlv_float(void) result = lwm2m_data_decode_float(dataP, &value); CU_ASSERT_EQUAL(result, 1); CU_ASSERT_EQUAL(value, 1234.56); + lwm2m_free(dataP->value.asBuffer.buffer); lwm2m_data_encode_string("-123456789.987", dataP); CU_ASSERT_EQUAL(dataP->type, LWM2M_TYPE_STRING); From bd5b63244217e2b98897b29093e194c654d10c4f Mon Sep 17 00:00:00 2001 From: Scott Bertin Date: Fri, 8 Mar 2019 13:40:24 -0500 Subject: [PATCH 2/8] Fix registration update clearing client binding. Signed-off-by: Scott Bertin --- core/registration.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/registration.c b/core/registration.c index b82afd4a3..ebeb4b0e9 100644 --- a/core/registration.c +++ b/core/registration.c @@ -1215,6 +1215,9 @@ static int prv_getParameters(multi_option_t * query, query = query->next; } + /* Binding not specified. */ + if (*bindingP == 0) *bindingP = BINDING_UNKNOWN; + if (*versionP == VERSION_1_0 && (*bindingP & (BINDING_T|BINDING_N)) != 0) { From 7aa6033d8eded58ed59b7bcb4f1f960ad2dd7194 Mon Sep 17 00:00:00 2001 From: Scott Bertin Date: Tue, 5 Mar 2019 11:18:48 -0500 Subject: [PATCH 3/8] Fix several JSON and SenML JSON issues. Fix problem serializing resource instance reads. Fix handling of empty strings and opaque. Fix JSON serializing single instance resource as multiple. Fixes #378. Fix JSON parsing of multiple instance resources. Fixes #399. Signed-off-by: Scott Bertin --- core/internals.h | 2 +- core/json.c | 37 ++++++----- core/json_common.c | 109 +++++++++++++++++++++---------- core/senml_json.c | 52 +++------------ tests/senml_json_tests.c | 54 +++++++++++++-- tests/tlv_json_lwm2m_data_test.c | 37 +++++++++++ 6 files changed, 188 insertions(+), 103 deletions(-) diff --git a/core/internals.h b/core/internals.h index c01745b95..3c39c80c8 100644 --- a/core/internals.h +++ b/core/internals.h @@ -366,7 +366,7 @@ lwm2m_data_t * json_extendData(lwm2m_data_t * parentP); int json_dataStrip(int size, lwm2m_data_t * dataP, lwm2m_data_t ** resultP); lwm2m_data_t * json_findDataItem(lwm2m_data_t * listP, size_t count, uint16_t id); uri_depth_t json_decreaseLevel(uri_depth_t level); -int json_findAndCheckData(const lwm2m_uri_t * uriP, uri_depth_t level, size_t size, const lwm2m_data_t * tlvP, lwm2m_data_t ** targetP); +int json_findAndCheckData(const lwm2m_uri_t * uriP, uri_depth_t baseLevel, size_t size, const lwm2m_data_t * tlvP, lwm2m_data_t ** targetP, uri_depth_t *targetLevelP); #endif // defined in discover.c diff --git a/core/json.c b/core/json.c index bfbe0e1f1..a311850df 100644 --- a/core/json.c +++ b/core/json.c @@ -652,6 +652,10 @@ int json_parse(lwm2m_uri_t * uriP, } else { + /* Base name may have a trailing "/" on a multiple instance + * resource. This isn't valid for a URI string in LWM2M 1.0. + * Strip off any trailing "/" to avoid an error. */ + if (buffer[bnStart + bnLen - 1] == '/') bnLen -= 1; res = lwm2m_stringToUri((char *)buffer + bnStart, bnLen, &baseURI); if (res < 0 || (size_t)res != bnLen) goto error; baseUriP = &baseURI; @@ -700,18 +704,10 @@ int json_parse(lwm2m_uri_t * uriP, 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 = json_dataStrip(1, targetP, &resP); - if (size <= 0) goto error; - lwm2m_data_free(count, parsedP); - parsedP = NULL; - } + size = json_dataStrip(1, targetP, &resP); + if (size <= 0) goto error; + lwm2m_data_free(count, parsedP); + parsedP = NULL; } } if (resP == NULL) goto error; @@ -774,7 +770,7 @@ static int prv_serializeValue(lwm2m_data_t * tlvP, bufferLen - head, tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length); - if (!res) return -1; + if (tlvP->value.asBuffer.length != 0 && res == 0) return -1; head += res; if (bufferLen - head < JSON_ITEM_STRING_END_SIZE) return -1; @@ -988,6 +984,7 @@ int json_serialize(lwm2m_uri_t * uriP, uint8_t baseUriStr[URI_MAX_STRING_LEN]; int baseUriLen; uri_depth_t rootLevel; + uri_depth_t baseLevel; int num; lwm2m_data_t * targetP; const uint8_t *parentUriStr = NULL; @@ -1011,13 +1008,21 @@ int json_serialize(lwm2m_uri_t * uriP, } #endif - baseUriLen = uri_toString(uriP, baseUriStr, URI_MAX_STRING_LEN, &rootLevel); + baseUriLen = uri_toString(uriP, baseUriStr, URI_MAX_STRING_LEN, &baseLevel); if (baseUriLen < 0) return -1; - rootLevel = json_decreaseLevel(rootLevel); - num = json_findAndCheckData(uriP, rootLevel, size, tlvP, &targetP); + num = json_findAndCheckData(uriP, baseLevel, size, tlvP, &targetP, &rootLevel); if (num < 0) return -1; + if (baseLevel >= URI_DEPTH_RESOURCE + && rootLevel == baseLevel + && baseUriLen > 1) + { + /* Remove the ID from the base name */ + while (baseUriLen > 1 && baseUriStr[baseUriLen - 1] != '/') baseUriLen--; + if (baseUriLen > 1 && baseUriStr[baseUriLen - 1] == '/') baseUriLen--; + } + while (num == 1 && (targetP->type == LWM2M_TYPE_OBJECT || targetP->type == LWM2M_TYPE_OBJECT_INSTANCE diff --git a/core/json_common.c b/core/json_common.c index a93fc8205..4ad6d8ae4 100644 --- a/core/json_common.c +++ b/core/json_common.c @@ -599,11 +599,12 @@ uri_depth_t json_decreaseLevel(uri_depth_t level) } } -int json_findAndCheckData(const lwm2m_uri_t * uriP, - uri_depth_t level, - size_t size, - const lwm2m_data_t * tlvP, - lwm2m_data_t ** targetP) +static int prv_findAndCheckData(const lwm2m_uri_t * uriP, + uri_depth_t desiredLevel, + size_t size, + const lwm2m_data_t * tlvP, + lwm2m_data_t ** targetP, + uri_depth_t *targetLevelP) { size_t index; int result; @@ -640,12 +641,13 @@ int json_findAndCheckData(const lwm2m_uri_t * uriP, *targetP = NULL; result = -1; - switch (level) + switch (desiredLevel) { case URI_DEPTH_OBJECT: if (tlvP[0].type == LWM2M_TYPE_OBJECT) { *targetP = (lwm2m_data_t*)tlvP; + *targetLevelP = URI_DEPTH_OBJECT; result = (int)size; } break; @@ -658,11 +660,13 @@ int json_findAndCheckData(const lwm2m_uri_t * uriP, { if (tlvP[index].id == uriP->objectId) { - return json_findAndCheckData(uriP, - level, - tlvP[index].value.asChildren.count, - tlvP[index].value.asChildren.array, - targetP); + *targetLevelP = URI_DEPTH_OBJECT_INSTANCE; + return prv_findAndCheckData(uriP, + desiredLevel, + tlvP[index].value.asChildren.count, + tlvP[index].value.asChildren.array, + targetP, + targetLevelP); } } break; @@ -683,11 +687,13 @@ int json_findAndCheckData(const lwm2m_uri_t * uriP, { if (tlvP[index].id == uriP->objectId) { - return json_findAndCheckData(uriP, - level, - tlvP[index].value.asChildren.count, - tlvP[index].value.asChildren.array, - targetP); + *targetLevelP = URI_DEPTH_OBJECT_INSTANCE; + return prv_findAndCheckData(uriP, + desiredLevel, + tlvP[index].value.asChildren.count, + tlvP[index].value.asChildren.array, + targetP, + targetLevelP); } } break; @@ -696,11 +702,12 @@ int json_findAndCheckData(const lwm2m_uri_t * uriP, { if (tlvP[index].id == uriP->instanceId) { - return json_findAndCheckData(uriP, - level, - tlvP[index].value.asChildren.count, - tlvP[index].value.asChildren.array, - targetP); + *targetLevelP = URI_DEPTH_RESOURCE; + return prv_findAndCheckData(uriP, + desiredLevel, + tlvP[index].value.asChildren.count, + tlvP[index].value.asChildren.array, + targetP, targetLevelP); } } break; @@ -719,11 +726,13 @@ int json_findAndCheckData(const lwm2m_uri_t * uriP, { if (tlvP[index].id == uriP->objectId) { - return json_findAndCheckData(uriP, - level, - tlvP[index].value.asChildren.count, - tlvP[index].value.asChildren.array, - targetP); + *targetLevelP = URI_DEPTH_OBJECT_INSTANCE; + return prv_findAndCheckData(uriP, + desiredLevel, + tlvP[index].value.asChildren.count, + tlvP[index].value.asChildren.array, + targetP, + targetLevelP); } } break; @@ -732,11 +741,13 @@ int json_findAndCheckData(const lwm2m_uri_t * uriP, { if (tlvP[index].id == uriP->instanceId) { - return json_findAndCheckData(uriP, - level, - tlvP[index].value.asChildren.count, - tlvP[index].value.asChildren.array, - targetP); + *targetLevelP = URI_DEPTH_RESOURCE; + return prv_findAndCheckData(uriP, + desiredLevel, + tlvP[index].value.asChildren.count, + tlvP[index].value.asChildren.array, + targetP, + targetLevelP); } } break; @@ -745,11 +756,13 @@ int json_findAndCheckData(const lwm2m_uri_t * uriP, { if (tlvP[index].id == uriP->resourceId) { - return json_findAndCheckData(uriP, - level, - tlvP[index].value.asChildren.count, - tlvP[index].value.asChildren.array, - targetP); + *targetLevelP = URI_DEPTH_RESOURCE_INSTANCE; + return prv_findAndCheckData(uriP, + desiredLevel, + tlvP[index].value.asChildren.count, + tlvP[index].value.asChildren.array, + targetP, + targetLevelP); } } break; @@ -767,4 +780,28 @@ int json_findAndCheckData(const lwm2m_uri_t * uriP, return result; } +int json_findAndCheckData(const lwm2m_uri_t * uriP, + uri_depth_t baseLevel, + size_t size, + const lwm2m_data_t * tlvP, + lwm2m_data_t ** targetP, + uri_depth_t *targetLevelP) +{ + uri_depth_t desiredLevel = json_decreaseLevel(baseLevel); + if (baseLevel < URI_DEPTH_RESOURCE) + { + *targetLevelP = desiredLevel; + } + else + { + *targetLevelP = baseLevel; + } + return prv_findAndCheckData(uriP, + desiredLevel, + size, + tlvP, + targetP, + targetLevelP); +} + #endif diff --git a/core/senml_json.c b/core/senml_json.c index 3ad5b8b32..4d06f89d2 100644 --- a/core/senml_json.c +++ b/core/senml_json.c @@ -806,7 +806,7 @@ static int prv_serializeValue(const lwm2m_data_t * tlvP, bufferLen - head, tlvP->value.asBuffer.buffer, tlvP->value.asBuffer.length); - if (!res) return -1; + if (res < tlvP->value.asBuffer.length) return -1; head += res; if (bufferLen - head < 1) return -1; @@ -901,7 +901,7 @@ static int prv_serializeValue(const lwm2m_data_t * tlvP, tlvP->value.asBuffer.length, buffer+head, bufferLen - head); - if (!res) return -1; + if (res < tlvP->value.asBuffer.length) return -1; head += res; } @@ -950,22 +950,6 @@ static int prv_serializeData(const lwm2m_data_t * tlvP, head = 0; - /* Check to override passed in level */ - switch (tlvP->type) - { - case LWM2M_TYPE_MULTIPLE_RESOURCE: - level = URI_DEPTH_RESOURCE; - break; - case LWM2M_TYPE_OBJECT: - level = URI_DEPTH_OBJECT; - break; - case LWM2M_TYPE_OBJECT_INSTANCE: - level = URI_DEPTH_OBJECT_INSTANCE; - break; - default: - break; - } - switch (tlvP->type) { case LWM2M_TYPE_MULTIPLE_RESOURCE: @@ -1112,35 +1096,15 @@ int senml_json_serialize(const lwm2m_uri_t * uriP, baseUriStr[baseUriLen++] = '/'; } - num = json_findAndCheckData(uriP, json_decreaseLevel(baseLevel), size, tlvP, &targetP); + num = json_findAndCheckData(uriP, baseLevel, size, tlvP, &targetP, &rootLevel); if (num < 0) return -1; - switch (tlvP->type) + if (baseLevel < rootLevel + && baseUriLen > 1 + && baseUriStr[baseUriLen - 1] != '/') { - case LWM2M_TYPE_OBJECT: - rootLevel = URI_DEPTH_OBJECT; - break; - case LWM2M_TYPE_OBJECT_INSTANCE: - rootLevel = URI_DEPTH_OBJECT_INSTANCE; - break; - case LWM2M_TYPE_MULTIPLE_RESOURCE: - if (baseUriLen > 1 && baseUriStr[baseUriLen - 1] != '/') - { - if (baseUriLen >= URI_MAX_STRING_LEN -1) return 0; - baseUriStr[baseUriLen++] = '/'; - } - rootLevel = URI_DEPTH_RESOURCE_INSTANCE; - break; - default: - if (baseLevel == URI_DEPTH_RESOURCE_INSTANCE) - { - rootLevel = URI_DEPTH_RESOURCE_INSTANCE; - } - else - { - rootLevel = URI_DEPTH_RESOURCE; - } - break; + if (baseUriLen >= URI_MAX_STRING_LEN -1) return 0; + baseUriStr[baseUriLen++] = '/'; } if (!baseUriLen || baseUriStr[baseUriLen - 1] != '/') diff --git a/tests/senml_json_tests.c b/tests/senml_json_tests.c index 5b184105f..098e26641 100644 --- a/tests/senml_json_tests.c +++ b/tests/senml_json_tests.c @@ -464,15 +464,12 @@ static void senml_json_test_14(void) { const char * buffer = "[{\"bn\":\"/5/0/1\", \ \"n\":\"/1\",\"vs\":\"http\"}]"; -#if 1 const char * expect = "[{\"bn\":\"/5/0/1/\",\"n\":\"1\",\"vs\":\"http\"}]"; + const char * expect2 = "[{\"bn\":\"/5/0/1/1\",\"vs\":\"http\"}]"; - senml_json_test_raw_expected("/5/0/1", (uint8_t *)buffer, strlen(buffer), expect, strlen(expect), LWM2M_CONTENT_SENML_JSON, "14"); -#else - const char * expect = "[{\"bn\":\"/5/0/1/1\",\"vs\":\"http\"}]"; + senml_json_test_raw_expected("/5/0/1", (uint8_t *)buffer, strlen(buffer), expect, strlen(expect), LWM2M_CONTENT_SENML_JSON, "14a"); - senml_json_test_raw_expected("/5/0/1/1", (uint8_t *)buffer, strlen(buffer), expect, strlen(expect), LWM2M_CONTENT_SENML_JSON, "14"); -#endif + senml_json_test_raw_expected("/5/0/1/1", (uint8_t *)buffer, strlen(buffer), expect2, strlen(expect2), LWM2M_CONTENT_SENML_JSON, "14b"); } static void senml_json_test_15(void) @@ -615,6 +612,49 @@ static void senml_json_test_24(void) lwm2m_data_free(1, dataP); } +static void senml_json_test_25(void) +{ + /* Test encoding from different depths */ + lwm2m_data_t * data1 = lwm2m_data_new(1); + lwm2m_data_t * data2 = lwm2m_data_new(1); + lwm2m_data_t * data3 = lwm2m_data_new(1); + lwm2m_data_t * data4 = lwm2m_data_new(1); + data4->id = 0; + lwm2m_data_encode_int(0, data4); + data3->id = 1; + lwm2m_data_encode_instances(data4, 1, data3); + data2->id = 0; + lwm2m_data_include(data3, 1, data2); + data1->id = 4; + lwm2m_data_include(data2, 1, data1); + const char *expect1 = "[{\"bn\":\"/4/\",\"n\":\"0/1/0\",\"v\":0}]"; + const char *expect2 = "[{\"bn\":\"/4/0/\",\"n\":\"1/0\",\"v\":0}]"; + const char *expect3 = "[{\"bn\":\"/4/0/1/\",\"n\":\"0\",\"v\":0}]"; + const char *expect4 = "[{\"bn\":\"/4/0/1/0\",\"v\":0}]"; + + senml_json_test_data_and_compare("/4", LWM2M_CONTENT_SENML_JSON, data1, 1, "25a", (const uint8_t *)expect1, strlen(expect1)); + senml_json_test_data_and_compare("/4/0", LWM2M_CONTENT_SENML_JSON, data1, 1, "25b", (const uint8_t *)expect2, strlen(expect2)); + senml_json_test_data_and_compare("/4/0", LWM2M_CONTENT_SENML_JSON, data2, 1, "25c", (const uint8_t *)expect2, strlen(expect2)); + senml_json_test_data_and_compare("/4/0/1", LWM2M_CONTENT_SENML_JSON, data1, 1, "25d", (const uint8_t *)expect3, strlen(expect3)); + senml_json_test_data_and_compare("/4/0/1", LWM2M_CONTENT_SENML_JSON, data2, 1, "25e", (const uint8_t *)expect3, strlen(expect3)); + senml_json_test_data_and_compare("/4/0/1", LWM2M_CONTENT_SENML_JSON, data3, 1, "25f", (const uint8_t *)expect3, strlen(expect3)); + senml_json_test_data_and_compare("/4/0/1/0", LWM2M_CONTENT_SENML_JSON, data1, 1, "25g", (const uint8_t *)expect4, strlen(expect4)); + senml_json_test_data_and_compare("/4/0/1/0", LWM2M_CONTENT_SENML_JSON, data2, 1, "25h", (const uint8_t *)expect4, strlen(expect4)); + senml_json_test_data_and_compare("/4/0/1/0", LWM2M_CONTENT_SENML_JSON, data3, 1, "25i", (const uint8_t *)expect4, strlen(expect4)); + senml_json_test_data_and_compare("/4/0/1/0", LWM2M_CONTENT_SENML_JSON, data4, 1, "25j", (const uint8_t *)expect4, strlen(expect4)); + + lwm2m_data_free(1, data1); +} + +static void senml_json_test_26(void) +{ + /* Test empty strings and opaques */ + const char * buffer = "[{\"bn\":\"/34/0/2\",\"vs\":\"\"}]"; + const char * buffer2 = "[{\"bn\":\"/34/0/2\",\"vd\":\"\"}]"; + senml_json_test_raw("/34/0/2", (uint8_t *)buffer, strlen(buffer), LWM2M_CONTENT_SENML_JSON, "26a"); + senml_json_test_raw("/34/0/2", (uint8_t *)buffer2, strlen(buffer2), LWM2M_CONTENT_SENML_JSON, "26b"); +} + static struct TestTable table[] = { { "test of senml_json_test_1()", senml_json_test_1 }, { "test of senml_json_test_2()", senml_json_test_2 }, @@ -640,6 +680,8 @@ static struct TestTable table[] = { { "test of senml_json_test_22()", senml_json_test_22 }, { "test of senml_json_test_23()", senml_json_test_23 }, { "test of senml_json_test_24()", senml_json_test_24 }, + { "test of senml_json_test_25()", senml_json_test_25 }, + { "test of senml_json_test_26()", senml_json_test_26 }, { NULL, NULL }, }; diff --git a/tests/tlv_json_lwm2m_data_test.c b/tests/tlv_json_lwm2m_data_test.c index b44ac75ad..c8324771f 100644 --- a/tests/tlv_json_lwm2m_data_test.c +++ b/tests/tlv_json_lwm2m_data_test.c @@ -395,6 +395,41 @@ static void test_13(void) test_raw(NULL, (uint8_t *)buffer, strlen(buffer), LWM2M_CONTENT_JSON, "13"); } +static void test_14(void) +{ + /* Test empty strings */ + const char * buffer = "{\"bn\":\"/34/0/\", \ + \"e\":[ \ + {\"n\":\"2\",\"sv\":\"\"}] \ + }"; + test_raw(NULL, (uint8_t *)buffer, strlen(buffer), LWM2M_CONTENT_JSON, "14"); +} + +static void test_15(void) +{ + /* Test multiple instance resources */ + lwm2m_data_t *tlvP; + lwm2m_uri_t uri; + int size; + const char *buffer = "{\"bn\":\"/2/0/2/\",\"e\":[{\"n\":\"0\",\"v\":15},{\"n\":\"999\",\"v\":1}]}"; + + LWM2M_URI_RESET(&uri); + uri.objectId = 2; + uri.instanceId = 0; + uri.resourceId = 2; + size = lwm2m_data_parse(&uri, (const uint8_t *)buffer, strlen(buffer), LWM2M_CONTENT_JSON, &tlvP); + if (size < 0) + { + printf("(Parsing 15a from JSON failed.)\t"); + } + CU_ASSERT_EQUAL_FATAL(size, 1); + CU_ASSERT_EQUAL(tlvP->id, 2); + CU_ASSERT_EQUAL(tlvP->type, LWM2M_TYPE_MULTIPLE_RESOURCE); + + + test_raw(NULL, (uint8_t *)buffer, strlen(buffer), LWM2M_CONTENT_JSON, "15b"); +} + static struct TestTable table[] = { { "test of test_1()", test_1 }, { "test of test_2()", test_2 }, @@ -409,6 +444,8 @@ static struct TestTable table[] = { { "test of test_11()", test_11 }, { "test of test_12()", test_12 }, { "test of test_13()", test_13 }, + { "test of test_14()", test_14 }, + { "test of test_15()", test_15 }, { NULL, NULL }, }; From 46db1b278e92f5b75146cf9cdb6ffe2481395787 Mon Sep 17 00:00:00 2001 From: Scott Bertin Date: Thu, 7 Mar 2019 11:14:55 -0500 Subject: [PATCH 4/8] Allow server writes to accept JSON for data. Signed-off-by: Scott Bertin --- examples/server/lwm2mserver.c | 46 +++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/examples/server/lwm2mserver.c b/examples/server/lwm2mserver.c index a39ac34f7..d64f239c0 100644 --- a/examples/server/lwm2mserver.c +++ b/examples/server/lwm2mserver.c @@ -350,6 +350,8 @@ static void prv_write_client(char * buffer, lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data; uint16_t clientId; lwm2m_uri_t uri; + lwm2m_data_t * dataP = NULL; + int count = 0; char * end = NULL; int result; @@ -367,7 +369,47 @@ static void prv_write_client(char * buffer, if (!check_end_of_args(end)) goto syntax_error; - result = lwm2m_dm_write(lwm2mH, clientId, &uri, LWM2M_CONTENT_TEXT, (uint8_t *)buffer, end - buffer, prv_result_callback, NULL); +#ifdef LWM2M_SUPPORT_SENML_JSON + if (count <= 0) + { + count = lwm2m_data_parse(&uri, (uint8_t *)buffer, end - buffer, LWM2M_CONTENT_SENML_JSON, &dataP); + } +#endif +#ifdef LWM2M_SUPPORT_JSON + if (count <= 0) + { + count = lwm2m_data_parse(&uri, (uint8_t *)buffer, end - buffer, LWM2M_CONTENT_JSON, &dataP); + } +#endif + if (count > 0) + { + lwm2m_client_t * clientP = NULL; + clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)lwm2mH->clientList, clientId); + if (clientP != NULL) + { + lwm2m_media_type_t format = clientP->format; + uint8_t *buffer; + int length = lwm2m_data_serialize(&uri, count, dataP, &format, &buffer); + if (length > 0) + { + result = lwm2m_dm_write(lwm2mH, clientId, &uri, format, buffer, length, prv_result_callback, NULL); + lwm2m_free(buffer); + } + else + { + result = COAP_500_INTERNAL_SERVER_ERROR; + } + } + else + { + result = COAP_404_NOT_FOUND; + } + lwm2m_data_free(count, dataP); + } + else + { + result = lwm2m_dm_write(lwm2mH, clientId, &uri, LWM2M_CONTENT_TEXT, (uint8_t *)buffer, end - buffer, prv_result_callback, NULL); + } if (result == 0) { @@ -910,7 +952,7 @@ int main(int argc, char *argv[]) {"write", "Write to a client.", " write CLIENT# URI DATA\r\n" " CLIENT#: client number as returned by command 'list'\r\n" " URI: uri to write to such as /3, /3/0/2, /1024/11, /1024/0/1\r\n" - " DATA: data to write\r\n" + " DATA: data to write. Text or a supported JSON format.\r\n" "Result will be displayed asynchronously.", prv_write_client, NULL}, {"time", "Write time-related attributes to a client.", " time CLIENT# URI PMIN PMAX\r\n" " CLIENT#: client number as returned by command 'list'\r\n" From b1f181d776f2175552238669d7d5adc41e5f0f96 Mon Sep 17 00:00:00 2001 From: Scott Bertin Date: Tue, 5 Mar 2019 11:31:54 -0500 Subject: [PATCH 5/8] Handle resource instances in reads. Signed-off-by: Scott Bertin --- core/data.c | 3 +- core/objects.c | 40 +++++- examples/client/object_access_control.c | 52 +++++-- examples/client/object_connectivity_moni.c | 128 ++++++++++++++---- examples/client/object_connectivity_stat.c | 10 +- examples/client/object_device.c | 149 ++++++++++++++++----- examples/client/object_firmware.c | 52 +++++-- examples/client/object_location.c | 54 +++++++- examples/client/object_security.c | 10 +- examples/client/object_server.c | 9 +- examples/client/test_object.c | 6 + examples/lightclient/object_device.c | 10 +- examples/lightclient/object_security.c | 10 +- examples/lightclient/object_server.c | 10 +- examples/lightclient/test_object.c | 6 + 15 files changed, 446 insertions(+), 103 deletions(-) diff --git a/core/data.c b/core/data.c index 8a5f9229b..e994babfd 100644 --- a/core/data.c +++ b/core/data.c @@ -626,7 +626,8 @@ void lwm2m_data_include(lwm2m_data_t * subDataP, dataP->type = LWM2M_TYPE_OBJECT; break; default: - return; + dataP->type = LWM2M_TYPE_MULTIPLE_RESOURCE; + break; } dataP->value.asChildren.count = count; dataP->value.asChildren.array = subDataP; diff --git a/core/objects.c b/core/objects.c index 57551c403..33813be70 100644 --- a/core/objects.c +++ b/core/objects.c @@ -67,6 +67,7 @@ uint8_t object_checkReadable(lwm2m_context_t * contextP, uint8_t result; lwm2m_object_t * targetP; lwm2m_data_t * dataP = NULL; + lwm2m_data_t * valueP = NULL; int size; LOG_URI(uriP); @@ -85,10 +86,21 @@ uint8_t object_checkReadable(lwm2m_context_t * contextP, if (dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; dataP->id = uriP->resourceId; + valueP = dataP; #ifndef LWM2M_VERSION_1_0 - // TODO: support resource instance - if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP)) return COAP_400_BAD_REQUEST; + if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP)) + { + lwm2m_data_t *subDataP = lwm2m_data_new(1); + if (subDataP == NULL) + { + lwm2m_data_free(1, dataP); + return COAP_500_INTERNAL_SERVER_ERROR; + } + subDataP->id = uriP->resourceInstanceId; + lwm2m_data_encode_instances(subDataP, 1, dataP); + valueP = subDataP; + } #endif result = targetP->readFunc(uriP->instanceId, &size, &dataP, targetP); @@ -96,7 +108,7 @@ uint8_t object_checkReadable(lwm2m_context_t * contextP, { if (attrP->toSet & ATTR_FLAG_NUMERIC) { - switch (dataP->type) + switch (valueP->type) { case LWM2M_TYPE_INTEGER: case LWM2M_TYPE_UNSIGNED_INTEGER: @@ -138,8 +150,19 @@ uint8_t object_readData(lwm2m_context_t * contextP, (*dataP)->id = uriP->resourceId; #ifndef LWM2M_VERSION_1_0 - // TODO: support resource instance - if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP)) return COAP_400_BAD_REQUEST; + if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP)) + { + lwm2m_data_t *subDataP = lwm2m_data_new(1); + if (subDataP == NULL) + { + lwm2m_data_free(*sizeP, *dataP); + *sizeP = 0; + *dataP = NULL; + return COAP_500_INTERNAL_SERVER_ERROR; + } + subDataP->id = uriP->resourceInstanceId; + lwm2m_data_encode_instances(subDataP, 1, *dataP); + } #endif } @@ -181,6 +204,13 @@ uint8_t object_readData(lwm2m_context_t * contextP, } } + if (result != COAP_205_CONTENT) + { + lwm2m_data_free(*sizeP, *dataP); + *sizeP = 0; + *dataP = NULL; + } + LOG_ARG("result: %u.%02u, size: %d", (result & 0xFF) >> 5, (result & 0x1F), *sizeP); return result; } diff --git a/examples/client/object_access_control.c b/examples/client/object_access_control.c index 9d8810cc8..ac1f75b32 100644 --- a/examples/client/object_access_control.c +++ b/examples/client/object_access_control.c @@ -13,6 +13,7 @@ * Contributors: * Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * ******************************************************************************/ @@ -66,40 +67,67 @@ static uint8_t prv_set_tlv(lwm2m_data_t* dataP, acc_ctrl_oi_t* accCtrlOiP) { switch (dataP->id) { case RES_M_OBJECT_ID: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(accCtrlOiP->objectId, dataP); return COAP_205_CONTENT; break; case RES_M_OBJECT_INSTANCE_ID: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(accCtrlOiP->objectInstId, dataP); return COAP_205_CONTENT; break; case RES_O_ACL: { - int ri; + size_t count; acc_ctrl_ri_t* accCtrlRiP; - for (accCtrlRiP =accCtrlOiP->accCtrlValList, ri=0; - accCtrlRiP!=NULL; - accCtrlRiP = accCtrlRiP->next, ri++); + for (accCtrlRiP = accCtrlOiP->accCtrlValList, count=0; + accCtrlRiP != NULL; + accCtrlRiP = accCtrlRiP->next, count++); - if (ri==0) // no values! + if (count == 0) // no values! { return COAP_404_NOT_FOUND; } else { - lwm2m_data_t* subTlvP = lwm2m_data_new(ri); - for (accCtrlRiP = accCtrlOiP->accCtrlValList, ri = 0; - accCtrlRiP!= NULL; - accCtrlRiP = accCtrlRiP->next, ri++) + lwm2m_data_t * subTlvP; + size_t i; + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) { - subTlvP[ri].id = accCtrlRiP->resInstId; - lwm2m_data_encode_int(accCtrlRiP->accCtrlValue, &subTlvP[ri]); + count = dataP->value.asChildren.count; + subTlvP = dataP->value.asChildren.array; + } + else + { + subTlvP = lwm2m_data_new(count); + for (accCtrlRiP = accCtrlOiP->accCtrlValList, i=0; + accCtrlRiP != NULL; + accCtrlRiP = accCtrlRiP->next, i++) + { + subTlvP[i].id = accCtrlRiP->resInstId; + } + lwm2m_data_encode_instances(subTlvP, count, dataP); + } + + for (i = 0; i < count; i++) + { + for (accCtrlRiP = accCtrlOiP->accCtrlValList; + accCtrlRiP != NULL; + accCtrlRiP = accCtrlRiP->next) + { + if (subTlvP[i].id == accCtrlRiP->resInstId) + { + lwm2m_data_encode_int(accCtrlRiP->accCtrlValue, subTlvP + i); + break; + } + } + if (accCtrlRiP == NULL) return COAP_404_NOT_FOUND; } - lwm2m_data_encode_instances(subTlvP, 2, dataP); return COAP_205_CONTENT; } } break; case RES_M_ACCESS_CONTROL_OWNER: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(accCtrlOiP->accCtrlOwner, dataP); return COAP_205_CONTENT; break; diff --git a/examples/client/object_connectivity_moni.c b/examples/client/object_connectivity_moni.c index 393065f01..b408190f5 100644 --- a/examples/client/object_connectivity_moni.c +++ b/examples/client/object_connectivity_moni.c @@ -13,6 +13,7 @@ * Contributors: * Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * *******************************************************************************/ @@ -88,84 +89,163 @@ typedef struct static uint8_t prv_set_value(lwm2m_data_t * dataP, conn_m_data_t * connDataP) { + lwm2m_data_t * subTlvP; + size_t count; + size_t i; + switch (dataP->id) { case RES_M_NETWORK_BEARER: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(VALUE_NETWORK_BEARER_GSM, dataP); return COAP_205_CONTENT; case RES_M_AVL_NETWORK_BEARER: { - int riCnt = 1; // reduced to 1 instance to fit in one block size - lwm2m_data_t * subTlvP; - subTlvP = lwm2m_data_new(riCnt); - subTlvP[0].id = 0; - lwm2m_data_encode_int(VALUE_AVL_NETWORK_BEARER_1, subTlvP); - lwm2m_data_encode_instances(subTlvP, riCnt, dataP); + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + count = dataP->value.asChildren.count; + subTlvP = dataP->value.asChildren.array; + } + else + { + count = 1; // reduced to 1 instance to fit in one block size + subTlvP = lwm2m_data_new(count); + for (i = 0; i < count; i++) subTlvP[i].id = i; + lwm2m_data_encode_instances(subTlvP, count, dataP); + } + + for (i = 0; i < count; i++) + { + switch (subTlvP[i].id) + { + case 0: + lwm2m_data_encode_int(VALUE_AVL_NETWORK_BEARER_1, subTlvP + i); + break; + default: + return COAP_404_NOT_FOUND; + } + } return COAP_205_CONTENT ; } case RES_M_RADIO_SIGNAL_STRENGTH: //s-int + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(connDataP->signalStrength, dataP); return COAP_205_CONTENT; case RES_O_LINK_QUALITY: //s-int + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(connDataP->linkQuality, dataP); return COAP_205_CONTENT ; case RES_M_IP_ADDRESSES: { - int ri, riCnt = 1; // reduced to 1 instance to fit in one block size - lwm2m_data_t* subTlvP = lwm2m_data_new(riCnt); - for (ri = 0; ri < riCnt; ri++) + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + count = dataP->value.asChildren.count; + subTlvP = dataP->value.asChildren.array; + } + else + { + count = 1; // reduced to 1 instance to fit in one block size + subTlvP = lwm2m_data_new(count); + for (i = 0; i < count; i++) subTlvP[i].id = i; + lwm2m_data_encode_instances(subTlvP, count, dataP); + } + + for (i = 0; i < count; i++) { - subTlvP[ri].id = ri; - lwm2m_data_encode_string(connDataP->ipAddresses[ri], subTlvP + ri); + switch (subTlvP[i].id) + { + case 0: + lwm2m_data_encode_string(connDataP->ipAddresses[i], subTlvP + i); + break; + default: + return COAP_404_NOT_FOUND; + } } - lwm2m_data_encode_instances(subTlvP, riCnt, dataP); return COAP_205_CONTENT ; } break; case RES_O_ROUTER_IP_ADDRESS: { - int ri, riCnt = 1; // reduced to 1 instance to fit in one block size - lwm2m_data_t* subTlvP = lwm2m_data_new(riCnt); - for (ri=0; ritype == LWM2M_TYPE_MULTIPLE_RESOURCE) { - subTlvP[ri].id = ri; - lwm2m_data_encode_string(connDataP->routerIpAddresses[ri], subTlvP + ri); + count = dataP->value.asChildren.count; + subTlvP = dataP->value.asChildren.array; + } + else + { + count = 1; // reduced to 1 instance to fit in one block size + subTlvP = lwm2m_data_new(count); + for (i = 0; i < count; i++) subTlvP[i].id = i; + lwm2m_data_encode_instances(subTlvP, count, dataP); + } + + for (i = 0; i < count; i++) + { + switch (subTlvP[i].id) + { + case 0: + lwm2m_data_encode_string(connDataP->routerIpAddresses[i], subTlvP + i); + break; + default: + return COAP_404_NOT_FOUND; + } } - lwm2m_data_encode_instances(subTlvP, riCnt, dataP); return COAP_205_CONTENT ; } break; case RES_O_LINK_UTILIZATION: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(connDataP->linkUtilization, dataP); return COAP_205_CONTENT; case RES_O_APN: { - int riCnt = 1; // reduced to 1 instance to fit in one block size - lwm2m_data_t * subTlvP; - subTlvP = lwm2m_data_new(riCnt); - subTlvP[0].id = 0; - lwm2m_data_encode_string(VALUE_APN_1, subTlvP); - lwm2m_data_encode_instances(subTlvP, riCnt, dataP); + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + count = dataP->value.asChildren.count; + subTlvP = dataP->value.asChildren.array; + } + else + { + count = 1; // reduced to 1 instance to fit in one block size + subTlvP = lwm2m_data_new(count); + for (i = 0; i < count; i++) subTlvP[i].id = i; + lwm2m_data_encode_instances(subTlvP, count, dataP); + } + + for (i = 0; i < count; i++) + { + switch (subTlvP[i].id) + { + case 0: + lwm2m_data_encode_string(VALUE_APN_1, subTlvP + i); + break; + default: + return COAP_404_NOT_FOUND; + } + } return COAP_205_CONTENT; } break; case RES_O_CELL_ID: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(connDataP->cellId, dataP); return COAP_205_CONTENT ; case RES_O_SMNC: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(VALUE_SMNC, dataP); return COAP_205_CONTENT ; case RES_O_SMCC: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(VALUE_SMCC, dataP); return COAP_205_CONTENT ; diff --git a/examples/client/object_connectivity_stat.c b/examples/client/object_connectivity_stat.c index 79d167e89..b58152f14 100644 --- a/examples/client/object_connectivity_stat.c +++ b/examples/client/object_connectivity_stat.c @@ -12,6 +12,7 @@ * * Contributors: * Bosch Software Innovations GmbH - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * *******************************************************************************/ @@ -127,7 +128,14 @@ static uint8_t prv_read(uint16_t instanceId, int * numDataP, lwm2m_data_t** data i = 0; do { - result = prv_set_tlv((*dataArrayP) + i, (conn_s_data_t*) (objectP->userData)); + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + } + else + { + result = prv_set_tlv((*dataArrayP) + i, (conn_s_data_t*) (objectP->userData)); + } i++; } while (i < *numDataP && result == COAP_205_CONTENT ); diff --git a/examples/client/object_device.c b/examples/client/object_device.c index 83affa545..88626f55d 100644 --- a/examples/client/object_device.c +++ b/examples/client/object_device.c @@ -17,6 +17,7 @@ * Axel Lorente - Please refer to git log * Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * *******************************************************************************/ @@ -164,22 +165,29 @@ static int prv_check_time_offset(char * buffer, static uint8_t prv_set_value(lwm2m_data_t * dataP, device_data_t * devDataP) { + lwm2m_data_t * subTlvP; + size_t count; + size_t i; // a simple switch structure is used to respond at the specified resource asked switch (dataP->id) { case RES_O_MANUFACTURER: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_string(PRV_MANUFACTURER, dataP); return COAP_205_CONTENT; case RES_O_MODEL_NUMBER: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_string(PRV_MODEL_NUMBER, dataP); return COAP_205_CONTENT; case RES_O_SERIAL_NUMBER: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_string(PRV_SERIAL_NUMBER, dataP); return COAP_205_CONTENT; case RES_O_FIRMWARE_VERSION: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_string(PRV_FIRMWARE_VERSION, dataP); return COAP_205_CONTENT; @@ -191,70 +199,139 @@ static uint8_t prv_set_value(lwm2m_data_t * dataP, case RES_O_AVL_POWER_SOURCES: { - lwm2m_data_t * subTlvP; - - subTlvP = lwm2m_data_new(2); - - subTlvP[0].id = 0; - lwm2m_data_encode_int(PRV_POWER_SOURCE_1, subTlvP); - subTlvP[1].id = 1; - lwm2m_data_encode_int(PRV_POWER_SOURCE_2, subTlvP + 1); + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + count = dataP->value.asChildren.count; + subTlvP = dataP->value.asChildren.array; + } + else + { + count = 2; + subTlvP = lwm2m_data_new(count); + for (i = 0; i < count; i++) subTlvP[i].id = i; + lwm2m_data_encode_instances(subTlvP, count, dataP); + } - lwm2m_data_encode_instances(subTlvP, 2, dataP); + for (i = 0; i < count; i++) + { + switch (subTlvP[i].id) + { + case 0: + lwm2m_data_encode_int(PRV_POWER_SOURCE_1, subTlvP + i); + break; + case 1: + lwm2m_data_encode_int(PRV_POWER_SOURCE_2, subTlvP + i); + break; + default: + return COAP_404_NOT_FOUND; + } + } return COAP_205_CONTENT; } case RES_O_POWER_SOURCE_VOLTAGE: { - lwm2m_data_t * subTlvP; - - subTlvP = lwm2m_data_new(2); - - subTlvP[0].id = 0; - lwm2m_data_encode_int(PRV_POWER_VOLTAGE_1, subTlvP); - subTlvP[1].id = 1; - lwm2m_data_encode_int(PRV_POWER_VOLTAGE_2, subTlvP + 1); + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + count = dataP->value.asChildren.count; + subTlvP = dataP->value.asChildren.array; + } + else + { + count = 2; + subTlvP = lwm2m_data_new(count); + for (i = 0; i < count; i++) subTlvP[i].id = i; + lwm2m_data_encode_instances(subTlvP, count, dataP); + } - lwm2m_data_encode_instances(subTlvP, 2, dataP); + for (i = 0; i < count; i++) + { + switch (subTlvP[i].id) + { + case 0: + lwm2m_data_encode_int(PRV_POWER_VOLTAGE_1, subTlvP + i); + break; + case 1: + lwm2m_data_encode_int(PRV_POWER_VOLTAGE_2, subTlvP + i); + break; + default: + return COAP_404_NOT_FOUND; + } + } return COAP_205_CONTENT; } case RES_O_POWER_SOURCE_CURRENT: { - lwm2m_data_t * subTlvP; - - subTlvP = lwm2m_data_new(2); + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + count = dataP->value.asChildren.count; + subTlvP = dataP->value.asChildren.array; + } + else + { + count = 2; + subTlvP = lwm2m_data_new(count); + for (i = 0; i < count; i++) subTlvP[i].id = i; + lwm2m_data_encode_instances(subTlvP, count, dataP); + } - subTlvP[0].id = 0; - lwm2m_data_encode_int(PRV_POWER_CURRENT_1, &subTlvP[0]); - subTlvP[1].id = 1; - lwm2m_data_encode_int(PRV_POWER_CURRENT_2, &subTlvP[1]); - - lwm2m_data_encode_instances(subTlvP, 2, dataP); + for (i = 0; i < count; i++) + { + switch (subTlvP[i].id) + { + case 0: + lwm2m_data_encode_int(PRV_POWER_CURRENT_1, subTlvP + i); + break; + case 1: + lwm2m_data_encode_int(PRV_POWER_CURRENT_2, subTlvP + i); + break; + default: + return COAP_404_NOT_FOUND; + } + } return COAP_205_CONTENT; } case RES_O_BATTERY_LEVEL: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(devDataP->battery_level, dataP); return COAP_205_CONTENT; case RES_O_MEMORY_FREE: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(devDataP->free_memory, dataP); return COAP_205_CONTENT; case RES_M_ERROR_CODE: { - lwm2m_data_t * subTlvP; - - subTlvP = lwm2m_data_new(1); - - subTlvP[0].id = 0; - lwm2m_data_encode_int(devDataP->error, subTlvP); + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + count = dataP->value.asChildren.count; + subTlvP = dataP->value.asChildren.array; + } + else + { + count = 1; + subTlvP = lwm2m_data_new(count); + for (i = 0; i < count; i++) subTlvP[i].id = i; + lwm2m_data_encode_instances(subTlvP, count, dataP); + } - lwm2m_data_encode_instances(subTlvP, 1, dataP); + for (i = 0; i < count; i++) + { + switch (subTlvP[i].id) + { + case 0: + lwm2m_data_encode_int(devDataP->error, subTlvP + i); + break; + default: + return COAP_404_NOT_FOUND; + } + } return COAP_205_CONTENT; } @@ -262,18 +339,22 @@ static uint8_t prv_set_value(lwm2m_data_t * dataP, return COAP_405_METHOD_NOT_ALLOWED; case RES_O_CURRENT_TIME: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(time(NULL) + devDataP->time, dataP); return COAP_205_CONTENT; case RES_O_UTC_OFFSET: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_string(devDataP->time_offset, dataP); return COAP_205_CONTENT; case RES_O_TIMEZONE: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_string(PRV_TIME_ZONE, dataP); return COAP_205_CONTENT; case RES_M_BINDING_MODES: + if (dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_string(PRV_BINDING_MODE, dataP); return COAP_205_CONTENT; diff --git a/examples/client/object_firmware.c b/examples/client/object_firmware.c index f9fbdefed..39d610623 100644 --- a/examples/client/object_firmware.c +++ b/examples/client/object_firmware.c @@ -17,6 +17,7 @@ * Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - Please refer to git log * Gregory Lemercier - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * *******************************************************************************/ @@ -88,7 +89,7 @@ static uint8_t prv_firmware_read(uint16_t instanceId, // is the server asking for the full object ? if (*numDataP == 0) { - *dataArrayP = lwm2m_data_new(3); + *dataArrayP = lwm2m_data_new(6); if (*dataArrayP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; *numDataP = 6; (*dataArrayP)[0].id = 3; @@ -112,55 +113,78 @@ static uint8_t prv_firmware_read(uint16_t instanceId, case RES_M_STATE: // firmware update state (int) + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(data->state, *dataArrayP + i); result = COAP_205_CONTENT; break; case RES_M_UPDATE_RESULT: + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(data->result, *dataArrayP + i); result = COAP_205_CONTENT; break; case RES_O_PKG_NAME: + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_string(data->pkg_name, *dataArrayP + i); result = COAP_205_CONTENT; break; case RES_O_PKG_VERSION: + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_string(data->pkg_version, *dataArrayP + i); result = COAP_205_CONTENT; break; case RES_O_UPDATE_PROTOCOL: { - int ri; + lwm2m_data_t * subTlvP; + size_t count; + size_t ri; int num = 0; - lwm2m_data_t* subTlvP = NULL; while ((num < LWM2M_FIRMWARE_PROTOCOL_NUM) && (data->protocol_support[num] != LWM2M_FIRMWARE_PROTOCOL_NULL)) num++; - if (num) { - subTlvP = lwm2m_data_new(num); - for (ri = 0; riprotocol_support[ri], subTlvP + ri); + if (subTlvP[ri].id >= num) return COAP_404_NOT_FOUND; + lwm2m_data_encode_int(data->protocol_support[subTlvP[ri].id], + subTlvP + ri); } - } else { + } + else + { /* If no protocol is provided, use CoAP as default (per spec) */ - num = 1; - subTlvP = lwm2m_data_new(num); - subTlvP[0].id = 0; - lwm2m_data_encode_int(0, subTlvP); + for (ri = 0; ri < count; ri++) + { + if (subTlvP[ri].id != 0) return COAP_404_NOT_FOUND; + lwm2m_data_encode_int(0, subTlvP + ri); + } } - lwm2m_data_encode_instances(subTlvP, num, *dataArrayP + i); result = COAP_205_CONTENT; break; } case RES_M_UPDATE_METHOD: + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; lwm2m_data_encode_int(data->delivery_method, *dataArrayP + i); result = COAP_205_CONTENT; break; diff --git a/examples/client/object_location.c b/examples/client/object_location.c index 5906202df..a1f8bb774 100644 --- a/examples/client/object_location.c +++ b/examples/client/object_location.c @@ -14,6 +14,7 @@ * Contributors: * Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * ******************************************************************************/ /*! \file @@ -65,11 +66,12 @@ #define RES_O_SPEED 6 //----- 3GPP TS 23.032 V11.0.0(2012-09) --------- -#define HORIZONTAL_VELOCITY 0 // for Octet-1 upper half(..<<4) -#define HORIZONTAL_VELOCITY_VERTICAL 1 // set vertical direction bit! -#define HORIZONTAL_VELOCITY_WITH_UNCERTAINTY 2 +#define HORIZONTAL_VELOCITY 0 // for Octet-1 upper half(..<<4) +#define HORIZONTAL_VELOCITY_VERTICAL 1 // set vertical direction bit! +#define HORIZONTAL_VELOCITY_WITH_UNCERTAINTY 2 +#define HORIZONTAL_VELOCITY_VERTICAL_WITH_UNCERTAINTY 3 -#define VELOCITY_OCTETS 5 // for HORIZONTAL_VELOCITY_WITH_UNCERTAINTY +#define VELOCITY_OCTETS 7 // for HORIZONTAL_VELOCITY_VERTICAL_WITH_UNCERTAINTY typedef struct { @@ -105,8 +107,39 @@ static uint8_t prv_res2tlv(lwm2m_data_t* dataP, lwm2m_data_encode_float(locDataP->radius, dataP); break; case RES_O_VELOCITY: - lwm2m_data_encode_string((const char*)locDataP->velocity, dataP); + { + size_t length; + switch( locDataP->velocity[0] >> 4 ) + { + case HORIZONTAL_VELOCITY: + { + length = 4; + break; + } + case HORIZONTAL_VELOCITY_VERTICAL: + { + length = 5; + break; + } + case HORIZONTAL_VELOCITY_WITH_UNCERTAINTY: + { + length = 5; + break; + } + case HORIZONTAL_VELOCITY_VERTICAL_WITH_UNCERTAINTY: + { + length = 7; + break; + } + default: + { + length = 0; + break; + } + } + lwm2m_data_encode_opaque(locDataP->velocity, length, dataP); break; + } case RES_M_TIMESTAMP: lwm2m_data_encode_int(locDataP->timestamp, dataP); break; @@ -171,7 +204,14 @@ static uint8_t prv_location_read(uint16_t objInstId, for (i = 0 ; i < *numDataP ; i++) { - result = prv_res2tlv ((*tlvArrayP)+i, locDataP); + if ((*tlvArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + } + else + { + result = prv_res2tlv ((*tlvArrayP)+i, locDataP); + } if (result!=COAP_205_CONTENT) break; } @@ -208,7 +248,7 @@ void location_setVelocity(lwm2m_object_t* locationObj, //-------------------------------------------------------------------- JH -- location_data_t* pData = locationObj->userData; pData->velocity[0] = HORIZONTAL_VELOCITY_WITH_UNCERTAINTY << 4; - pData->velocity[0] = (bearing & 0x100) >> 8; + pData->velocity[0] |= (bearing & 0x100) >> 8; pData->velocity[1] = (bearing & 0x0FF); pData->velocity[2] = horizontalSpeed >> 8; pData->velocity[3] = horizontalSpeed & 0xff; diff --git a/examples/client/object_security.c b/examples/client/object_security.c index 52950b69c..b3f484a44 100644 --- a/examples/client/object_security.c +++ b/examples/client/object_security.c @@ -15,6 +15,7 @@ * Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - Please refer to git log * Ville Skyttä - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * *******************************************************************************/ @@ -175,7 +176,14 @@ static uint8_t prv_security_read(uint16_t instanceId, i = 0; do { - result = prv_get_value((*dataArrayP) + i, targetP); + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + } + else + { + result = prv_get_value((*dataArrayP) + i, targetP); + } i++; } while (i < *numDataP && result == COAP_205_CONTENT); diff --git a/examples/client/object_server.c b/examples/client/object_server.c index fbc05a6e3..81273c4a7 100644 --- a/examples/client/object_server.c +++ b/examples/client/object_server.c @@ -358,7 +358,14 @@ static uint8_t prv_server_read(uint16_t instanceId, i = 0; do { - result = prv_get_value((*dataArrayP) + i, targetP); + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + } + else + { + result = prv_get_value((*dataArrayP) + i, targetP); + } i++; } while (i < *numDataP && result == COAP_205_CONTENT); diff --git a/examples/client/test_object.c b/examples/client/test_object.c index 168935cdb..f5b16df15 100644 --- a/examples/client/test_object.c +++ b/examples/client/test_object.c @@ -18,6 +18,7 @@ * Achim Kraus, Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - Please refer to git log * Ville Skyttä - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * *******************************************************************************/ @@ -153,6 +154,11 @@ static uint8_t prv_read(uint16_t instanceId, for (i = 0 ; i < *numDataP ; i++) { + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + return COAP_404_NOT_FOUND; + } + switch ((*dataArrayP)[i].id) { case 1: diff --git a/examples/lightclient/object_device.c b/examples/lightclient/object_device.c index 72e51926a..4dcb63780 100644 --- a/examples/lightclient/object_device.c +++ b/examples/lightclient/object_device.c @@ -17,6 +17,7 @@ * Axel Lorente - Please refer to git log * Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * *******************************************************************************/ @@ -157,7 +158,14 @@ static uint8_t prv_device_read(uint16_t instanceId, i = 0; do { - result = prv_set_value((*dataArrayP) + i); + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + } + else + { + result = prv_set_value((*dataArrayP) + i); + } i++; } while (i < *numDataP && result == COAP_205_CONTENT); diff --git a/examples/lightclient/object_security.c b/examples/lightclient/object_security.c index 2613ba5ba..e1022ab73 100644 --- a/examples/lightclient/object_security.c +++ b/examples/lightclient/object_security.c @@ -14,6 +14,7 @@ * David Navarro, Intel Corporation - initial API and implementation * Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * *******************************************************************************/ @@ -180,7 +181,14 @@ static uint8_t prv_security_read(uint16_t instanceId, i = 0; do { - result = prv_get_value((*dataArrayP) + i, targetP); + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + } + else + { + result = prv_get_value((*dataArrayP) + i, targetP); + } i++; } while (i < *numDataP && result == COAP_205_CONTENT); diff --git a/examples/lightclient/object_server.c b/examples/lightclient/object_server.c index a4ac65357..703035ac4 100644 --- a/examples/lightclient/object_server.c +++ b/examples/lightclient/object_server.c @@ -15,6 +15,7 @@ * Julien Vermillard, Sierra Wireless * Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * *******************************************************************************/ @@ -117,7 +118,14 @@ static uint8_t prv_server_read(uint16_t instanceId, i = 0; do { - result = prv_get_value((*dataArrayP) + i, targetP); + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + } + else + { + result = prv_get_value((*dataArrayP) + i, targetP); + } i++; } while (i < *numDataP && result == COAP_205_CONTENT); diff --git a/examples/lightclient/test_object.c b/examples/lightclient/test_object.c index e823302cb..d40ef4c71 100644 --- a/examples/lightclient/test_object.c +++ b/examples/lightclient/test_object.c @@ -18,6 +18,7 @@ * Achim Kraus, Bosch Software Innovations GmbH - Please refer to git log * Pascal Rieux - Please refer to git log * Ville Skyttä - Please refer to git log + * Scott Bertin, AMETEK, Inc. - Please refer to git log * *******************************************************************************/ @@ -154,6 +155,11 @@ static uint8_t prv_read(uint16_t instanceId, for (i = 0 ; i < *numDataP ; i++) { + if ((*dataArrayP)[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + return COAP_404_NOT_FOUND; + } + switch ((*dataArrayP)[i].id) { case 1: From 0e35e2f1dfef57ac4259e2eaf40b0604a70b45c4 Mon Sep 17 00:00:00 2001 From: Scott Bertin Date: Fri, 8 Mar 2019 08:24:27 -0500 Subject: [PATCH 6/8] Handle partial updates in writes. Fixes #332 and prepares for writes of individual resource instances. Signed-off-by: Scott Bertin --- core/bootstrap.c | 2 +- core/internals.h | 2 +- core/liblwm2m.h | 9 +- core/management.c | 4 +- core/objects.c | 22 +++- examples/client/lwm2mclient.c | 2 +- examples/client/object_access_control.c | 150 +++++++++++++++++++----- examples/client/object_device.c | 6 +- examples/client/object_firmware.c | 6 +- examples/client/object_security.c | 8 +- examples/client/object_server.c | 26 +++- examples/client/test_object.c | 26 +++- examples/lightclient/object_server.c | 26 +++- examples/lightclient/test_object.c | 26 +++- 14 files changed, 264 insertions(+), 51 deletions(-) diff --git a/core/bootstrap.c b/core/bootstrap.c index 31a7fa217..5ade18e4b 100644 --- a/core/bootstrap.c +++ b/core/bootstrap.c @@ -402,7 +402,7 @@ uint8_t bootstrap_handleCommand(lwm2m_context_t * contextP, } else { - result = object_write(contextP, uriP, format, message->payload, message->payload_len); + result = object_write(contextP, uriP, format, message->payload, message->payload_len, false); if (uriP->objectId == LWM2M_SECURITY_OBJECT_ID && result == COAP_204_CHANGED) { diff --git a/core/internals.h b/core/internals.h index 3c39c80c8..6b242063f 100644 --- a/core/internals.h +++ b/core/internals.h @@ -280,7 +280,7 @@ int uri_toString(const lwm2m_uri_t * uriP, uint8_t * buffer, size_t bufferLen, u // defined in objects.c uint8_t object_readData(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, int * sizeP, lwm2m_data_t ** dataP); uint8_t object_read(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, const uint16_t * accept, uint8_t acceptNum, lwm2m_media_type_t * formatP, uint8_t ** bufferP, size_t * lengthP); -uint8_t object_write(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length); +uint8_t object_write(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length, bool partial); uint8_t object_create(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, size_t length); uint8_t object_execute(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, uint8_t * buffer, size_t length); uint8_t object_delete(lwm2m_context_t * contextP, lwm2m_uri_t * uriP); diff --git a/core/liblwm2m.h b/core/liblwm2m.h index 1d08bb9b3..7e2bf0129 100644 --- a/core/liblwm2m.h +++ b/core/liblwm2m.h @@ -434,9 +434,16 @@ int lwm2m_decode_TLV(const uint8_t * buffer, size_t buffer_len, lwm2m_data_type_ typedef struct _lwm2m_object_t lwm2m_object_t; +typedef enum +{ + LWM2M_WRITE_PARTIAL_UPDATE, // Write should add or update resources and resource instances. + LWM2M_WRITE_REPLACE_RESOURCES, // Write should replace resources entirely. + LWM2M_WRITE_REPLACE_INSTANCE, // Write should replace the entire instance. +} lwm2m_write_type_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_write_callback_t) (uint16_t instanceId, int numData, lwm2m_data_t * dataArray, lwm2m_object_t * objectP, lwm2m_write_type_t writeType); 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); diff --git a/core/management.c b/core/management.c index 029ab649f..8983ebcf4 100644 --- a/core/management.c +++ b/core/management.c @@ -302,7 +302,7 @@ uint8_t dm_handleRequest(lwm2m_context_t * contextP, } else if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) { - result = object_write(contextP, uriP, format, message->payload, message->payload_len); + result = object_write(contextP, uriP, format, message->payload, message->payload_len, true); } else { @@ -328,7 +328,7 @@ uint8_t dm_handleRequest(lwm2m_context_t * contextP, } else if (LWM2M_URI_IS_SET_INSTANCE(uriP)) { - result = object_write(contextP, uriP, format, message->payload, message->payload_len); + result = object_write(contextP, uriP, format, message->payload, message->payload_len, false); } else { diff --git a/core/objects.c b/core/objects.c index 33813be70..c46b99ac3 100644 --- a/core/objects.c +++ b/core/objects.c @@ -266,7 +266,8 @@ uint8_t object_write(lwm2m_context_t * contextP, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, - size_t length) + size_t length, + bool partial) { uint8_t result = NO_ERROR; lwm2m_object_t * targetP; @@ -274,6 +275,9 @@ uint8_t object_write(lwm2m_context_t * contextP, int size = 0; LOG_URI(uriP); + if (!LWM2M_URI_IS_SET_OBJECT(uriP)) return COAP_400_BAD_REQUEST; + if (!LWM2M_URI_IS_SET_INSTANCE(uriP)) return COAP_400_BAD_REQUEST; + targetP = (lwm2m_object_t *)LWM2M_LIST_FIND(contextP->objectList, uriP->objectId); if (NULL == targetP) { @@ -297,7 +301,19 @@ uint8_t object_write(lwm2m_context_t * contextP, #endif if (result == NO_ERROR) { - result = targetP->writeFunc(uriP->instanceId, size, dataP, targetP); + lwm2m_write_type_t writeType = LWM2M_WRITE_PARTIAL_UPDATE; + if (!partial) + { + if (LWM2M_URI_IS_SET_RESOURCE(uriP)) + { + writeType = LWM2M_WRITE_REPLACE_RESOURCES; + } + else + { + writeType = LWM2M_WRITE_REPLACE_INSTANCE; + } + } + result = targetP->writeFunc(uriP->instanceId, size, dataP, targetP, writeType); lwm2m_data_free(size, dataP); } @@ -999,7 +1015,7 @@ uint8_t object_writeInstance(lwm2m_context_t * contextP, return COAP_405_METHOD_NOT_ALLOWED; } - return targetP->writeFunc(dataP->id, dataP->value.asChildren.count, dataP->value.asChildren.array, targetP); + return targetP->writeFunc(dataP->id, dataP->value.asChildren.count, dataP->value.asChildren.array, targetP, LWM2M_WRITE_REPLACE_INSTANCE); } #endif diff --git a/examples/client/lwm2mclient.c b/examples/client/lwm2mclient.c index c566aedc1..aabd4aea2 100644 --- a/examples/client/lwm2mclient.c +++ b/examples/client/lwm2mclient.c @@ -142,7 +142,7 @@ void handle_value_changed(lwm2m_context_t * lwm2mH, dataP->id = uri->resourceId; lwm2m_data_encode_nstring(value, valueLength, dataP); - result = object->writeFunc(uri->instanceId, 1, dataP, object); + result = object->writeFunc(uri->instanceId, 1, dataP, object, LWM2M_WRITE_PARTIAL_UPDATE); if (COAP_405_METHOD_NOT_ALLOWED == result) { switch (uri->objectId) diff --git a/examples/client/object_access_control.c b/examples/client/object_access_control.c index ac1f75b32..ab448b00f 100644 --- a/examples/client/object_access_control.c +++ b/examples/client/object_access_control.c @@ -62,6 +62,9 @@ typedef struct acc_ctrl_oi_s acc_ctrl_ri_t* accCtrlValList; } acc_ctrl_oi_t; +static uint8_t prv_delete(uint16_t id, lwm2m_object_t * objectP); +static uint8_t prv_create(uint16_t objInstId, int numData, + lwm2m_data_t * tlvArray, lwm2m_object_t * objectP); static uint8_t prv_set_tlv(lwm2m_data_t* dataP, acc_ctrl_oi_t* accCtrlOiP) { @@ -214,7 +217,8 @@ static bool prv_add_ac_val(acc_ctrl_oi_t* accCtrlOiP, } static uint8_t prv_write_resources(uint16_t instanceId, int numData, - lwm2m_data_t* tlvArray, lwm2m_object_t* objectP, bool doCreate) + lwm2m_data_t* tlvArray, lwm2m_object_t* objectP, bool doCreate, + lwm2m_write_type_t writeType) { int i; uint8_t result = COAP_500_INTERNAL_SERVER_ERROR; @@ -225,6 +229,20 @@ static uint8_t prv_write_resources(uint16_t instanceId, int numData, if (NULL == accCtrlOiP) return COAP_404_NOT_FOUND ; + if (writeType == LWM2M_WRITE_REPLACE_INSTANCE) + { + result = prv_delete(instanceId, objectP); + if (result == COAP_202_DELETED) + { + result = prv_create(instanceId, numData, tlvArray, objectP); + if (result == COAP_201_CREATED) + { + result = COAP_204_CHANGED; + } + } + return result; + } + i = 0; do { @@ -282,9 +300,6 @@ static uint8_t prv_write_resources(uint16_t instanceId, int numData, } else { - // MR-Write: Replace-implementation variant only - // see LWM2M-TS:5.4.3 (wakaama has no part-update switch) - // 1st: save accValueList! acc_ctrl_ri_t* acValListSave = accCtrlOiP->accCtrlValList; accCtrlOiP->accCtrlValList = NULL; @@ -292,37 +307,111 @@ static uint8_t prv_write_resources(uint16_t instanceId, int numData, size_t ri; lwm2m_data_t* subTlvArray = tlvArray[i].value.asChildren.array; - if (tlvArray[i].value.asChildren.count == 0) + if (writeType == LWM2M_WRITE_PARTIAL_UPDATE) { result = COAP_204_CHANGED; - } - else if (subTlvArray==NULL) - { - result = COAP_400_BAD_REQUEST; - } - else - { - for (ri=0; ri < tlvArray[i].value.asChildren.count; ri++) + + if (tlvArray[i].value.asChildren.count == 0) { - if (1 != lwm2m_data_decode_int(&subTlvArray[ri], &value)) - { - result = COAP_400_BAD_REQUEST; - break; - } - else if (value < 0 || value > 0xFFFF) + acValListSave = NULL; + accCtrlOiP->accCtrlValList = acValListSave; + } + else if (subTlvArray==NULL) + { + result = COAP_400_BAD_REQUEST; + } + else + { + // Duplicate original list + acc_ctrl_ri_t* acValListElement; + for (acValListElement = acValListSave; + acValListElement != NULL; + acValListElement = acValListElement->next) { - result = COAP_406_NOT_ACCEPTABLE; - break; + if (!prv_add_ac_val(accCtrlOiP, + acValListElement->resInstId, + acValListElement->accCtrlValue)) + { + result = COAP_500_INTERNAL_SERVER_ERROR; + break; + } } - else if (!prv_add_ac_val(accCtrlOiP, subTlvArray[ri].id, - (uint16_t)value)) + + if (result == COAP_204_CHANGED) { - result = COAP_500_INTERNAL_SERVER_ERROR; - break; + // Add or replace based on new values + for (ri=0; ri < tlvArray[i].value.asChildren.count; ri++) + { + if (1 != lwm2m_data_decode_int(&subTlvArray[ri], &value)) + { + result = COAP_400_BAD_REQUEST; + break; + } + else if (value < 0 || value > 0xFFFF) + { + result = COAP_406_NOT_ACCEPTABLE; + break; + } + else + { + for (acValListElement = accCtrlOiP->accCtrlValList; + acValListElement != NULL; + acValListElement = acValListElement->next) + { + if (subTlvArray[ri].id == acValListElement->resInstId) + { + acValListElement->accCtrlValue = (uint16_t)value; + break; + } + } + + if (acValListElement == NULL && + !prv_add_ac_val(accCtrlOiP, + subTlvArray[ri].id, + (uint16_t)value)) + { + result = COAP_500_INTERNAL_SERVER_ERROR; + break; + } + } + } } - else + } + } + else + { + if (tlvArray[i].value.asChildren.count == 0) + { + result = COAP_204_CHANGED; + } + else if (subTlvArray==NULL) + { + result = COAP_400_BAD_REQUEST; + } + else + { + for (ri=0; ri < tlvArray[i].value.asChildren.count; ri++) { - result = COAP_204_CHANGED; + if (1 != lwm2m_data_decode_int(&subTlvArray[ri], &value)) + { + result = COAP_400_BAD_REQUEST; + break; + } + else if (value < 0 || value > 0xFFFF) + { + result = COAP_406_NOT_ACCEPTABLE; + break; + } + else if (!prv_add_ac_val(accCtrlOiP, subTlvArray[ri].id, + (uint16_t)value)) + { + result = COAP_500_INTERNAL_SERVER_ERROR; + break; + } + else + { + result = COAP_204_CHANGED; + } } } } @@ -370,9 +459,10 @@ static uint8_t prv_write_resources(uint16_t instanceId, int numData, } static uint8_t prv_write(uint16_t instanceId, int numData, - lwm2m_data_t* tlvArray, lwm2m_object_t* objectP) + lwm2m_data_t* tlvArray, lwm2m_object_t* objectP, + lwm2m_write_type_t writeType) { - return prv_write_resources(instanceId, numData, tlvArray, objectP, false); + return prv_write_resources(instanceId, numData, tlvArray, objectP, false, writeType); } static uint8_t prv_delete(uint16_t id, lwm2m_object_t * objectP) @@ -402,7 +492,7 @@ static uint8_t prv_create(uint16_t objInstId, int numData, targetP->objInstId = objInstId; objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, targetP); - result = prv_write_resources(objInstId, numData, tlvArray, objectP, true); + result = prv_write_resources(objInstId, numData, tlvArray, objectP, true, LWM2M_WRITE_REPLACE_RESOURCES); if (result != COAP_204_CHANGED) { diff --git a/examples/client/object_device.c b/examples/client/object_device.c index 88626f55d..6951725c0 100644 --- a/examples/client/object_device.c +++ b/examples/client/object_device.c @@ -504,11 +504,15 @@ static uint8_t prv_device_discover(uint16_t instanceId, static uint8_t prv_device_write(uint16_t instanceId, int numData, lwm2m_data_t * dataArray, - lwm2m_object_t * objectP) + lwm2m_object_t * objectP, + lwm2m_write_type_t writeType) { int i; uint8_t result; + // All write types are treated the same here + (void)writeType; + // this is a single instance object if (instanceId != 0) { diff --git a/examples/client/object_firmware.c b/examples/client/object_firmware.c index 39d610623..f7ffaa6c7 100644 --- a/examples/client/object_firmware.c +++ b/examples/client/object_firmware.c @@ -202,11 +202,15 @@ static uint8_t prv_firmware_read(uint16_t instanceId, static uint8_t prv_firmware_write(uint16_t instanceId, int numData, lwm2m_data_t * dataArray, - lwm2m_object_t * objectP) + lwm2m_object_t * objectP, + lwm2m_write_type_t writeType) { int i; uint8_t result; + // All write types are treated the same here + (void)writeType; + // this is a single instance object if (instanceId != 0) { diff --git a/examples/client/object_security.c b/examples/client/object_security.c index b3f484a44..aefe2b6da 100644 --- a/examples/client/object_security.c +++ b/examples/client/object_security.c @@ -195,12 +195,16 @@ static uint8_t prv_security_read(uint16_t instanceId, static uint8_t prv_security_write(uint16_t instanceId, int numData, lwm2m_data_t * dataArray, - lwm2m_object_t * objectP) + lwm2m_object_t * objectP, + lwm2m_write_type_t writeType) { security_instance_t * targetP; int i; uint8_t result = COAP_204_CHANGED; + /* All write types are ignored. They don't apply during bootstrap. */ + (void)writeType; + targetP = (security_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId); if (NULL == targetP) { @@ -437,7 +441,7 @@ static uint8_t prv_security_create(uint16_t instanceId, targetP->instanceId = instanceId; objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, targetP); - result = prv_security_write(instanceId, numData, dataArray, objectP); + result = prv_security_write(instanceId, numData, dataArray, objectP, LWM2M_WRITE_REPLACE_RESOURCES); if (result != COAP_204_CHANGED) { diff --git a/examples/client/object_server.c b/examples/client/object_server.c index 81273c4a7..e39cbb491 100644 --- a/examples/client/object_server.c +++ b/examples/client/object_server.c @@ -75,6 +75,13 @@ typedef struct _server_instance_ #endif } server_instance_t; +static uint8_t prv_server_delete(uint16_t id, + lwm2m_object_t * objectP); +static uint8_t prv_server_create(uint16_t instanceId, + int numData, + lwm2m_data_t * dataArray, + lwm2m_object_t * objectP); + static uint8_t prv_get_value(lwm2m_data_t * dataP, server_instance_t * targetP) { @@ -630,7 +637,8 @@ static uint8_t prv_set_int_value(lwm2m_data_t * dataArray, uint32_t * data) { static uint8_t prv_server_write(uint16_t instanceId, int numData, lwm2m_data_t * dataArray, - lwm2m_object_t * objectP) + lwm2m_object_t * objectP, + lwm2m_write_type_t writeType) { server_instance_t * targetP; int i; @@ -642,6 +650,20 @@ static uint8_t prv_server_write(uint16_t instanceId, return COAP_404_NOT_FOUND; } + if (writeType == LWM2M_WRITE_REPLACE_INSTANCE) + { + result = prv_server_delete(instanceId, objectP); + if (result == COAP_202_DELETED) + { + result = prv_server_create(instanceId, numData, dataArray, objectP); + if (result == COAP_201_CREATED) + { + result = COAP_204_CHANGED; + } + } + return result; + } + i = 0; do { @@ -961,7 +983,7 @@ static uint8_t prv_server_create(uint16_t instanceId, #endif objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, serverInstance); - result = prv_server_write(instanceId, numData, dataArray, objectP); + result = prv_server_write(instanceId, numData, dataArray, objectP, LWM2M_WRITE_REPLACE_RESOURCES); if (result != COAP_204_CHANGED) { diff --git a/examples/client/test_object.c b/examples/client/test_object.c index f5b16df15..8266c6739 100644 --- a/examples/client/test_object.c +++ b/examples/client/test_object.c @@ -95,6 +95,13 @@ typedef struct _prv_instance_ double dec; } prv_instance_t; +static uint8_t prv_delete(uint16_t id, + lwm2m_object_t * objectP); +static uint8_t prv_create(uint16_t instanceId, + int numData, + lwm2m_data_t * dataArray, + lwm2m_object_t * objectP); + static void prv_output_buffer(uint8_t * buffer, int length) { @@ -216,7 +223,8 @@ static uint8_t prv_discover(uint16_t instanceId, static uint8_t prv_write(uint16_t instanceId, int numData, lwm2m_data_t * dataArray, - lwm2m_object_t * objectP) + lwm2m_object_t * objectP, + lwm2m_write_type_t writeType) { prv_instance_t * targetP; int i; @@ -224,6 +232,20 @@ static uint8_t prv_write(uint16_t instanceId, targetP = (prv_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId); if (NULL == targetP) return COAP_404_NOT_FOUND; + if (writeType == LWM2M_WRITE_REPLACE_INSTANCE) + { + uint8_t result = prv_delete(instanceId, objectP); + if (result == COAP_202_DELETED) + { + result = prv_create(instanceId, numData, dataArray, objectP); + if (result == COAP_201_CREATED) + { + result = COAP_204_CHANGED; + } + } + return result; + } + for (i = 0 ; i < numData ; i++) { switch (dataArray[i].id) @@ -284,7 +306,7 @@ static uint8_t prv_create(uint16_t instanceId, targetP->shortID = instanceId; objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, targetP); - result = prv_write(instanceId, numData, dataArray, objectP); + result = prv_write(instanceId, numData, dataArray, objectP, LWM2M_WRITE_REPLACE_RESOURCES); if (result != COAP_204_CHANGED) { diff --git a/examples/lightclient/object_server.c b/examples/lightclient/object_server.c index 703035ac4..a7545666f 100644 --- a/examples/lightclient/object_server.c +++ b/examples/lightclient/object_server.c @@ -51,6 +51,13 @@ typedef struct _server_instance_ char binding[4]; } server_instance_t; +static uint8_t prv_server_delete(uint16_t id, + lwm2m_object_t * objectP); +static uint8_t prv_server_create(uint16_t instanceId, + int numData, + lwm2m_data_t * dataArray, + lwm2m_object_t * objectP); + static uint8_t prv_get_value(lwm2m_data_t * dataP, server_instance_t * targetP) { @@ -219,7 +226,8 @@ static uint8_t prv_set_int_value(lwm2m_data_t * dataArray, static uint8_t prv_server_write(uint16_t instanceId, int numData, lwm2m_data_t * dataArray, - lwm2m_object_t * objectP) + lwm2m_object_t * objectP, + lwm2m_write_type_t writeType) { server_instance_t * targetP; int i; @@ -231,6 +239,20 @@ static uint8_t prv_server_write(uint16_t instanceId, return COAP_404_NOT_FOUND; } + if (writeType == LWM2M_WRITE_REPLACE_INSTANCE) + { + result = prv_server_delete(instanceId, objectP); + if (result == COAP_202_DELETED) + { + result = prv_server_create(instanceId, numData, dataArray, objectP); + if (result == COAP_201_CREATED) + { + result = COAP_204_CHANGED; + } + } + return result; + } + i = 0; do { @@ -366,7 +388,7 @@ static uint8_t prv_server_create(uint16_t instanceId, serverInstance->instanceId = instanceId; objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, serverInstance); - result = prv_server_write(instanceId, numData, dataArray, objectP); + result = prv_server_write(instanceId, numData, dataArray, objectP, LWM2M_WRITE_REPLACE_RESOURCES); if (result != COAP_204_CHANGED) { diff --git a/examples/lightclient/test_object.c b/examples/lightclient/test_object.c index d40ef4c71..486e3df16 100644 --- a/examples/lightclient/test_object.c +++ b/examples/lightclient/test_object.c @@ -77,6 +77,13 @@ #include #include +static uint8_t prv_delete(uint16_t id, + lwm2m_object_t * objectP); +static uint8_t prv_create(uint16_t instanceId, + int numData, + lwm2m_data_t * dataArray, + lwm2m_object_t * objectP); + static void prv_output_buffer(uint8_t * buffer, int length) { @@ -221,7 +228,8 @@ static uint8_t prv_discover(uint16_t instanceId, static uint8_t prv_write(uint16_t instanceId, int numData, lwm2m_data_t * dataArray, - lwm2m_object_t * objectP) + lwm2m_object_t * objectP, + lwm2m_write_type_t writeType) { prv_instance_t * targetP; int i; @@ -229,6 +237,20 @@ static uint8_t prv_write(uint16_t instanceId, targetP = (prv_instance_t *)lwm2m_list_find(objectP->instanceList, instanceId); if (NULL == targetP) return COAP_404_NOT_FOUND; + if (writeType == LWM2M_WRITE_REPLACE_INSTANCE) + { + uint8_t result = prv_delete(instanceId, objectP); + if (result == COAP_202_DELETED) + { + result = prv_create(instanceId, numData, dataArray, objectP); + if (result == COAP_201_CREATED) + { + result = COAP_204_CHANGED; + } + } + return result; + } + for (i = 0 ; i < numData ; i++) { switch (dataArray[i].id) @@ -300,7 +322,7 @@ static uint8_t prv_create(uint16_t instanceId, targetP->shortID = instanceId; objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, targetP); - result = prv_write(instanceId, numData, dataArray, objectP); + result = prv_write(instanceId, numData, dataArray, objectP, LWM2M_WRITE_REPLACE_RESOURCES); if (result != COAP_204_CHANGED) { From 1b8c6366f4db29417426568f2c550df5177a80be Mon Sep 17 00:00:00 2001 From: Scott Bertin Date: Thu, 7 Mar 2019 11:14:13 -0500 Subject: [PATCH 7/8] Handle resource instances in writes. Signed-off-by: Scott Bertin --- core/data.c | 16 +++---- core/liblwm2m.h | 2 +- core/management.c | 39 ++++++++++-------- core/objects.c | 15 +++++-- examples/client/lwm2mclient.c | 21 +++++++++- examples/client/object_access_control.c | 33 +++++++++++---- examples/client/object_device.c | 7 ++++ examples/client/object_firmware.c | 7 ++++ examples/client/object_security.c | 7 ++++ examples/client/object_server.c | 12 +++++- examples/client/test_object.c | 3 ++ examples/lightclient/object_server.c | 12 +++++- examples/lightclient/test_object.c | 3 ++ examples/server/lwm2mserver.c | 55 +++++++++++++++++++++---- 14 files changed, 184 insertions(+), 48 deletions(-) diff --git a/core/data.c b/core/data.c index e994babfd..091295466 100644 --- a/core/data.c +++ b/core/data.c @@ -656,13 +656,13 @@ int lwm2m_data_parse(lwm2m_uri_t * uriP, { case LWM2M_CONTENT_TEXT: if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return 0; -#ifndef LWM2M_VERSION_1_0 - // TODO: Support resource instance - if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP)) return 0; -#endif *dataP = lwm2m_data_new(1); if (*dataP == NULL) return 0; (*dataP)->id = uriP->resourceId; +#ifndef LWM2M_VERSION_1_0 + if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP)) + (*dataP)->id = uriP->resourceInstanceId; +#endif (*dataP)->type = LWM2M_TYPE_STRING; res = prv_setBuffer(*dataP, buffer, bufferLen); if (res == 0) @@ -674,13 +674,13 @@ int lwm2m_data_parse(lwm2m_uri_t * uriP, case LWM2M_CONTENT_OPAQUE: if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) return 0; -#ifndef LWM2M_VERSION_1_0 - // TODO: Support resource instance - if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP)) return 0; -#endif *dataP = lwm2m_data_new(1); if (*dataP == NULL) return 0; (*dataP)->id = uriP->resourceId; +#ifndef LWM2M_VERSION_1_0 + if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP)) + (*dataP)->id = uriP->resourceInstanceId; +#endif (*dataP)->type = LWM2M_TYPE_OPAQUE; res = prv_setBuffer(*dataP, buffer, bufferLen); if (res == 0) diff --git a/core/liblwm2m.h b/core/liblwm2m.h index 7e2bf0129..c0ce91495 100644 --- a/core/liblwm2m.h +++ b/core/liblwm2m.h @@ -786,7 +786,7 @@ void lwm2m_set_monitoring_callback(lwm2m_context_t * contextP, lwm2m_result_call // 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(lwm2m_context_t * contextP, uint16_t clientID, lwm2m_uri_t * uriP, lwm2m_media_type_t format, uint8_t * buffer, int length, bool partialUpdate, 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, int numData, lwm2m_data_t * dataP, lwm2m_result_callback_t callback, void * userData); diff --git a/core/management.c b/core/management.c index 8983ebcf4..a327b69a7 100644 --- a/core/management.c +++ b/core/management.c @@ -300,13 +300,25 @@ uint8_t dm_handleRequest(lwm2m_context_t * contextP, lwm2m_update_registration(contextP, 0, true); } } - else if (!LWM2M_URI_IS_SET_RESOURCE(uriP)) + else if (!IS_OPTION(message, COAP_OPTION_CONTENT_TYPE) + || format == LWM2M_CONTENT_TEXT) { - result = object_write(contextP, uriP, format, message->payload, message->payload_len, true); + if (!LWM2M_URI_IS_SET_RESOURCE(uriP) +#ifndef LWM2M_VERSION_1_0 + || LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP) +#endif + ) + { + result = COAP_400_BAD_REQUEST; + } + else + { + result = object_execute(contextP, uriP, message->payload, message->payload_len); + } } else { - result = object_execute(contextP, uriP, message->payload, message->payload_len); + result = object_write(contextP, uriP, format, message->payload, message->payload_len, true); } } break; @@ -516,9 +528,12 @@ int lwm2m_dm_write(lwm2m_context_t * contextP, lwm2m_media_type_t format, uint8_t * buffer, int length, + bool partialUpdate, lwm2m_result_callback_t callback, void * userData) { + coap_method_t method = partialUpdate ? COAP_POST : COAP_PUT; + LOG_ARG("clientID: %d, format: %s, length: %d", clientID, STR_MEDIA_TYPE(format), length); LOG_URI(uriP); if (!LWM2M_URI_IS_SET_INSTANCE(uriP) @@ -527,20 +542,10 @@ int lwm2m_dm_write(lwm2m_context_t * contextP, 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); - } + return prv_makeOperation(contextP, clientID, uriP, + method, + format, buffer, length, + callback, userData); } int lwm2m_dm_execute(lwm2m_context_t * contextP, diff --git a/core/objects.c b/core/objects.c index c46b99ac3..cc37ee1db 100644 --- a/core/objects.c +++ b/core/objects.c @@ -290,14 +290,23 @@ uint8_t object_write(lwm2m_context_t * contextP, else { size = lwm2m_data_parse(uriP, buffer, length, format, &dataP); - if (size == 0) + if (size <= 0) { result = COAP_406_NOT_ACCEPTABLE; } } #ifndef LWM2M_VERSION_1_0 - // TODO: support resource instance - if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP)) result = COAP_400_BAD_REQUEST; + if (result == NO_ERROR + && LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP)) + { + lwm2m_data_t *subDataP = dataP; + dataP = lwm2m_data_new(1); + if (dataP == NULL) return COAP_500_INTERNAL_SERVER_ERROR; + dataP->id = uriP->resourceId; + lwm2m_data_encode_instances(subDataP, size, dataP); + size = 1; + partial = true; + } #endif if (result == NO_ERROR) { diff --git a/examples/client/lwm2mclient.c b/examples/client/lwm2mclient.c index aabd4aea2..ddf1bed7b 100644 --- a/examples/client/lwm2mclient.c +++ b/examples/client/lwm2mclient.c @@ -140,7 +140,26 @@ void handle_value_changed(lwm2m_context_t * lwm2mH, return; } dataP->id = uri->resourceId; - lwm2m_data_encode_nstring(value, valueLength, dataP); + +#ifndef LWM2M_VERSION_1_0 + if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uri)) + { + lwm2m_data_t *subDataP = lwm2m_data_new(1); + if (subDataP == NULL) + { + fprintf(stderr, "Internal allocation failure !\n"); + lwm2m_data_free(1, dataP); + return; + } + subDataP->id = uri->resourceInstanceId; + lwm2m_data_encode_nstring(value, valueLength, subDataP); + lwm2m_data_encode_instances(subDataP, 1, dataP); + } + else +#endif + { + lwm2m_data_encode_nstring(value, valueLength, dataP); + } result = object->writeFunc(uri->instanceId, 1, dataP, object, LWM2M_WRITE_PARTIAL_UPDATE); if (COAP_405_METHOD_NOT_ALLOWED == result) diff --git a/examples/client/object_access_control.c b/examples/client/object_access_control.c index ab448b00f..98be64b34 100644 --- a/examples/client/object_access_control.c +++ b/examples/client/object_access_control.c @@ -253,6 +253,10 @@ static uint8_t prv_write_resources(uint16_t instanceId, int numData, { result = COAP_405_METHOD_NOT_ALLOWED; } + else if (tlvArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + } else { if (1 != lwm2m_data_decode_int(&tlvArray[i], &value)) @@ -275,6 +279,10 @@ static uint8_t prv_write_resources(uint16_t instanceId, int numData, { result = COAP_405_METHOD_NOT_ALLOWED; } + else if (tlvArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + } else { if (1 != lwm2m_data_decode_int(&tlvArray[i], &value)) @@ -431,22 +439,29 @@ static uint8_t prv_write_resources(uint16_t instanceId, int numData, } } break; case RES_M_ACCESS_CONTROL_OWNER: { - if (1 == lwm2m_data_decode_int(tlvArray + i, &value)) + if (tlvArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + } + else { - if (value >= 0 && value <= 0xFFFF) + if (1 == lwm2m_data_decode_int(tlvArray + i, &value)) { - accCtrlOiP->accCtrlOwner = value; - result = COAP_204_CHANGED; + if (value >= 0 && value <= 0xFFFF) + { + accCtrlOiP->accCtrlOwner = value; + result = COAP_204_CHANGED; + } + else + { + result = COAP_406_NOT_ACCEPTABLE; + } } else { - result = COAP_406_NOT_ACCEPTABLE; + result = COAP_400_BAD_REQUEST; } } - else - { - result = COAP_400_BAD_REQUEST; - } } break; default: diff --git a/examples/client/object_device.c b/examples/client/object_device.c index 6951725c0..169d79149 100644 --- a/examples/client/object_device.c +++ b/examples/client/object_device.c @@ -523,6 +523,13 @@ static uint8_t prv_device_write(uint16_t instanceId, do { + /* No multiple instance resources */ + if (dataArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + continue; + } + switch (dataArray[i].id) { case RES_O_CURRENT_TIME: diff --git a/examples/client/object_firmware.c b/examples/client/object_firmware.c index f7ffaa6c7..ecbe3e387 100644 --- a/examples/client/object_firmware.c +++ b/examples/client/object_firmware.c @@ -221,6 +221,13 @@ static uint8_t prv_firmware_write(uint16_t instanceId, do { + /* No multiple instance resources */ + if (dataArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + continue; + } + switch (dataArray[i].id) { case RES_M_PACKAGE: diff --git a/examples/client/object_security.c b/examples/client/object_security.c index aefe2b6da..85e1f862a 100644 --- a/examples/client/object_security.c +++ b/examples/client/object_security.c @@ -213,6 +213,13 @@ static uint8_t prv_security_write(uint16_t instanceId, i = 0; do { + /* No multiple instance resources */ + if (dataArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + continue; + } + switch (dataArray[i].id) { case LWM2M_SECURITY_URI_ID: diff --git a/examples/client/object_server.c b/examples/client/object_server.c index e39cbb491..013283f4a 100644 --- a/examples/client/object_server.c +++ b/examples/client/object_server.c @@ -667,6 +667,13 @@ static uint8_t prv_server_write(uint16_t instanceId, i = 0; do { + /* No multiple instance resources */ + if (dataArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + continue; + } + switch (dataArray[i].id) { case LWM2M_SERVER_SHORT_ID_ID: @@ -726,12 +733,15 @@ static uint8_t prv_server_write(uint16_t instanceId, case LWM2M_SERVER_BINDING_ID: if ((dataArray[i].type == LWM2M_TYPE_STRING || dataArray[i].type == LWM2M_TYPE_OPAQUE) && dataArray[i].value.asBuffer.length > 0 && dataArray[i].value.asBuffer.length <= 3 +#ifdef LWM2M_VERSION_1_0 && (strncmp((char*)dataArray[i].value.asBuffer.buffer, "U", dataArray[i].value.asBuffer.length) == 0 || strncmp((char*)dataArray[i].value.asBuffer.buffer, "UQ", dataArray[i].value.asBuffer.length) == 0 || strncmp((char*)dataArray[i].value.asBuffer.buffer, "S", dataArray[i].value.asBuffer.length) == 0 || strncmp((char*)dataArray[i].value.asBuffer.buffer, "SQ", dataArray[i].value.asBuffer.length) == 0 || strncmp((char*)dataArray[i].value.asBuffer.buffer, "US", dataArray[i].value.asBuffer.length) == 0 - || strncmp((char*)dataArray[i].value.asBuffer.buffer, "UQS", dataArray[i].value.asBuffer.length) == 0)) + || strncmp((char*)dataArray[i].value.asBuffer.buffer, "UQS", dataArray[i].value.asBuffer.length) == 0) +#endif + ) { strncpy(targetP->binding, (char*)dataArray[i].value.asBuffer.buffer, dataArray[i].value.asBuffer.length); result = COAP_204_CHANGED; diff --git a/examples/client/test_object.c b/examples/client/test_object.c index 8266c6739..422a21659 100644 --- a/examples/client/test_object.c +++ b/examples/client/test_object.c @@ -248,6 +248,9 @@ static uint8_t prv_write(uint16_t instanceId, for (i = 0 ; i < numData ; i++) { + /* No multiple instance resources */ + if (dataArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; + switch (dataArray[i].id) { case 1: diff --git a/examples/lightclient/object_server.c b/examples/lightclient/object_server.c index a7545666f..44bd43954 100644 --- a/examples/lightclient/object_server.c +++ b/examples/lightclient/object_server.c @@ -256,6 +256,13 @@ static uint8_t prv_server_write(uint16_t instanceId, i = 0; do { + /* No multiple instance resources */ + if (dataArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) + { + result = COAP_404_NOT_FOUND; + continue; + } + switch (dataArray[i].id) { case LWM2M_SERVER_SHORT_ID_ID: @@ -304,12 +311,15 @@ static uint8_t prv_server_write(uint16_t instanceId, case LWM2M_SERVER_BINDING_ID: if ((dataArray[i].type == LWM2M_TYPE_STRING || dataArray[i].type == LWM2M_TYPE_OPAQUE) && dataArray[i].value.asBuffer.length > 0 && dataArray[i].value.asBuffer.length <= 3 +#ifdef LWM2M_VERSION_1_0 && (strncmp((char*)dataArray[i].value.asBuffer.buffer, "U", dataArray[i].value.asBuffer.length) == 0 || strncmp((char*)dataArray[i].value.asBuffer.buffer, "UQ", dataArray[i].value.asBuffer.length) == 0 || strncmp((char*)dataArray[i].value.asBuffer.buffer, "S", dataArray[i].value.asBuffer.length) == 0 || strncmp((char*)dataArray[i].value.asBuffer.buffer, "SQ", dataArray[i].value.asBuffer.length) == 0 || strncmp((char*)dataArray[i].value.asBuffer.buffer, "US", dataArray[i].value.asBuffer.length) == 0 - || strncmp((char*)dataArray[i].value.asBuffer.buffer, "UQS", dataArray[i].value.asBuffer.length) == 0)) + || strncmp((char*)dataArray[i].value.asBuffer.buffer, "UQS", dataArray[i].value.asBuffer.length) == 0) +#endif + ) { strncpy(targetP->binding, (char*)dataArray[i].value.asBuffer.buffer, dataArray[i].value.asBuffer.length); result = COAP_204_CHANGED; diff --git a/examples/lightclient/test_object.c b/examples/lightclient/test_object.c index 486e3df16..ce9993d40 100644 --- a/examples/lightclient/test_object.c +++ b/examples/lightclient/test_object.c @@ -253,6 +253,9 @@ static uint8_t prv_write(uint16_t instanceId, for (i = 0 ; i < numData ; i++) { + /* No multiple instance resources */ + if (dataArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE) return COAP_404_NOT_FOUND; + switch (dataArray[i].id) { case 1: diff --git a/examples/server/lwm2mserver.c b/examples/server/lwm2mserver.c index d64f239c0..b44943dfe 100644 --- a/examples/server/lwm2mserver.c +++ b/examples/server/lwm2mserver.c @@ -344,8 +344,9 @@ static void prv_discover_client(char * buffer, fprintf(stdout, "Syntax error !"); } -static void prv_write_client(char * buffer, - void * user_data) +static void prv_do_write_client(char * buffer, + void * user_data, + bool partialUpdate) { lwm2m_context_t * lwm2mH = (lwm2m_context_t *) user_data; uint16_t clientId; @@ -388,12 +389,24 @@ static void prv_write_client(char * buffer, if (clientP != NULL) { lwm2m_media_type_t format = clientP->format; - uint8_t *buffer; - int length = lwm2m_data_serialize(&uri, count, dataP, &format, &buffer); + uint8_t *serialized; + int length = lwm2m_data_serialize(&uri, + count, + dataP, + &format, + &serialized); if (length > 0) { - result = lwm2m_dm_write(lwm2mH, clientId, &uri, format, buffer, length, prv_result_callback, NULL); - lwm2m_free(buffer); + result = lwm2m_dm_write(lwm2mH, + clientId, + &uri, + format, + serialized, + length, + partialUpdate, + prv_result_callback, + NULL); + lwm2m_free(serialized); } else { @@ -406,9 +419,21 @@ static void prv_write_client(char * buffer, } lwm2m_data_free(count, dataP); } + else if(!partialUpdate) + { + result = lwm2m_dm_write(lwm2mH, + clientId, + &uri, + LWM2M_CONTENT_TEXT, + (uint8_t *)buffer, + end - buffer, + partialUpdate, + prv_result_callback, + NULL); + } else { - result = lwm2m_dm_write(lwm2mH, clientId, &uri, LWM2M_CONTENT_TEXT, (uint8_t *)buffer, end - buffer, prv_result_callback, NULL); + goto syntax_error; } if (result == 0) @@ -425,6 +450,17 @@ static void prv_write_client(char * buffer, fprintf(stdout, "Syntax error !"); } +static void prv_write_client(char * buffer, + void * user_data) +{ + prv_do_write_client(buffer, user_data, false); +} + +static void prv_update_client(char * buffer, + void * user_data) +{ + prv_do_write_client(buffer, user_data, true); +} static void prv_time_client(char * buffer, void * user_data) @@ -954,6 +990,11 @@ int main(int argc, char *argv[]) " URI: uri to write to such as /3, /3/0/2, /1024/11, /1024/0/1\r\n" " DATA: data to write. Text or a supported JSON format.\r\n" "Result will be displayed asynchronously.", prv_write_client, NULL}, + {"update", "Write to a client with partial update.", " update CLIENT# URI DATA\r\n" + " CLIENT#: client number as returned by command 'list'\r\n" + " URI: uri to write to such as /3, /3/0/2, /1024/11, /1024/0/1\r\n" + " DATA: data to write. Must be a supported JSON format.\r\n" + "Result will be displayed asynchronously.", prv_update_client, NULL}, {"time", "Write time-related attributes to a client.", " time CLIENT# URI PMIN PMAX\r\n" " CLIENT#: client number as returned by command 'list'\r\n" " URI: uri to write attributes to such as /3, /3/0/2, /1024/11, /1024/0/1\r\n" From 68b6c491d18effc7c312410f08a337e0d9f316e9 Mon Sep 17 00:00:00 2001 From: Scott Bertin Date: Tue, 12 Mar 2019 09:52:11 -0400 Subject: [PATCH 8/8] Handle resource instances in observes Signed-off-by: Scott Bertin --- core/observe.c | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/core/observe.c b/core/observe.c index b9472c1bd..4d3aec5c8 100644 --- a/core/observe.c +++ b/core/observe.c @@ -167,6 +167,7 @@ uint8_t observe_handleRequest(lwm2m_context_t * contextP, { lwm2m_observed_t * observedP; lwm2m_watcher_t * watcherP; + lwm2m_data_t * valueP; uint32_t count; (void) size; /* unused */ @@ -192,18 +193,27 @@ uint8_t observe_handleRequest(lwm2m_context_t * contextP, watcherP->lastMid = response->mid; watcherP->format = (lwm2m_media_type_t)response->content_type; + valueP = dataP; +#ifndef LWM2M_VERSION_1_0 + if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(uriP) + && dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE + && dataP->value.asChildren.count == 1) + { + valueP = dataP->value.asChildren.array; + } +#endif if (LWM2M_URI_IS_SET_RESOURCE(uriP)) { - switch (dataP->type) + switch (valueP->type) { case LWM2M_TYPE_INTEGER: - if (1 != lwm2m_data_decode_int(dataP, &(watcherP->lastValue.asInteger))) return COAP_500_INTERNAL_SERVER_ERROR; + if (1 != lwm2m_data_decode_int(valueP, &(watcherP->lastValue.asInteger))) return COAP_500_INTERNAL_SERVER_ERROR; break; case LWM2M_TYPE_UNSIGNED_INTEGER: - if (1 != lwm2m_data_decode_uint(dataP, &(watcherP->lastValue.asUnsigned))) return COAP_500_INTERNAL_SERVER_ERROR; + if (1 != lwm2m_data_decode_uint(valueP, &(watcherP->lastValue.asUnsigned))) 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; + if (1 != lwm2m_data_decode_float(valueP, &(watcherP->lastValue.asFloat))) return COAP_500_INTERNAL_SERVER_ERROR; break; default: break; @@ -510,6 +520,7 @@ void observe_step(lwm2m_context_t * contextP, uint8_t * buffer = NULL; size_t length = 0; lwm2m_data_t * dataP = NULL; + lwm2m_data_type_t dataType = LWM2M_TYPE_UNDEFINED; int size = 0; double floatValue = 0; int64_t integerValue = 0; @@ -523,11 +534,23 @@ void observe_step(lwm2m_context_t * contextP, LOG_URI(&(targetP->uri)); if (LWM2M_URI_IS_SET_RESOURCE(&targetP->uri)) { + lwm2m_data_t *valueP; + if (COAP_205_CONTENT != object_readData(contextP, &targetP->uri, &size, &dataP)) continue; - switch (dataP->type) + valueP = dataP; +#ifndef LWM2M_VERSION_1_0 + if (LWM2M_URI_IS_SET_RESOURCE_INSTANCE(&targetP->uri) + && dataP->type == LWM2M_TYPE_MULTIPLE_RESOURCE + && dataP->value.asChildren.count == 1) + { + valueP = dataP->value.asChildren.array; + } +#endif + dataType = valueP->type; + switch (dataType) { case LWM2M_TYPE_INTEGER: - if (1 != lwm2m_data_decode_int(dataP, &integerValue)) + if (1 != lwm2m_data_decode_int(valueP, &integerValue)) { lwm2m_data_free(size, dataP); continue; @@ -535,7 +558,7 @@ void observe_step(lwm2m_context_t * contextP, storeValue = true; break; case LWM2M_TYPE_UNSIGNED_INTEGER: - if (1 != lwm2m_data_decode_uint(dataP, &unsignedValue)) + if (1 != lwm2m_data_decode_uint(valueP, &unsignedValue)) { lwm2m_data_free(size, dataP); continue; @@ -543,7 +566,7 @@ void observe_step(lwm2m_context_t * contextP, storeValue = true; break; case LWM2M_TYPE_FLOAT: - if (1 != lwm2m_data_decode_float(dataP, &floatValue)) + if (1 != lwm2m_data_decode_float(valueP, &floatValue)) { lwm2m_data_free(size, dataP); continue; @@ -580,7 +603,7 @@ void observe_step(lwm2m_context_t * contextP, { LOG("Checking lower threshold"); // Did we cross the lower threshold ? - switch (dataP->type) + switch (dataType) { case LWM2M_TYPE_INTEGER: if ((integerValue < watcherP->parameters->lessThan @@ -620,7 +643,7 @@ void observe_step(lwm2m_context_t * contextP, { LOG("Checking upper threshold"); // Did we cross the upper threshold ? - switch (dataP->type) + switch (dataType) { case LWM2M_TYPE_INTEGER: if ((integerValue < watcherP->parameters->greaterThan @@ -660,7 +683,7 @@ void observe_step(lwm2m_context_t * contextP, { LOG("Checking step"); - switch (dataP->type) + switch (dataType) { case LWM2M_TYPE_INTEGER: { @@ -790,7 +813,7 @@ void observe_step(lwm2m_context_t * contextP, // Store this value if (notify == true && storeValue == true) { - switch (dataP->type) + switch (dataType) { case LWM2M_TYPE_INTEGER: watcherP->lastValue.asInteger = integerValue;